diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/Gruntfile.coffee b/Gruntfile.coffee new file mode 100644 index 0000000..2f656e2 --- /dev/null +++ b/Gruntfile.coffee @@ -0,0 +1,49 @@ +fs = require("fs") + +#global module:false +module.exports = (grunt) -> + + + # Project configuration. + grunt.initConfig + pkg: grunt.file.readJSON('component.json') + banner: + "/** <%= pkg.title || pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today(\"yyyy/mm/dd\") %>\n"+ + " * <%= pkg.homepage %>\n" + + " * Copyright (c) <%= grunt.template.today(\"yyyy\") %> <%= pkg.author.name %> - MIT\n"+ + " */" + + coffee: + options: + bare: true + compile: + files: + 'dist/<%= pkg.name %>.js': 'src/<%= pkg.name %>.coffee' + + wrap: + dist: + src: ['dist/<%= pkg.name %>.js'] + dest: '.' + wrapper: ["<%= banner %>\n(function(window,document,undefined) {\n","\n}(window,document));"] + + uglify: + options: + stripBanners: true + banner: '<%= banner %>' + + dist: + src: "dist/<%= pkg.name %>.js" + dest: "dist/<%= pkg.name %>.min.js" + + watch: + scripts: + files: 'src/*.coffee' + tasks: 'default' + + grunt.loadNpmTasks "grunt-contrib-watch" + grunt.loadNpmTasks "grunt-contrib-uglify" + grunt.loadNpmTasks "grunt-contrib-coffee" + grunt.loadNpmTasks "grunt-wrap" + + # Default task. + grunt.registerTask "default", "coffee wrap uglify".split(' ') diff --git a/README.md b/README.md index 74f187a..830859a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -notifyjs -======== -Notify.js - A robust, customizable notification library \ No newline at end of file +### Notify.js - A robust, customizable notification library + +## http://notifyjs.com diff --git a/component.json b/component.json new file mode 100644 index 0000000..d1aa995 --- /dev/null +++ b/component.json @@ -0,0 +1,10 @@ +{ + "title": "Notify.js", + "name": "notify", + "version": "0.0.1", + "author": { + "name": "Jaime Pillora" + }, + "homepage": "http://notifyjs.com/", + "license": "MIT" +} \ No newline at end of file diff --git a/dist/notify.js b/dist/notify.js new file mode 100644 index 0000000..28ce459 --- /dev/null +++ b/dist/notify.js @@ -0,0 +1,244 @@ +/** Notify.js - v0.0.1 - 2013/04/04 + * http://notifyjs.com/ + * Copyright (c) 2013 Jaime Pillora - MIT + */ +(function(window,document,undefined) { +'use strict'; + +var Options, Prompt, arrowDirs, className, coreStyle, create, getAnchorElement, pluginName, pluginOptions, userStyles; + +pluginName = 'notify'; + +className = '__notify'; + +arrowDirs = { + top: 'bottom', + bottom: 'top', + left: 'right', + right: 'left' +}; + +coreStyle = { + html: "
\n
\n
\n
\n
\n
", + css: "." + className + "Wrapper {\n z-index: 1;\n position: absolute;\n display: inline-block;\n height: 0;\n width: 0;\n}\n\n." + className + "Main {\n display: none;\n z-index: 1;\n position: absolute;\n cursor: pointer;\n}\n\n." + className + "Content {\n background: #fff;\n position: relative;\n font-size: 11px;\n box-shadow: 0 0 6px #000;\n -moz-box-shadow: 0 0 6px #000;\n -webkit-box-shadow: 0 0 6px #000;\n padding: 4px 10px 4px 8px;\n border-radius: 6px;\n border-style: solid;\n border-width: 2px;\n -moz-border-radius: 6px;\n -webkit-border-radius: 6px;\n white-space: nowrap;\n}" +}; + +userStyles = { + "default": { + html: "test", + css: "body {\n test: 42\n}" + }, + bootstrap: { + html: "test", + css: "body {\n test: 42\n}" + } +}; + +pluginOptions = { + autoHidePrompt: false, + autoHideDelay: 10000, + arrowShow: true, + arrowSize: 5, + arrowPosition: 'top', + color: 'red', + colors: { + red: '#ee0101', + green: '#33be40', + black: '#393939', + blue: '#00f' + }, + showAnimation: 'fadeIn', + showDuration: 200, + hideAnimation: 'fadeOut', + hideDuration: 600, + gap: 2 +}; + +create = function(tag) { + return $(document.createElement(tag)); +}; + +Options = function(options) { + if ($.isPlainObject(options)) { + return $.extend(this, options); + } +}; + +Options.prototype = pluginOptions; + +getAnchorElement = function(element) { + var fBefore, radios; + if (element.is('[type=radio]')) { + radios = element.parents('form:first').find('[type=radio]').filter(function(i, e) { + return $(e).attr('name') === element.attr('name'); + }); + element = radios.first(); + } + fBefore = element.prev(); + if (fBefore.is('span.styled,span.OBS_checkbox')) { + element = fBefore; + } + return element; +}; + +Prompt = (function() { + + function Prompt(elem, node, options) { + if ($.type(options) === 'string') { + options = { + color: options + }; + } + this.options = new Options($.isPlainObject(options) ? options : {}); + this.elementType = elem.attr('type'); + this.originalElement = elem; + this.elem = getAnchorElement(elem); + this.elem.data(pluginName, this); + this.wrapper = $(coreStyle.html); + this.main = this.wrapper.find("." + className + "Main"); + this.content = this.main.find("." + className + "Content"); + this.elem.before(this.wrapper); + this.main.css(this.calculateCSS()); + this.run(node); + } + + Prompt.prototype.buildArrow = function() { + var alt, d, dir, showArrow, size; + dir = this.options.arrowPosition; + size = this.options.arrowSize; + alt = arrowDirs[dir]; + this.arrow = create("div"); + this.arrow.addClass(className + 'Arrow').css({ + 'margin-top': 2 + (document.documentMode === 5 ? size * -4 : 0), + 'position': 'relative', + 'z-index': '2', + 'margin-left': 10, + 'width': 0, + 'height': 0 + }).css('border-' + alt, size + 'px solid ' + this.getColor()); + for (d in arrowDirs) { + if (d !== dir && d !== alt) { + this.arrow.css('border-' + d, size + 'px solid transparent'); + } + } + showArrow = this.options.arrowShow && this.elementType !== 'radio'; + if (showArrow) { + return this.arrow.show(); + } else { + return this.arrow.hide(); + } + }; + + Prompt.prototype.showMain = function(show) { + var hidden; + hidden = this.main.parent().parents(':hidden').length > 0; + if (hidden && show) { + this.main.show(); + } + if (hidden && !show) { + this.main.hide(); + } + if (!hidden && show) { + this.main[this.options.showAnimation](this.options.showDuration); + } + if (!hidden && !show) { + return this.main[this.options.hideAnimation](this.options.hideDuration); + } + }; + + Prompt.prototype.calculateCSS = function() { + var elementPosition, height, left, mainPosition; + elementPosition = this.elem.position(); + mainPosition = this.main.parent().position(); + height = this.elem.outerHeight(); + left = elementPosition.left - mainPosition.left; + if (!navigator.userAgent.match(/MSIE/)) { + height += elementPosition.top - mainPosition.top; + } + return { + top: height + this.options.gap, + left: left + }; + }; + + Prompt.prototype.getColor = function() { + return this.options.colors[this.options.color] || this.options.color; + }; + + Prompt.prototype.run = function(node, options) { + var t; + if ($.isPlainObject(options)) { + $.extend(this.options, options); + } else if ($.type(options) === 'string') { + this.options.color = options; + } + if (this.main && !node) { + this.showMain(false); + return; + } else if (!this.main && !node) { + return; + } + if ($.type(node) === 'string') { + this.content.html(node.replace('\n', '
')); + } else { + this.content.empty().append(node); + } + this.content.css({ + 'color': this.getColor(), + 'border-color': this.getColor() + }); + if (this.arrow) { + this.arrow.remove(); + } + this.buildArrow(); + this.content.before(this.arrow); + this.showMain(true); + if (this.options.autoHidePrompt) { + clearTimeout(this.elem.data('mainTimer')); + t = setTimeout(function() { + return this.showMain(false); + }, this.options.autoHideDelay); + return this.elem.data('mainTimer', t); + } + }; + + return Prompt; + +})(); + +$(function() { + $("head").append(create("style").html(coreStyle.css)); + return $(document).on('click', "." + className, function() { + var inst; + inst = getAnchorElement($(this)).data(pluginName); + if (inst != null) { + return inst.showMain(false); + } + }); +}); + +$[pluginName] = function(elem, node, options) { + return $(elem)[pluginName](node, options); +}; + +$[pluginName].options = function(options) { + return $.extend(pluginOptions, options); +}; + +$[pluginName].addStyle = function(s) { + return $.extend(true, userStyles, s); +}; + +$.fn[pluginName] = function(node, options) { + return $(this).each(function() { + var inst; + inst = getAnchorElement($(this)).data(pluginName); + if (inst != null) { + return inst.run(node, options); + } else { + return new Prompt($(this), node, options); + } + }); +}; + +}(window,document)); \ No newline at end of file diff --git a/dist/notify.min.js b/dist/notify.min.js new file mode 100644 index 0000000..d504fac --- /dev/null +++ b/dist/notify.min.js @@ -0,0 +1,4 @@ +/** Notify.js - v0.0.1 - 2013/04/04 + * http://notifyjs.com/ + * Copyright (c) 2013 Jaime Pillora - MIT + */(function(t,n){"use strict";var i,o,e,r,s,a,h,p,d,l;p="notify",r="__notify",e={top:"bottom",bottom:"top",left:"right",right:"left"},s={html:'
\n
\n
\n
\n
\n
',css:"."+r+"Wrapper {\n z-index: 1;\n position: absolute;\n display: inline-block;\n height: 0;\n width: 0;\n}\n\n."+r+"Main {\n display: none;\n z-index: 1;\n position: absolute;\n cursor: pointer;\n}\n\n."+r+"Content {\n background: #fff;\n position: relative;\n font-size: 11px;\n box-shadow: 0 0 6px #000;\n -moz-box-shadow: 0 0 6px #000;\n -webkit-box-shadow: 0 0 6px #000;\n padding: 4px 10px 4px 8px;\n border-radius: 6px;\n border-style: solid;\n border-width: 2px;\n -moz-border-radius: 6px;\n -webkit-border-radius: 6px;\n white-space: nowrap;\n}"},l={"default":{html:"test",css:"body {\n test: 42\n}"},bootstrap:{html:"test",css:"body {\n test: 42\n}"}},d={autoHidePrompt:!1,autoHideDelay:1e4,arrowShow:!0,arrowSize:5,arrowPosition:"top",color:"red",colors:{red:"#ee0101",green:"#33be40",black:"#393939",blue:"#00f"},showAnimation:"fadeIn",showDuration:200,hideAnimation:"fadeOut",hideDuration:600,gap:2},a=function(t){return $(n.createElement(t))},i=function(t){return $.isPlainObject(t)?$.extend(this,t):undefined},i.prototype=d,h=function(t){var n,i;return t.is("[type=radio]")&&(i=t.parents("form:first").find("[type=radio]").filter(function(n,i){return $(i).attr("name")===t.attr("name")}),t=i.first()),n=t.prev(),n.is("span.styled,span.OBS_checkbox")&&(t=n),t},o=function(){function t(t,n,o){"string"===$.type(o)&&(o={color:o}),this.options=new i($.isPlainObject(o)?o:{}),this.elementType=t.attr("type"),this.originalElement=t,this.elem=h(t),this.elem.data(p,this),this.wrapper=$(s.html),this.main=this.wrapper.find("."+r+"Main"),this.content=this.main.find("."+r+"Content"),this.elem.before(this.wrapper),this.main.css(this.calculateCSS()),this.run(n)}return t.prototype.buildArrow=function(){var t,i,o,s,h;o=this.options.arrowPosition,h=this.options.arrowSize,t=e[o],this.arrow=a("div"),this.arrow.addClass(r+"Arrow").css({"margin-top":2+(5===n.documentMode?-4*h:0),position:"relative","z-index":"2","margin-left":10,width:0,height:0}).css("border-"+t,h+"px solid "+this.getColor());for(i in e)i!==o&&i!==t&&this.arrow.css("border-"+i,h+"px solid transparent");return s=this.options.arrowShow&&"radio"!==this.elementType,s?this.arrow.show():this.arrow.hide()},t.prototype.showMain=function(t){var n;return n=this.main.parent().parents(":hidden").length>0,n&&t&&this.main.show(),n&&!t&&this.main.hide(),!n&&t&&this.main[this.options.showAnimation](this.options.showDuration),n||t?undefined:this.main[this.options.hideAnimation](this.options.hideDuration)},t.prototype.calculateCSS=function(){var t,n,i,o;return t=this.elem.position(),o=this.main.parent().position(),n=this.elem.outerHeight(),i=t.left-o.left,navigator.userAgent.match(/MSIE/)||(n+=t.top-o.top),{top:n+this.options.gap,left:i}},t.prototype.getColor=function(){return this.options.colors[this.options.color]||this.options.color},t.prototype.run=function(t,n){var i;return $.isPlainObject(n)?$.extend(this.options,n):"string"===$.type(n)&&(this.options.color=n),this.main&&!t?(this.showMain(!1),undefined):this.main||t?("string"===$.type(t)?this.content.html(t.replace("\n","
")):this.content.empty().append(t),this.content.css({color:this.getColor(),"border-color":this.getColor()}),this.arrow&&this.arrow.remove(),this.buildArrow(),this.content.before(this.arrow),this.showMain(!0),this.options.autoHidePrompt?(clearTimeout(this.elem.data("mainTimer")),i=setTimeout(function(){return this.showMain(!1)},this.options.autoHideDelay),this.elem.data("mainTimer",i)):undefined):undefined},t}(),$(function(){return $("head").append(a("style").html(s.css)),$(n).on("click","."+r,function(){var t;return t=h($(this)).data(p),null!=t?t.showMain(!1):undefined})}),$[p]=function(t,n,i){return $(t)[p](n,i)},$[p].options=function(t){return $.extend(d,t)},$[p].addStyle=function(t){return $.extend(!0,l,t)},$.fn[p]=function(t,n){return $(this).each(function(){var i;return i=h($(this)).data(p),null!=i?i.run(t,n):new o($(this),t,n)})}})(window,document); \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..5d73cea --- /dev/null +++ b/index.html @@ -0,0 +1,26 @@ + + + + + +
+ + + +
+ + \ No newline at end of file diff --git a/notify.jquery.json b/notify.jquery.json new file mode 100644 index 0000000..ea29b0f --- /dev/null +++ b/notify.jquery.json @@ -0,0 +1,28 @@ +{ + "name": "notify", + "version": "0.0.1", + "title": "notify", + "description": "A robust, customizable notification library", + "bugs": "https://github.com/jpillora/notifyjs/issues", + "homepage": "http://notifyjs.com", + "docs": "http://notifyjs.com", + "download": "http://notifyjs.com#download", + "author": { + "name": "Jaime Pillora" + }, + "licenses": [ + { + "type": "MIT", + "url": "http://opensource.org/licenses/MIT" + } + ], + "keywords": [ + "notify", + "notification", + "prompt", + "popup" + ], + "dependencies": { + "jquery": ">=1.8" + } +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..2906229 --- /dev/null +++ b/package.json @@ -0,0 +1,12 @@ +{ + "name": "notify", + "devDependencies": { + "grunt": "~0.4.x", + "grunt-contrib-watch": "~0.1.4", + "grunt-contrib-uglify": "~0.1.1rc6", + "grunt-contrib-jshint": "~0.1.0", + "grunt-contrib-concat": "~0.1.1", + "grunt-contrib-coffee": "~0.4.0rc7", + "grunt-wrap": "~0.2.0" + } +} diff --git a/src/notify.coffee b/src/notify.coffee new file mode 100644 index 0000000..4c443bb --- /dev/null +++ b/src/notify.coffee @@ -0,0 +1,260 @@ + +'use strict' + +#plugin constants +pluginName = 'notify' +className = '__notify' + +arrowDirs = + top: 'bottom' + bottom: 'top' + left: 'right' + right: 'left' + +coreStyle = + html: """ +
+
+
+
+
+
+ """ + css: """ + .#{className}Wrapper { + z-index: 1; + position: absolute; + display: inline-block; + height: 0; + width: 0; + } + + .#{className}Main { + display: none; + z-index: 1; + position: absolute; + cursor: pointer; + } + + .#{className}Content { + background: #fff; + position: relative; + font-size: 11px; + box-shadow: 0 0 6px #000; + -moz-box-shadow: 0 0 6px #000; + -webkit-box-shadow: 0 0 6px #000; + padding: 4px 10px 4px 8px; + border-radius: 6px; + border-style: solid; + border-width: 2px; + -moz-border-radius: 6px; + -webkit-border-radius: 6px; + white-space: nowrap; + } + """ + +userStyles = + + default: + html: """ + test + """ + css: """ + body { + test: 42 + } + """ + + bootstrap: + html: """ + test + """ + css: """ + body { + test: 42 + } + """ + +#overridable options +pluginOptions = + autoHidePrompt: false + autoHideDelay: 10000 + arrowShow: true + arrowSize: 5 + arrowPosition: 'top' + # Default color + color: 'red' + # Color mappings + colors: + red: '#ee0101' + green: '#33be40' + black: '#393939' + blue: '#00f' + showAnimation: 'fadeIn' + showDuration: 200 + hideAnimation: 'fadeOut' + hideDuration: 600 + # Gap between main and element + gap: 2 + #TODO add z-index watches + #parents: { '.ui-dialog': 5001 } + +# plugin helpers +create = (tag) -> + $ document.createElement(tag) + +# inherit plugin options +Options = (options) -> + $.extend @, options if $.isPlainObject(options) +Options:: = pluginOptions + + #gets first on n radios, and gets the fancy stylised input for hidden inputs +getAnchorElement = (element) -> + #choose the first of n radios + if element.is('[type=radio]') + radios = element.parents('form:first').find('[type=radio]').filter (i, e) -> + $(e).attr('name') is element.attr('name') + element = radios.first() + #custom-styled inputs - find thier real element + fBefore = element.prev() + element = fBefore if fBefore.is('span.styled,span.OBS_checkbox') + element + +#define plugin +class Prompt + + #setup instance variables + constructor: (elem, node, options) -> + options = {color: options} if $.type(options) is 'string' + @options = new Options if $.isPlainObject(options) then options else {} + @elementType = elem.attr('type') + @originalElement = elem + @elem = getAnchorElement(elem) + @elem.data pluginName, @ + + @wrapper = $(coreStyle.html) + @main = @wrapper.find ".#{className}Main" + @content = @main.find ".#{className}Content" + + # add into dom + @elem.before @wrapper + @main.css @calculateCSS() + + @run(node) + + buildArrow: -> + dir = @options.arrowPosition + size = @options.arrowSize + alt = arrowDirs[dir] + @arrow = create("div") + @arrow.addClass(className + 'Arrow').css( + 'margin-top': 2 + (if document.documentMode is 5 then (size*-4) else 0) + 'position': 'relative' + 'z-index': '2' + 'margin-left': 10 + 'width': 0 + 'height': 0 + ).css( + 'border-' + alt, size + 'px solid ' + @getColor() + ) + for d of arrowDirs + @arrow.css 'border-' + d, size + 'px solid transparent' if d isnt dir and d isnt alt + + showArrow = @options.arrowShow and @elementType isnt 'radio' + if showArrow then @arrow.show() else @arrow.hide() + + showMain: (show) -> + hidden = @main.parent().parents(':hidden').length > 0 + @main.show() if hidden and show + @main.hide() if hidden and not show + @main[@options.showAnimation] @options.showDuration if not hidden and show + @main[@options.hideAnimation] @options.hideDuration if not hidden and not show + + calculateCSS: () -> + elementPosition = @elem.position() + mainPosition = @main.parent().position() + height = @elem.outerHeight() + left = elementPosition.left - mainPosition.left + height += (elementPosition.top - mainPosition.top) unless navigator.userAgent.match /MSIE/ + return { + top: height + @options.gap + left: left + } + + getColor: -> + @options.colors[@options.color] or @options.color + + #run plugin + run: (node, options) -> + #update options + if $.isPlainObject(options) + $.extend @options, options + #shortcut special case + else if $.type(options) is 'string' + @options.color = options + + if @main and not node + @showMain false #hide + return + else if not @main and not node + return + + #update content + if $.type(node) is 'string' + @content.html node.replace('\n', '
') + else + @content.empty().append(node) + + @content.css + 'color': @getColor() + 'border-color': @getColor() + + @arrow.remove() if @arrow + @buildArrow() + @content.before @arrow + + @showMain true + + #autohide + if @options.autoHidePrompt + clearTimeout @elem.data 'mainTimer' + t = setTimeout -> + @showMain false + , @options.autoHideDelay + @elem.data 'mainTimer', t + +#when ready, bind permanent hide listener +$ -> + + $("head").append(create("style").html(coreStyle.css)) + + $(document).on 'click', ".#{className}", -> + inst = getAnchorElement($(@)).data pluginName + inst.showMain false if inst? + +# publicise jquery plugin +# return alert "$.#{pluginName} already defined" if $[pluginName]? +# $.pluginName( { ... } ) changes options for all instances +$[pluginName] = (elem, node, options) -> + $(elem)[pluginName](node, options) + +# publicise options method +$[pluginName].options = (options) -> + $.extend pluginOptions, options + +$[pluginName].addStyle = (s) -> + $.extend true, userStyles, s + +# $( ... ).pluginName( { .. } ) creates a cached instance on each +# selected item with custom options for just that instance +# return alert "$.fn#{pluginName} already defined" if $.fn[pluginName]? +$.fn[pluginName] = (node, options) -> + $(@).each -> + inst = getAnchorElement($(@)).data pluginName + if inst? + inst.run node, options + else + new Prompt $(@), node, options + +#ps. alex is gay. +