/*! * Backbone.Notifier.js v0.2.5 * Copyright 2012, Eyal Weiss * Backbone.Notifier.js may be freely distributed under the MIT license. */ (function ($, Backbone, _) { var emptyFn = function () {}, Notifier = Backbone.Notifier = Backbone.Model.extend({ defaults: { 'baseCls': 'notifier', 'types': ['warning', 'error', 'info', 'success'], // available notification styles 'dialog': false, // whether display the notification with a title bar and a dialog style (sets 'hideOnClick' to false, unless defined) 'theme': 'plastic', // default theme for notifications (currently available: 'plastic' / 'clean'). 'message': '', // message content 'closeBtn': false, 'title': undefined, // notification title, if defined causes the notification to be 'dialog' (unless dialog is 'false') 'hideOnClick': true, // whether to hide the notifications on mouse click 'type': null, // default notification style (null / 'warning' / 'error' / 'info' / 'success') 'cls': null, // additional css class 'ms': 10000, // milliseconds before hiding 'loader': false, // whether to display loader animation in notifactions 'destroy': false, // notification or selector of nofications to hide on show 'modal': false, // whether to dark and block the UI behind the nofication 'opacity': 1, // opacity of nofications 'offsetY': 0, // distance between the notifications and the top/bottom edge 'fadeInMs': 500, // duration (milliseconds) of notification's fade-in effect 'fadeOutMs': 500, // duration (milliseconds) of notification's fade-out effect 'position': 'top', // default notifications position ('top' / 'center' / 'bottom') 'screenOpacity': .65, // opacity of dark screen background that goes behind for modals (between 0 to 1) 'zIndex': 10000, // minimal z-index for notifications 'width': undefined, // notification's width 'modules': undefined // modules to register immediately }, transitions: { top: { 'in': function (el, inner, options, duration, callback) { el.css({display: 'block', top: -1000}); el.css({top: -inner.innerHeight()}) .animate({top: options.offsetY, opacity: options.opacity}, duration, callback || emptyFn); }, 'out': function (el, inner, options, duration, callback) { el.animate({top: -inner.innerHeight(), opacity: 0}, duration, callback || emptyFn); } }, center: { 'in': function (el, inner, options, duration, callback) { el.css({top: options.offsetY - 40, display: 'block'}) .animate({ top: '50%', marginTop: -inner.innerHeight() / 2, opacity: options.opacity}, duration, callback || emptyFn); }, 'out': function (el, inner, options, duration, callback) { el.animate({top: '0%', opacity: 0}, duration, callback || emptyFn); } }, bottom: { 'in': function (el, inner, options, duration, callback) { el.css({bottom: options.offsetY - 40, top: 'auto', display: 'block'}) .animate({bottom: inner.innerHeight() + options.offsetY, opacity: options.opacity}, duration, callback || emptyFn); }, 'out': function (el, inner, options, duration, callback) { el.animate({bottom: -inner.height(), opacity: 0}, duration, callback || emptyFn); } } }, template: function (settings) { //function(settings){ ... return html; } var strBuilder = [ '<div class="' + settings.wrapperCls + '">', '<div class="' + settings.innerCls + '">', (settings.title ? '<div class="' + settings.baseCls + '-title">' + settings.title + '</div>' : ''), (settings.closeBtn ? '<button class="' + settings.baseCls + '-close" data-handler="destroy"><span>x</span></button>' : '') ]; if (settings.dialog) { strBuilder.push( '<div class="' + settings.baseCls + '-message">' + settings.message + (settings.loader ? '<div class="' + settings.baseCls + '-loader"></div>' : '') + '</div>' ); } else { strBuilder.push( '<div class="' + settings.baseCls + '-message">' + settings.message + '</div>', (settings.loader ? '<div class="' + settings.baseCls + '-loader"></div>' : '') ); } if (settings.buttons) { var btnPh = $('<div />'); _.each(settings.buttons, function (btn) { btnPh.append($('<button/>', btn)); }); strBuilder.push('<div class="' + settings.baseCls + '-btns">' + btnPh.html() + '</div>'); } strBuilder.push('</div></div>'); return strBuilder.join(''); }, initEl: function () { var el = this.el ? this.el : 'body', $el = _.isObject(el) ? el : $(el); if (!$el.length) { return $($.proxy(this.initEl, this)); } this.$el = $el; this._cssPos = ($el.get(0) === document.body) ? 'fixed' : 'absolute'; ($el.get(0) !== document.body) && $el.css('position', 'relative'); if (this._cssPos === 'absolute') { $el.css('overflow', 'hidden'); } }, initialize: function (options) { var scope = this; this.el = options && options.el; this.initEl.call(this, options && options.el); this.current = {}; scope.NotificationView = Backbone.View.extend({ defaults: scope.attributes, on: function (eventName, handler) { var fn = handler, view = this; if (_.isString(handler)) { fn = function () { view[handler].apply(view, arguments); }; } return Backbone.View.prototype.on.call(this, eventName, fn); } }); var notifyFn = function (type, opts) { if (_.isString(opts)) { opts = {message: opts}; } var o = _.extend({}, {'type': ''}, opts); o.type = o.type ? type + ' ' + o.type : type; return scope.notify(o); }; var createNotifyFn = function (type) { scope[type] = scope[type] || function (opts) { return notifyFn(type, opts); }; }; _.each(scope.attributes.types, function (type) { createNotifyFn(type); }); if (scope.attributes) { var initialModules = this.attributes.modules; initialModules && $.each(initialModules, function (mName, m) { m.name = _.isArray(initialModules) ? m.name : mName; Notifier.regModule(m); }); scope.attributes.modules = undefined; } }, calcZIndex: function () { if (this._cssPos === 'absolute') { return this.attributes.zIndex; } var z = this.attributes.zIndex + 1, scope = this; _.each(scope.current, function (view) { z = view.zIndex > z ? view.zIndex : z; }); return ++z; }, destroyAll: function (keyFilter, valueFilter) { var i = 0, scope = this; if (_.isFunction(keyFilter)) { _.each(scope.current, function (view) { if (keyFilter(view)) { view.destroy.call(view); i++; } }); } else if (keyFilter !== undefined) { _.each(scope.current, function (view) { if (view.settings[keyFilter] === valueFilter) { view.destroy.call(view); i++; } }); } else { _.each(scope.current, function (view) { view.destroy.call(view); i++; }); } return i; }, getWrapperCls: function (settings) { var c = (settings.baseCls + ' ') + (settings.type ? settings.type + ' ' : '') + ('theme-' + settings.theme + ' ') + (settings.dialog ? 'dialog ' : '') + ('pos-' + settings.position + ' ') + (settings.buttons ? 'with-buttons ' : '') + (settings.loader ? 'with-loader ' : '') + (settings.closeBtn ? 'with-close-btn ' : ''); return $.trim(c).split(' ').join(' ' + settings.baseCls + '-') + ' ' + (settings.cls || ''); }, getSettings: function (options) { if (_.isString(options)) { options = {message: options}; } var settings = $.extend({}, this.attributes, options); if (settings.title && options.dialog === undefined) { settings.dialog = true; } if ((settings.modal || settings.dialog) && options.hideOnClick === undefined) { settings.hideOnClick = false; } if (settings.dialog && options.ms === undefined) { settings.ms = null; } if (settings.dialog && options.position === undefined) { settings.position = 'center'; } return settings; }, notify: function (options) { options = options || {}; if (options.el) { this.el = options && options.el; this.initEl.call(this, options && options.el); } var scope = this, settings = this.getSettings(options); if (_.isObject(settings.destroy)) { if (settings.destroy instanceof scope.NotificationView) { settings.destroy.destroy(); } else { scope.destroyAll.apply(scope, _.isArray(settings.destroy) ? settings.destroy : [settings.destroy]); } } else if (settings.destroy === true) { scope.destroyAll(); } var zIndex = options.zIndex || scope.calcZIndex.call(scope); settings.wrapperCls = scope.getWrapperCls(settings); settings.innerCls = settings.baseCls + '-inner'; var msgEl = $(scope.template(settings)), msgInner = msgEl.find('.' + settings.innerCls); settings.width && msgInner.css({width: settings.width}); Notifier._modulesBinder.trigger('beforeAppendMsgEl', scope, settings, msgEl, msgInner); msgEl.css({display: 'none', opacity: 0, position: scope._cssPos, zIndex: settings.modal ? ++zIndex : zIndex}).prependTo(scope.$el); var msgView = new scope.NotificationView({ el: msgEl }); msgView.settings = settings; if (settings.buttons || msgInner.find('button').length) { msgInner.on('click', 'button[data-handler]', function () { var handler = $(this).data('handler'), fn = _.isFunction(handler) ? handler : msgView[handler]; fn.apply(msgView, arguments); }); msgInner.on('button click', function (e) { msgView.trigger('click:' + $(e.target).data('role')); }); } var removeFn = msgView.destroy = function (e) { if (_.isObject(e) && e.preventDefault) { e.preventDefault(); e.stopPropagation(); } msgView.trigger('beforeHide', msgView, msgEl); settings.modal && msgView.screenEl.fadeOut(300, function () { msgView.trigger('screenHide', msgView, msgEl); msgView.screenEl.remove(); }); Notifier._modulesBinder.trigger('beforeHideMsgEl', scope, settings, msgEl, msgInner, msgView); var outAnimFn = $.isFunction(settings.out) ? settings.out : scope.transitions[settings.position].out; outAnimFn.call(scope, msgEl, msgInner, settings, settings.fadeOutMs, function () { msgView.remove(); msgView.trigger('destroy', msgView, msgEl); Notifier._modulesBinder.trigger('afterDestroyMsgEl', scope, settings, msgEl, msgInner, msgView); _.isFunction(e) && e.call(msgView, msgView, msgEl); }); if (msgView.timeoutId) { clearTimeout(msgView.timeoutId); } delete msgView.timeoutId; delete scope.current[msgView.cid]; }; var preventDefaultFn = function (e) { if (e) { e.preventDefault(); e.stopPropagation(); } }; if (settings.modal) { msgView.screenEl = $('<div/>', { 'class': settings.baseCls + '-screen ' + settings.baseCls + '-theme-' + settings.theme, css: { position: scope._cssPos, top: 0, left: 0, width: '100%', height: '100%', opacity: 0, zIndex: zIndex - 1 } }).prependTo(scope.$el) .click(function (e) { e.preventDefault(); e.stopPropagation(); return false; }).fadeTo(300, settings.screenOpacity); } if (settings.ms > 0 || settings.ms === 0) { msgView.timeoutId = setTimeout(function () { msgView.trigger('timeout', msgView, msgEl); removeFn(); }, settings.ms); } msgInner.click(settings.hideOnClick ? removeFn : preventDefaultFn); settings.css && msgInner.css(settings.css); var animateFn = $.isFunction(settings['in']) ? settings['in'] : scope.transitions[settings.position]['in']; scope.current[msgView.cid] = msgView; msgView.zIndex = zIndex; Notifier._modulesBinder.trigger('beforeAnimateInMsgEl', scope, settings, msgEl, msgInner, msgView); animateFn.call(scope, msgEl, msgInner, settings, settings.fadeInMs, function () { Notifier._modulesBinder.trigger('afterAnimateInMsgEl', scope, settings, msgEl, msgInner, msgView); }); return msgView; } }); // ====================== Modules mechanism ====================== Notifier.getModule = function (moduleName) { return (_.isObject(moduleName)) ? moduleName : Notifier.modules[moduleName]; }; var modulesBinder = {}, shift = Array.prototype.shift; _.extend(modulesBinder, Backbone.Events); Notifier._modulesBinder = modulesBinder; Notifier.regModule = function (moduleName, m) { if (arguments.length === 1) { m = $.isFunction(moduleName) ? moduleName() : moduleName; moduleName = m.name; } else { m = $.isFunction(m) ? m() : m; m.name = moduleName; } if (!moduleName) { throw('module name is not defined.'); } if (m.extend) { $.each(m.extend, function (k, v) { var orig = Notifier.prototype[k]; if (_.isFunction(v) || orig === undefined) { Notifier.prototype[k] = function () { return v.apply({scope: this, supr: orig, module: m}, arguments); }; } else if (!_.isObject(v)) { Notifier.prototype[k] = v; } else { Notifier.prototype[k] = $.extend(true, {}, orig, v); } }); } (Notifier.modules = Notifier.modules || {})[moduleName] = m; $.isFunction(m.register) && m.register.call(m, Notifier); m.enabled && Notifier.enableModule(m); return m; }; Notifier.enableModule = function (moduleName) { var m = Notifier.getModule(moduleName); if (m) { m._handlers = m._handlers || {}; m.events && $.each(m.events, function (k, fn) { var handler = m._handlers[k] = function () { var notifier = shift.call(arguments); fn.apply({module: m, scope: notifier}, arguments); }; Notifier._modulesBinder.on(k, handler); }); m.enabled = true; $.isFunction(m.enable) && m.enable.call(m, Notifier); return m; } // console.log('module "' + moduleName + '" is not registered.'); return false; }; Notifier.disableModule = function (moduleName) { var m = Notifier.getModule(moduleName); if (m) { $.each(m._handlers, function (k, fn) { Notifier._modulesBinder.off(k, fn); }); m.enabled = false; $.isFunction(m.disable) && m.disable.call(m, Notifier); return m; } // console.log('module "' + moduleName + '" is not registered.'); return false; }; })(jQuery, Backbone, _); /*! * Backbone.Notifier Template Module v0.0.1 * Copyright 2012, Eyal Weiss * Backbone.Notifier Template Module be freely distributed under the MIT license. */ (function(Notifier, $, _){ Notifier.regModule({ name: 'template', enabled: true, extend: { template: function(settings){ if (settings.data) { var compiled = _.template(settings.message); settings.message = compiled(settings.data); } return this.supr.call(this.scope, settings); } } }); })(Backbone.Notifier, jQuery, _);