kaiwa/clientapp/libraries/ui.js

1521 lines
26 KiB
JavaScript
Raw Normal View History

2013-09-12 14:18:44 -04:00
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>");