kaiwa/clientapp/libraries/ui.js

1521 lines
26 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

var ui = {};
;(function(exports){
/**
* Expose `Emitter`.
*/
exports.Emitter = Emitter;
/**
* Initialize a new `Emitter`.
*
* @api public
*/
function Emitter() {
this.callbacks = {};
};
/**
* Listen on the given `event` with `fn`.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
* @api public
*/
Emitter.prototype.on = function(event, fn){
(this.callbacks[event] = this.callbacks[event] || [])
.push(fn);
return this;
};
/**
* Adds an `event` listener that will be invoked a single
* time then automatically removed.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
* @api public
*/
Emitter.prototype.once = function(event, fn){
var self = this;
function on() {
self.off(event, on);
fn.apply(this, arguments);
}
this.on(event, on);
return this;
};
/**
* Remove the given callback for `event` or all
* registered callbacks.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
* @api public
*/
Emitter.prototype.off = function(event, fn){
var callbacks = this.callbacks[event];
if (!callbacks) return this;
// remove all handlers
if (1 == arguments.length) {
delete this.callbacks[event];
return this;
}
// remove specific handler
var i = callbacks.indexOf(fn);
callbacks.splice(i, 1);
return this;
};
/**
* Emit `event` with the given args.
*
* @param {String} event
* @param {Mixed} ...
* @return {Emitter}
*/
Emitter.prototype.emit = function(event){
var args = [].slice.call(arguments, 1)
, callbacks = this.callbacks[event];
if (callbacks) {
for (var i = 0, len = callbacks.length; i < len; ++i) {
callbacks[i].apply(this, args)
}
}
return this;
};
})(ui);
;(function(exports, html){
/**
* Active dialog.
*/
var active;
/**
* Expose `Dialog`.
*/
exports.Dialog = Dialog;
/**
* Return a new `Dialog` with the given
* (optional) `title` and `msg`.
*
* @param {String} title or msg
* @param {String} msg
* @return {Dialog}
* @api public
*/
exports.dialog = function(title, msg){
switch (arguments.length) {
case 2:
return new Dialog({ title: title, message: msg });
case 1:
return new Dialog({ message: title });
}
};
/**
* Initialize a new `Dialog`.
*
* Options:
*
* - `title` dialog title
* - `message` a message to display
*
* Emits:
*
* - `show` when visible
* - `hide` when hidden
*
* @param {Object} options
* @api public
*/
function Dialog(options) {
ui.Emitter.call(this);
options = options || {};
this.template = html;
this.el = $(this.template);
this.render(options);
if (active) active.hide();
if (Dialog.effect) this.effect(Dialog.effect);
active = this;
};
/**
* Inherit from `Emitter.prototype`.
*/
Dialog.prototype = new ui.Emitter;
/**
* Render with the given `options`.
*
* @param {Object} options
* @api public
*/
Dialog.prototype.render = function(options){
var el = this.el
, title = options.title
, msg = options.message
, self = this;
el.find('.close').click(function(){
self.emit('close');
self.hide();
return false;
});
el.find('h1').text(title);
if (!title) el.find('h1').remove();
// message
if ('string' == typeof msg) {
el.find('p').text(msg);
} else if (msg) {
el.find('p').replaceWith(msg.el || msg);
}
setTimeout(function(){
el.removeClass('hide');
}, 0);
};
/**
* Enable the dialog close link.
*
* @return {Dialog} for chaining
* @api public
*/
Dialog.prototype.closable = function(){
this.el.addClass('closable');
return this;
};
/**
* Set the effect to `type`.
*
* @param {String} type
* @return {Dialog} for chaining
* @api public
*/
Dialog.prototype.effect = function(type){
this._effect = type;
this.el.addClass(type);
return this;
};
/**
* Make it modal!
*
* @return {Dialog} for chaining
* @api public
*/
Dialog.prototype.modal = function(){
this._overlay = ui.overlay();
return this;
};
/**
* Add an overlay.
*
* @return {Dialog} for chaining
* @api public
*/
Dialog.prototype.overlay = function(){
var self = this;
this._overlay = ui
.overlay({ closable: true })
.on('hide', function(){
self.closedOverlay = true;
self.hide();
});
return this;
};
/**
* Show the dialog.
*
* Emits "show" event.
*
* @return {Dialog} for chaining
* @api public
*/
Dialog.prototype.show = function(){
this.emit('show');
if (this._overlay) {
this._overlay.show();
this.el.addClass('modal');
}
this.el.appendTo('body');
this.el.css({ marginLeft: -(this.el.width() / 2) + 'px' });
return this;
};
/**
* Hide the dialog with optional delay of `ms`,
* otherwise the dialog is removed immediately.
*
* Emits "hide" event.
*
* @return {Number} ms
* @return {Dialog} for chaining
* @api public
*/
Dialog.prototype.hide = function(ms){
var self = this;
this.emit('hide');
// duration
if (ms) {
setTimeout(function(){
self.hide();
}, ms);
return this;
}
// hide / remove
this.el.addClass('hide');
if (this._effect) {
setTimeout(function(self){
self.remove();
}, 500, this);
} else {
self.remove();
}
// modal
if (this._overlay && !self.closedOverlay) this._overlay.hide();
return this;
};
/**
* Hide the dialog without potential animation.
*
* @return {Dialog} for chaining
* @api public
*/
Dialog.prototype.remove = function(){
this.el.remove();
return this;
};
})(ui, "<div id=\"dialog\" class=\"hide\">\n <div class=\"content\">\n <h1>Title</h1>\n <a href=\"#\" class=\"close\">×</a>\n <p>Message</p>\n </div>\n</div>");
;(function(exports, html){
/**
* Expose `Overlay`.
*/
exports.Overlay = Overlay;
/**
* Return a new `Overlay` with the given `options`.
*
* @param {Object} options
* @return {Overlay}
* @api public
*/
exports.overlay = function(options){
return new Overlay(options);
};
/**
* Initialize a new `Overlay`.
*
* @param {Object} options
* @api public
*/
function Overlay(options) {
ui.Emitter.call(this);
var self = this;
options = options || {};
this.closable = options.closable;
this.el = $(html);
this.el.appendTo('body');
if (this.closable) {
this.el.click(function(){
self.hide();
});
}
}
/**
* Inherit from `Emitter.prototype`.
*/
Overlay.prototype = new ui.Emitter;
/**
* Show the overlay.
*
* Emits "show" event.
*
* @return {Overlay} for chaining
* @api public
*/
Overlay.prototype.show = function(){
this.emit('show');
this.el.removeClass('hide');
return this;
};
/**
* Hide the overlay.
*
* Emits "hide" event.
*
* @return {Overlay} for chaining
* @api public
*/
Overlay.prototype.hide = function(){
var self = this;
this.emit('hide');
this.el.addClass('hide');
setTimeout(function(){
self.el.remove();
}, 2000);
return this;
};
})(ui, "<div id=\"overlay\" class=\"hide\"></div>");
;(function(exports, html){
/**
* Expose `Confirmation`.
*/
exports.Confirmation = Confirmation;
/**
* Return a new `Confirmation` dialog with the given
* `title` and `msg`.
*
* @param {String} title or msg
* @param {String} msg
* @return {Dialog}
* @api public
*/
exports.confirm = function(title, msg){
switch (arguments.length) {
case 2:
return new Confirmation({ title: title, message: msg });
case 1:
return new Confirmation({ message: title });
}
};
/**
* Initialize a new `Confirmation` dialog.
*
* Options:
*
* - `title` dialog title
* - `message` a message to display
*
* Emits:
*
* - `cancel` the user pressed cancel or closed the dialog
* - `ok` the user clicked ok
* - `show` when visible
* - `hide` when hidden
*
* @param {Object} options
* @api public
*/
function Confirmation(options) {
ui.Dialog.call(this, options);
};
/**
* Inherit from `Dialog.prototype`.
*/
Confirmation.prototype = new ui.Dialog;
/**
* Change "cancel" button `text`.
*
* @param {String} text
* @return {Confirmation}
* @api public
*/
Confirmation.prototype.cancel = function(text){
this.el.find('.cancel').text(text);
return this;
};
/**
* Change "ok" button `text`.
*
* @param {String} text
* @return {Confirmation}
* @api public
*/
Confirmation.prototype.ok = function(text){
this.el.find('.ok').text(text);
return this;
};
/**
* Show the confirmation dialog and invoke `fn(ok)`.
*
* @param {Function} fn
* @return {Confirmation} for chaining
* @api public
*/
Confirmation.prototype.show = function(fn){
ui.Dialog.prototype.show.call(this);
this.callback = fn || function(){};
return this;
};
/**
* Render with the given `options`.
*
* Emits "cancel" event.
* Emits "ok" event.
*
* @param {Object} options
* @api public
*/
Confirmation.prototype.render = function(options){
ui.Dialog.prototype.render.call(this, options);
var self = this
, actions = $(html);
this.el.addClass('confirmation');
this.el.append(actions);
this.on('close', function(){
self.emit('cancel');
self.callback(false);
});
actions.find('.cancel').click(function(){
self.emit('cancel');
self.callback(false);
self.hide();
});
actions.find('.ok').click(function(){
self.emit('ok');
self.callback(true);
self.hide();
});
};
})(ui, "<div class=\"actions\">\n <button class=\"cancel\">Cancel</button>\n <button class=\"ok main\">Ok</button>\n</div>");
;(function(exports, html){
/**
* Expose `ColorPicker`.
*/
exports.ColorPicker = ColorPicker;
/**
* RGB util.
*/
function rgb(r,g,b) {
return 'rgb(' + r + ', ' + g + ', ' + b + ')';
}
/**
* RGBA util.
*/
function rgba(r,g,b,a) {
return 'rgba(' + r + ', ' + g + ', ' + b + ', ' + a + ')';
}
/**
* Initialize a new `ColorPicker`.
*
* Emits:
*
* - `change` with the given color object
*
* @api public
*/
function ColorPicker() {
ui.Emitter.call(this);
this._colorPos = {};
this.el = $(html);
this.main = this.el.find('.main').get(0);
this.spectrum = this.el.find('.spectrum').get(0);
$(this.main).bind('selectstart', function(e){ e.preventDefault() });
$(this.spectrum).bind('selectstart', function(e){ e.preventDefault() });
this.hue(rgb(255, 0, 0));
this.spectrumEvents();
this.mainEvents();
this.w = 180;
this.h = 180;
this.render();
}
/**
* Inherit from `Emitter.prototype`.
*/
ColorPicker.prototype = new ui.Emitter;
/**
* Set width / height to `n`.
*
* @param {Number} n
* @return {ColorPicker} for chaining
* @api public
*/
ColorPicker.prototype.size = function(n){
return this
.width(n)
.height(n);
};
/**
* Set width to `n`.
*
* @param {Number} n
* @return {ColorPicker} for chaining
* @api public
*/
ColorPicker.prototype.width = function(n){
this.w = n;
this.render();
return this;
};
/**
* Set height to `n`.
*
* @param {Number} n
* @return {ColorPicker} for chaining
* @api public
*/
ColorPicker.prototype.height = function(n){
this.h = n;
this.render();
return this;
};
/**
* Spectrum related events.
*
* @api private
*/
ColorPicker.prototype.spectrumEvents = function(){
var self = this
, canvas = $(this.spectrum)
, down;
function update(e) {
var color = self.hueAt(e.offsetY - 4);
self.hue(color.toString());
self.emit('change', color);
self._huePos = e.offsetY;
self.render();
}
canvas.mousedown(function(e){
e.preventDefault();
down = true;
update(e);
});
canvas.mousemove(function(e){
if (down) update(e);
});
canvas.mouseup(function(){
down = false;
});
};
/**
* Hue / lightness events.
*
* @api private
*/
ColorPicker.prototype.mainEvents = function(){
var self = this
, canvas = $(this.main)
, down;
function update(e) {
var color = self.colorAt(e.offsetX, e.offsetY);
self.color(color.toString());
self.emit('change', color);
self._colorPos = e;
self.render();
}
canvas.mousedown(function(e){
down = true;
update(e);
});
canvas.mousemove(function(e){
if (down) update(e);
});
canvas.mouseup(function(){
down = false;
});
};
/**
* Get the RGB color at `(x, y)`.
*
* @param {Number} x
* @param {Number} y
* @return {Object}
* @api private
*/
ColorPicker.prototype.colorAt = function(x, y){
var data = this.main.getContext('2d').getImageData(x, y, 1, 1).data;
return {
r: data[0]
, g: data[1]
, b: data[2]
, toString: function(){
return rgb(this.r, this.g, this.b);
}
};
};
/**
* Get the RGB value at `y`.
*
* @param {Type} name
* @return {Type}
* @api private
*/
ColorPicker.prototype.hueAt = function(y){
var data = this.spectrum.getContext('2d').getImageData(0, y, 1, 1).data;
return {
r: data[0]
, g: data[1]
, b: data[2]
, toString: function(){
return rgb(this.r, this.g, this.b);
}
};
};
/**
* Get or set `color`.
*
* @param {String} color
* @return {String|ColorPicker}
* @api public
*/
ColorPicker.prototype.color = function(color){
// TODO: update pos
if (0 == arguments.length) return this._color;
this._color = color;
return this;
};
/**
* Get or set hue `color`.
*
* @param {String} color
* @return {String|ColorPicker}
* @api public
*/
ColorPicker.prototype.hue = function(color){
// TODO: update pos
if (0 == arguments.length) return this._hue;
this._hue = color;
return this;
};
/**
* Render with the given `options`.
*
* @param {Object} options
* @api public
*/
ColorPicker.prototype.render = function(options){
options = options || {};
this.renderMain(options);
this.renderSpectrum(options);
};
/**
* Render spectrum.
*
* @api private
*/
ColorPicker.prototype.renderSpectrum = function(options){
var el = this.el
, canvas = this.spectrum
, ctx = canvas.getContext('2d')
, pos = this._huePos
, w = this.w * .12
, h = this.h;
canvas.width = w;
canvas.height = h;
var grad = ctx.createLinearGradient(0, 0, 0, h);
grad.addColorStop(0, rgb(255, 0, 0));
grad.addColorStop(.15, rgb(255, 0, 255));
grad.addColorStop(.33, rgb(0, 0, 255));
grad.addColorStop(.49, rgb(0, 255, 255));
grad.addColorStop(.67, rgb(0, 255, 0));
grad.addColorStop(.84, rgb(255, 255, 0));
grad.addColorStop(1, rgb(255, 0, 0));
ctx.fillStyle = grad;
ctx.fillRect(0, 0, w, h);
// pos
if (!pos) return;
ctx.fillStyle = rgba(0,0,0, .3);
ctx.fillRect(0, pos, w, 1);
ctx.fillStyle = rgba(255,255,255, .3);
ctx.fillRect(0, pos + 1, w, 1);
};
/**
* Render hue/luminosity canvas.
*
* @api private
*/
ColorPicker.prototype.renderMain = function(options){
var el = this.el
, canvas = this.main
, ctx = canvas.getContext('2d')
, w = this.w
, h = this.h
, x = (this._colorPos.offsetX || w) + .5
, y = (this._colorPos.offsetY || 0) + .5;
canvas.width = w;
canvas.height = h;
var grad = ctx.createLinearGradient(0, 0, w, 0);
grad.addColorStop(0, rgb(255, 255, 255));
grad.addColorStop(1, this._hue);
ctx.fillStyle = grad;
ctx.fillRect(0, 0, w, h);
grad = ctx.createLinearGradient(0, 0, 0, h);
grad.addColorStop(0, rgba(255, 255, 255, 0));
grad.addColorStop(1, rgba(0, 0, 0, 1));
ctx.fillStyle = grad;
ctx.fillRect(0, 0, w, h);
// pos
var rad = 10;
ctx.save();
ctx.beginPath();
ctx.lineWidth = 1;
// outer dark
ctx.strokeStyle = rgba(0,0,0,.5);
ctx.arc(x, y, rad / 2, 0, Math.PI * 2, false);
ctx.stroke();
// outer light
ctx.strokeStyle = rgba(255,255,255,.5);
ctx.arc(x, y, rad / 2 - 1, 0, Math.PI * 2, false);
ctx.stroke();
ctx.beginPath();
ctx.restore();
};
})(ui, "<div class=\"color-picker\">\n <canvas class=\"main\"></canvas>\n <canvas class=\"spectrum\"></canvas>\n</div>");
;(function(exports, html){
/**
* Notification list.
*/
var list;
/**
* Expose `Notification`.
*/
exports.Notification = Notification;
// list
$(function(){
list = $('<ul id="notifications">');
list.appendTo('body');
})
/**
* Return a new `Notification` with the given
* (optional) `title` and `msg`.
*
* @param {String} title or msg
* @param {String} msg
* @return {Dialog}
* @api public
*/
exports.notify = function(title, msg){
switch (arguments.length) {
case 2:
return new Notification({ title: title, message: msg })
.show()
.hide(4000);
case 1:
return new Notification({ message: title })
.show()
.hide(4000);
}
};
/**
* Construct a notification function for `type`.
*
* @param {String} type
* @return {Function}
* @api private
*/
function type(type) {
return function(title, msg){
return exports.notify.apply(this, arguments)
.type(type);
}
}
/**
* Notification methods.
*/
exports.info = exports.notify;
exports.warn = type('warn');
exports.error = type('error');
/**
* Initialize a new `Notification`.
*
* Options:
*
* - `title` dialog title
* - `message` a message to display
*
* @param {Object} options
* @api public
*/
function Notification(options) {
ui.Emitter.call(this);
options = options || {};
this.template = html;
this.el = $(this.template);
this.render(options);
if (Notification.effect) this.effect(Notification.effect);
};
/**
* Inherit from `Emitter.prototype`.
*/
Notification.prototype = new ui.Emitter;
/**
* Render with the given `options`.
*
* @param {Object} options
* @api public
*/
Notification.prototype.render = function(options){
var el = this.el
, title = options.title
, msg = options.message
, self = this;
el.find('.close').click(function(){
self.hide();
return false;
});
el.click(function(e){
e.preventDefault();
self.emit('click', e);
});
el.find('h1').text(title);
if (!title) el.find('h1').remove();
// message
if ('string' == typeof msg) {
el.find('p').text(msg);
} else if (msg) {
el.find('p').replaceWith(msg.el || msg);
}
setTimeout(function(){
el.removeClass('hide');
}, 0);
};
/**
* Enable the dialog close link.
*
* @return {Notification} for chaining
* @api public
*/
Notification.prototype.closable = function(){
this.el.addClass('closable');
return this;
};
/**
* Set the effect to `type`.
*
* @param {String} type
* @return {Notification} for chaining
* @api public
*/
Notification.prototype.effect = function(type){
this._effect = type;
this.el.addClass(type);
return this;
};
/**
* Show the notification.
*
* @return {Notification} for chaining
* @api public
*/
Notification.prototype.show = function(){
this.el.appendTo(list);
return this;
};
/**
* Set the notification `type`.
*
* @param {String} type
* @return {Notification} for chaining
* @api public
*/
Notification.prototype.type = function(type){
this._type = type;
this.el.addClass(type);
return this;
};
/**
* Make it stick (clear hide timer), and make it closable.
*
* @return {Notification} for chaining
* @api public
*/
Notification.prototype.sticky = function(){
return this.hide(0).closable();
};
/**
* Hide the dialog with optional delay of `ms`,
* otherwise the notification is removed immediately.
*
* @return {Number} ms
* @return {Notification} for chaining
* @api public
*/
Notification.prototype.hide = function(ms){
var self = this;
// duration
if ('number' == typeof ms) {
clearTimeout(this.timer);
if (!ms) return this;
this.timer = setTimeout(function(){
self.hide();
}, ms);
return this;
}
// hide / remove
this.el.addClass('hide');
if (this._effect) {
setTimeout(function(self){
self.remove();
}, 500, this);
} else {
self.remove();
}
return this;
};
/**
* Hide the notification without potential animation.
*
* @return {Dialog} for chaining
* @api public
*/
Notification.prototype.remove = function(){
this.el.remove();
return this;
};
})(ui, "<li class=\"notification hide\">\n <div class=\"content\">\n <h1>Title</h1>\n <a href=\"#\" class=\"close\">×</a>\n <p>Message</p>\n </div>\n</li>");
;(function(exports, html){
/**
* Expose `SplitButton`.
*/
exports.SplitButton = SplitButton;
/**
* Initialize a new `SplitButton`
* with an optional `label`.
*
* @param {String} label
* @api public
*/
function SplitButton(label) {
ui.Emitter.call(this);
this.el = $(html);
this.events();
this.render({ label: label });
this.state = 'hidden';
}
/**
* Inherit from `Emitter.prototype`.
*/
SplitButton.prototype = new ui.Emitter;
/**
* Register event handlers.
*
* @api private
*/
SplitButton.prototype.events = function(){
var self = this
, el = this.el;
el.find('.button').click(function(e){
e.preventDefault();
self.emit('click', e);
});
el.find('.toggle').click(function(e){
e.preventDefault();
self.toggle();
});
};
/**
* Toggle the drop-down contents.
*
* @return {SplitButton}
* @api public
*/
SplitButton.prototype.toggle = function(){
return 'hidden' == this.state
? this.show()
: this.hide();
};
/**
* Show the drop-down contents.
*
* @return {SplitButton}
* @api public
*/
SplitButton.prototype.show = function(){
this.state = 'visible';
this.emit('show');
return this;
};
/**
* Hide the drop-down contents.
*
* @return {SplitButton}
* @api public
*/
SplitButton.prototype.hide = function(){
this.state = 'hidden';
this.emit('hide');
return this;
};
/**
* Render the split-button with the given `options`.
*
* @param {Object} options
* @return {SplitButton}
* @api private
*/
SplitButton.prototype.render = function(options){
var options = options || {}
, button = this.el.find('.button')
, label = options.label;
if ('string' == label) button.text(label);
else button.text('').append(label);
return this;
};
})(ui, "<div class=\"split-button\">\n <a class=\"button\" href=\"#\">Action</a>\n <a class=\"toggle\" href=\"#\"><span></span></a>\n</div>");
;(function(exports, html){
/**
* Expose `Menu`.
*/
exports.Menu = Menu;
/**
* Create a new `Menu`.
*
* @return {Menu}
* @api public
*/
exports.menu = function(){
return new Menu;
};
/**
* Initialize a new `Menu`.
*
* Emits:
*
* - "show" when shown
* - "hide" when hidden
* - "remove" with the item name when an item is removed
* - * menu item events are emitted when clicked
*
* @api public
*/
function Menu() {
var self = this;
ui.Emitter.call(this);
this.items = {};
this.el = $(html).hide().appendTo('body');
$('html').click(function(){ self.hide(); });
};
/**
* Inherit from `Emitter.prototype`.
*/
Menu.prototype = new ui.Emitter;
/**
* Add menu item with the given `text` and optional callback `fn`.
*
* When the item is clicked `fn()` will be invoked
* and the `Menu` is immediately closed. When clicked
* an event of the name `text` is emitted regardless of
* the callback function being present.
*
* @param {String} text
* @param {Function} fn
* @return {Menu}
* @api public
*/
Menu.prototype.add = function(text, fn){
var self = this
, el = $('<li><a href="#">' + text + '</a></li>')
.addClass(slug(text))
.appendTo(this.el)
.click(function(e){
e.preventDefault();
e.stopPropagation();
self.hide();
self.emit(text);
fn && fn();
});
this.items[text] = el;
return this;
};
/**
* Remove menu item with the given `text`.
*
* @param {String} text
* @return {Menu}
* @api public
*/
Menu.prototype.remove = function(text){
var item = this.items[text];
if (!item) throw new Error('no menu item named "' + text + '"');
this.emit('remove', text);
item.remove();
delete this.items[text];
return this;
};
/**
* Check if this menu has an item with the given `text`.
*
* @param {String} text
* @return {Boolean}
* @api public
*/
Menu.prototype.has = function(text){
return !! this.items[text];
};
/**
* Move context menu to `(x, y)`.
*
* @param {Number} x
* @param {Number} y
* @return {Menu}
* @api public
*/
Menu.prototype.moveTo = function(x, y){
this.el.css({
top: y,
left: x
});
return this;
};
/**
* Show the menu.
*
* @return {Menu}
* @api public
*/
Menu.prototype.show = function(){
this.emit('show');
this.el.show();
return this;
};
/**
* Hide the menu.
*
* @return {Menu}
* @api public
*/
Menu.prototype.hide = function(){
this.emit('hide');
this.el.hide();
return this;
};
/**
* Generate a slug from `str`.
*
* @param {String} str
* @return {String}
* @api private
*/
function slug(str) {
return str
.toLowerCase()
.replace(/ +/g, '-')
.replace(/[^a-z0-9-]/g, '');
}
})(ui, "<div class=\"menu\">\n</div>");
;(function(exports, html){
/**
* Expose `Card`.
*/
exports.Card = Card;
/**
* Create a new `Card`.
*
* @param {Mixed} front
* @param {Mixed} back
* @return {Card}
* @api public
*/
exports.card = function(front, back){
return new Card(front, back);
};
/**
* Initialize a new `Card` with content
* for face `front` and `back`.
*
* Emits "flip" event.
*
* @param {Mixed} front
* @param {Mixed} back
* @api public
*/
function Card(front, back) {
ui.Emitter.call(this);
this._front = front || $('<p>front</p>');
this._back = back || $('<p>back</p>');
this.template = html;
this.render();
};
/**
* Inherit from `Emitter.prototype`.
*/
Card.prototype = new ui.Emitter;
/**
* Set front face `val`.
*
* @param {Mixed} val
* @return {Card}
* @api public
*/
Card.prototype.front = function(val){
this._front = val;
this.render();
return this;
};
/**
* Set back face `val`.
*
* @param {Mixed} val
* @return {Card}
* @api public
*/
Card.prototype.back = function(val){
this._back = val;
this.render();
return this;
};
/**
* Flip the card.
*
* @return {Card} for chaining
* @api public
*/
Card.prototype.flip = function(){
this.emit('flip');
this.el.toggleClass('flipped');
return this;
};
/**
* Set the effect to `type`.
*
* @param {String} type
* @return {Dialog} for chaining
* @api public
*/
Card.prototype.effect = function(type){
this.el.addClass(type);
return this;
};
/**
* Render with the given `options`.
*
* @param {Object} options
* @api public
*/
Card.prototype.render = function(options){
var self = this
, el = this.el = $(this.template);
el.find('.front').empty().append(this._front.el || $(this._front));
el.find('.back').empty().append(this._back.el || $(this._back));
el.click(function(){
self.flip();
});
};
})(ui, "<div class=\"card\">\n <div class=\"wrapper\">\n <div class=\"face front\">1</div>\n <div class=\"face back\">2</div>\n </div>\n</div>");