Nuiton-js-commits
Threads by month
- ----- 2026 -----
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- 636 discussions
r99 - in sandbox/nuiton-js-leaflet/src/main/resources: META-INF/nuiton-js nuiton-js-leaflet
by echatellier@users.nuiton.org 19 Jun '13
by echatellier@users.nuiton.org 19 Jun '13
19 Jun '13
Author: echatellier
Date: 2013-06-19 16:40:43 +0200 (Wed, 19 Jun 2013)
New Revision: 99
Url: http://nuiton.org/projects/nuiton-js/repository/revisions/99
Log:
Remove hack (not used)
Removed:
sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/leaflet-fix.css
Modified:
sandbox/nuiton-js-leaflet/src/main/resources/META-INF/nuiton-js/wro-leaflet.xml
sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/leaflet-src.js
Modified: sandbox/nuiton-js-leaflet/src/main/resources/META-INF/nuiton-js/wro-leaflet.xml
===================================================================
--- sandbox/nuiton-js-leaflet/src/main/resources/META-INF/nuiton-js/wro-leaflet.xml 2013-06-19 14:06:55 UTC (rev 98)
+++ sandbox/nuiton-js-leaflet/src/main/resources/META-INF/nuiton-js/wro-leaflet.xml 2013-06-19 14:40:43 UTC (rev 99)
@@ -28,7 +28,6 @@
<group name='leaflet'>
<js>classpath:nuiton-js-leaflet/leaflet-src.js</js>
<css>classpath:nuiton-js-leaflet/leaflet.css</css>
- <css>classpath:nuiton-js-leaflet/leaflet-fix.css</css>
</group>
<group name='leaflet-ie'>
Deleted: sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/leaflet-fix.css
===================================================================
--- sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/leaflet-fix.css 2013-06-19 14:06:55 UTC (rev 98)
+++ sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/leaflet-fix.css 2013-06-19 14:40:43 UTC (rev 99)
@@ -1,4 +0,0 @@
-/* add hack for this image to be in cache */
-.fixleafletwro {
- background: url('images/marker-icon.png');
-}
\ No newline at end of file
Modified: sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/leaflet-src.js
===================================================================
--- sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/leaflet-src.js 2013-06-19 14:06:55 UTC (rev 98)
+++ sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/leaflet-src.js 2013-06-19 14:40:43 UTC (rev 99)
@@ -3045,8 +3045,7 @@
matches = src.match(leafletRe);
if (matches) {
- //return src.split(leafletRe)[0] + '/images';
- return src.split(leafletRe)[0] + '/wroResources?id=classpath:nuiton-js-leaflet/images';
+ return src.split(leafletRe)[0] + '/images';
}
}
}());
1
0
r98 - in sandbox/nuiton-js-leaflet/src/main/resources: META-INF/nuiton-js nuiton-js-leaflet
by echatellier@users.nuiton.org 19 Jun '13
by echatellier@users.nuiton.org 19 Jun '13
19 Jun '13
Author: echatellier
Date: 2013-06-19 16:06:55 +0200 (Wed, 19 Jun 2013)
New Revision: 98
Url: http://nuiton.org/projects/nuiton-js/repository/revisions/98
Log:
Patch leaflet to find correct images.
Added:
sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/leaflet-fix.css
Modified:
sandbox/nuiton-js-leaflet/src/main/resources/META-INF/nuiton-js/wro-leaflet.xml
sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/leaflet-src.js
Modified: sandbox/nuiton-js-leaflet/src/main/resources/META-INF/nuiton-js/wro-leaflet.xml
===================================================================
--- sandbox/nuiton-js-leaflet/src/main/resources/META-INF/nuiton-js/wro-leaflet.xml 2013-06-19 11:24:48 UTC (rev 97)
+++ sandbox/nuiton-js-leaflet/src/main/resources/META-INF/nuiton-js/wro-leaflet.xml 2013-06-19 14:06:55 UTC (rev 98)
@@ -28,6 +28,7 @@
<group name='leaflet'>
<js>classpath:nuiton-js-leaflet/leaflet-src.js</js>
<css>classpath:nuiton-js-leaflet/leaflet.css</css>
+ <css>classpath:nuiton-js-leaflet/leaflet-fix.css</css>
</group>
<group name='leaflet-ie'>
Added: sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/leaflet-fix.css
===================================================================
--- sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/leaflet-fix.css (rev 0)
+++ sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/leaflet-fix.css 2013-06-19 14:06:55 UTC (rev 98)
@@ -0,0 +1,4 @@
+/* add hack for this image to be in cache */
+.fixleafletwro {
+ background: url('images/marker-icon.png');
+}
\ No newline at end of file
Modified: sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/leaflet-src.js
===================================================================
--- sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/leaflet-src.js 2013-06-19 11:24:48 UTC (rev 97)
+++ sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/leaflet-src.js 2013-06-19 14:06:55 UTC (rev 98)
@@ -3045,7 +3045,8 @@
matches = src.match(leafletRe);
if (matches) {
- return src.split(leafletRe)[0] + '/images';
+ //return src.split(leafletRe)[0] + '/images';
+ return src.split(leafletRe)[0] + '/wroResources?id=classpath:nuiton-js-leaflet/images';
}
}
}());
1
0
r97 - in sandbox/nuiton-js-leaflet: . src src/main src/main/resources src/main/resources/META-INF src/main/resources/META-INF/nuiton-js src/main/resources/nuiton-js-leaflet src/main/resources/nuiton-js-leaflet/images
by echatellier@users.nuiton.org 19 Jun '13
by echatellier@users.nuiton.org 19 Jun '13
19 Jun '13
Author: echatellier
Date: 2013-06-19 13:24:48 +0200 (Wed, 19 Jun 2013)
New Revision: 97
Url: http://nuiton.org/projects/nuiton-js/repository/revisions/97
Log:
Initial import.
Added:
sandbox/nuiton-js-leaflet/LICENSE.txt
sandbox/nuiton-js-leaflet/README.txt
sandbox/nuiton-js-leaflet/changelog.txt
sandbox/nuiton-js-leaflet/pom.xml
sandbox/nuiton-js-leaflet/src/
sandbox/nuiton-js-leaflet/src/main/
sandbox/nuiton-js-leaflet/src/main/resources/
sandbox/nuiton-js-leaflet/src/main/resources/META-INF/
sandbox/nuiton-js-leaflet/src/main/resources/META-INF/nuiton-js/
sandbox/nuiton-js-leaflet/src/main/resources/META-INF/nuiton-js/wro-leaflet.xml
sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/
sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/images/
sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/images/layers.png
sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/images/marker-icon.png
sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/images/marker-icon(a)2x.png
sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/images/marker-shadow.png
sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/leaflet-src.js
sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/leaflet.css
sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/leaflet.ie.css
Added: sandbox/nuiton-js-leaflet/LICENSE.txt
===================================================================
Added: sandbox/nuiton-js-leaflet/README.txt
===================================================================
Added: sandbox/nuiton-js-leaflet/changelog.txt
===================================================================
Added: sandbox/nuiton-js-leaflet/pom.xml
===================================================================
(Binary files differ)
Property changes on: sandbox/nuiton-js-leaflet/pom.xml
___________________________________________________________________
Added: svn:mime-type
+ application/xml
Added: sandbox/nuiton-js-leaflet/src/main/resources/META-INF/nuiton-js/wro-leaflet.xml
===================================================================
--- sandbox/nuiton-js-leaflet/src/main/resources/META-INF/nuiton-js/wro-leaflet.xml (rev 0)
+++ sandbox/nuiton-js-leaflet/src/main/resources/META-INF/nuiton-js/wro-leaflet.xml 2013-06-19 11:24:48 UTC (rev 97)
@@ -0,0 +1,38 @@
+<!--
+ #%L
+ Nuiton JS :: Leaflet
+ $Id:$
+ $HeadURL:$
+ %%
+ Copyright (C) 2013 CodeLutin, Chatellier Eric
+ %%
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Lesser Public License for more details.
+
+ You should have received a copy of the GNU General Lesser Public
+ License along with this program. If not, see
+ <http://www.gnu.org/licenses/lgpl-3.0.html>.
+ #L%
+ -->
+<groups xmlns="http://www.isdc.ro/wro">
+
+<import>classpath:META-INF/nuiton-js/wro-jquery.xml</import>
+
+ <group name='leaflet'>
+ <js>classpath:nuiton-js-leaflet/leaflet-src.js</js>
+ <css>classpath:nuiton-js-leaflet/leaflet.css</css>
+ </group>
+
+ <group name='leaflet-ie'>
+ <group-ref>jquery</group-ref>
+ <css>classpath:nuiton-js-leaflet/leaflet.ie.css</css>
+ </group>
+
+</groups>
Added: sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/images/layers.png
===================================================================
(Binary files differ)
Property changes on: sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/images/layers.png
___________________________________________________________________
Added: svn:mime-type
+ image/png
Added: sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/images/marker-icon.png
===================================================================
(Binary files differ)
Property changes on: sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/images/marker-icon.png
___________________________________________________________________
Added: svn:mime-type
+ image/png
Added: sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/images/marker-icon(a)2x.png
===================================================================
(Binary files differ)
Property changes on: sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/images/marker-icon(a)2x.png
___________________________________________________________________
Added: svn:mime-type
+ image/png
Added: sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/images/marker-shadow.png
===================================================================
(Binary files differ)
Property changes on: sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/images/marker-shadow.png
___________________________________________________________________
Added: svn:mime-type
+ image/png
Added: sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/leaflet-src.js
===================================================================
--- sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/leaflet-src.js (rev 0)
+++ sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/leaflet-src.js 2013-06-19 11:24:48 UTC (rev 97)
@@ -0,0 +1,8339 @@
+/* %%Ignore-License
+ Leaflet, a JavaScript library for mobile-friendly interactive maps. http://leafletjs.com
+ (c) 2010-2013, Vladimir Agafonkin, CloudMade
+*/
+(function (window, document, undefined) {/*
+ * The L namespace contains all Leaflet classes and functions.
+ * This code allows you to handle any possible namespace conflicts.
+ */
+
+var L, originalL;
+
+if (typeof exports !== undefined + '') {
+ L = exports;
+} else {
+ originalL = window.L;
+ L = {};
+
+ L.noConflict = function () {
+ window.L = originalL;
+ return this;
+ };
+
+ window.L = L;
+}
+
+L.version = '0.5.1';
+
+
+/*
+ * L.Util contains various utility functions used throughout Leaflet code.
+ */
+
+L.Util = {
+ extend: function (dest) { // (Object[, Object, ...]) ->
+ var sources = Array.prototype.slice.call(arguments, 1),
+ i, j, len, src;
+
+ for (j = 0, len = sources.length; j < len; j++) {
+ src = sources[j] || {};
+ for (i in src) {
+ if (src.hasOwnProperty(i)) {
+ dest[i] = src[i];
+ }
+ }
+ }
+ return dest;
+ },
+
+ bind: function (fn, obj) { // (Function, Object) -> Function
+ var args = arguments.length > 2 ? Array.prototype.slice.call(arguments, 2) : null;
+ return function () {
+ return fn.apply(obj, args || arguments);
+ };
+ },
+
+ stamp: (function () {
+ var lastId = 0, key = '_leaflet_id';
+ return function (/*Object*/ obj) {
+ obj[key] = obj[key] || ++lastId;
+ return obj[key];
+ };
+ }()),
+
+ limitExecByInterval: function (fn, time, context) {
+ var lock, execOnUnlock;
+
+ return function wrapperFn() {
+ var args = arguments;
+
+ if (lock) {
+ execOnUnlock = true;
+ return;
+ }
+
+ lock = true;
+
+ setTimeout(function () {
+ lock = false;
+
+ if (execOnUnlock) {
+ wrapperFn.apply(context, args);
+ execOnUnlock = false;
+ }
+ }, time);
+
+ fn.apply(context, args);
+ };
+ },
+
+ falseFn: function () {
+ return false;
+ },
+
+ formatNum: function (num, digits) {
+ var pow = Math.pow(10, digits || 5);
+ return Math.round(num * pow) / pow;
+ },
+
+ splitWords: function (str) {
+ return str.replace(/^\s+|\s+$/g, '').split(/\s+/);
+ },
+
+ setOptions: function (obj, options) {
+ obj.options = L.extend({}, obj.options, options);
+ return obj.options;
+ },
+
+ getParamString: function (obj, existingUrl) {
+ var params = [];
+ for (var i in obj) {
+ if (obj.hasOwnProperty(i)) {
+ params.push(i + '=' + obj[i]);
+ }
+ }
+ return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
+ },
+
+ template: function (str, data) {
+ return str.replace(/\{ *([\w_]+) *\}/g, function (str, key) {
+ var value = data[key];
+ if (!data.hasOwnProperty(key)) {
+ throw new Error('No value provided for variable ' + str);
+ }
+ return value;
+ });
+ },
+
+ isArray: function (obj) {
+ return (Object.prototype.toString.call(obj) === '[object Array]');
+ },
+
+ emptyImageUrl: 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='
+};
+
+(function () {
+
+ // inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/
+
+ function getPrefixed(name) {
+ var i, fn,
+ prefixes = ['webkit', 'moz', 'o', 'ms'];
+
+ for (i = 0; i < prefixes.length && !fn; i++) {
+ fn = window[prefixes[i] + name];
+ }
+
+ return fn;
+ }
+
+ var lastTime = 0;
+
+ function timeoutDefer(fn) {
+ var time = +new Date(),
+ timeToCall = Math.max(0, 16 - (time - lastTime));
+
+ lastTime = time + timeToCall;
+ return window.setTimeout(fn, timeToCall);
+ }
+
+ var requestFn = window.requestAnimationFrame ||
+ getPrefixed('RequestAnimationFrame') || timeoutDefer;
+
+ var cancelFn = window.cancelAnimationFrame ||
+ getPrefixed('CancelAnimationFrame') ||
+ getPrefixed('CancelRequestAnimationFrame') ||
+ function (id) { window.clearTimeout(id); };
+
+
+ L.Util.requestAnimFrame = function (fn, context, immediate, element) {
+ fn = L.bind(fn, context);
+
+ if (immediate && requestFn === timeoutDefer) {
+ fn();
+ } else {
+ return requestFn.call(window, fn, element);
+ }
+ };
+
+ L.Util.cancelAnimFrame = function (id) {
+ if (id) {
+ cancelFn.call(window, id);
+ }
+ };
+
+}());
+
+// shortcuts for most used utility functions
+L.extend = L.Util.extend;
+L.bind = L.Util.bind;
+L.stamp = L.Util.stamp;
+L.setOptions = L.Util.setOptions;
+
+
+/*
+ * L.Class powers the OOP facilities of the library.
+ * Thanks to John Resig and Dean Edwards for inspiration!
+ */
+
+L.Class = function () {};
+
+L.Class.extend = function (props) {
+
+ // extended class with the new prototype
+ var NewClass = function () {
+
+ // call the constructor
+ if (this.initialize) {
+ this.initialize.apply(this, arguments);
+ }
+
+ // call all constructor hooks
+ if (this._initHooks) {
+ this.callInitHooks();
+ }
+ };
+
+ // instantiate class without calling constructor
+ var F = function () {};
+ F.prototype = this.prototype;
+
+ var proto = new F();
+ proto.constructor = NewClass;
+
+ NewClass.prototype = proto;
+
+ //inherit parent's statics
+ for (var i in this) {
+ if (this.hasOwnProperty(i) && i !== 'prototype') {
+ NewClass[i] = this[i];
+ }
+ }
+
+ // mix static properties into the class
+ if (props.statics) {
+ L.extend(NewClass, props.statics);
+ delete props.statics;
+ }
+
+ // mix includes into the prototype
+ if (props.includes) {
+ L.Util.extend.apply(null, [proto].concat(props.includes));
+ delete props.includes;
+ }
+
+ // merge options
+ if (props.options && proto.options) {
+ props.options = L.extend({}, proto.options, props.options);
+ }
+
+ // mix given properties into the prototype
+ L.extend(proto, props);
+
+ proto._initHooks = [];
+
+ var parent = this;
+ // add method for calling all hooks
+ proto.callInitHooks = function () {
+
+ if (this._initHooksCalled) { return; }
+
+ if (parent.prototype.callInitHooks) {
+ parent.prototype.callInitHooks.call(this);
+ }
+
+ this._initHooksCalled = true;
+
+ for (var i = 0, len = proto._initHooks.length; i < len; i++) {
+ proto._initHooks[i].call(this);
+ }
+ };
+
+ return NewClass;
+};
+
+
+// method for adding properties to prototype
+L.Class.include = function (props) {
+ L.extend(this.prototype, props);
+};
+
+// merge new default options to the Class
+L.Class.mergeOptions = function (options) {
+ L.extend(this.prototype.options, options);
+};
+
+// add a constructor hook
+L.Class.addInitHook = function (fn) { // (Function) || (String, args...)
+ var args = Array.prototype.slice.call(arguments, 1);
+
+ var init = typeof fn === 'function' ? fn : function () {
+ this[fn].apply(this, args);
+ };
+
+ this.prototype._initHooks = this.prototype._initHooks || [];
+ this.prototype._initHooks.push(init);
+};
+
+
+/*
+ * L.Mixin.Events is used to add custom events functionality to Leaflet classes.
+ */
+
+var key = '_leaflet_events';
+
+L.Mixin = {};
+
+L.Mixin.Events = {
+
+ addEventListener: function (types, fn, context) { // (String, Function[, Object]) or (Object[, Object])
+ var events = this[key] = this[key] || {},
+ type, i, len;
+
+ // Types can be a map of types/handlers
+ if (typeof types === 'object') {
+ for (type in types) {
+ if (types.hasOwnProperty(type)) {
+ this.addEventListener(type, types[type], fn);
+ }
+ }
+
+ return this;
+ }
+
+ types = L.Util.splitWords(types);
+
+ for (i = 0, len = types.length; i < len; i++) {
+ events[types[i]] = events[types[i]] || [];
+ events[types[i]].push({
+ action: fn,
+ context: context || this
+ });
+ }
+
+ return this;
+ },
+
+ hasEventListeners: function (type) { // (String) -> Boolean
+ return (key in this) && (type in this[key]) && (this[key][type].length > 0);
+ },
+
+ removeEventListener: function (types, fn, context) { // (String[, Function, Object]) or (Object[, Object])
+ var events = this[key],
+ type, i, len, listeners, j;
+
+ if (typeof types === 'object') {
+ for (type in types) {
+ if (types.hasOwnProperty(type)) {
+ this.removeEventListener(type, types[type], fn);
+ }
+ }
+
+ return this;
+ }
+
+ types = L.Util.splitWords(types);
+
+ for (i = 0, len = types.length; i < len; i++) {
+
+ if (this.hasEventListeners(types[i])) {
+ listeners = events[types[i]];
+
+ for (j = listeners.length - 1; j >= 0; j--) {
+ if (
+ (!fn || listeners[j].action === fn) &&
+ (!context || (listeners[j].context === context))
+ ) {
+ listeners.splice(j, 1);
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ fireEvent: function (type, data) { // (String[, Object])
+ if (!this.hasEventListeners(type)) {
+ return this;
+ }
+
+ var event = L.extend({
+ type: type,
+ target: this
+ }, data);
+
+ var listeners = this[key][type].slice();
+
+ for (var i = 0, len = listeners.length; i < len; i++) {
+ listeners[i].action.call(listeners[i].context || this, event);
+ }
+
+ return this;
+ }
+};
+
+L.Mixin.Events.on = L.Mixin.Events.addEventListener;
+L.Mixin.Events.off = L.Mixin.Events.removeEventListener;
+L.Mixin.Events.fire = L.Mixin.Events.fireEvent;
+
+
+/*
+ * L.Browser handles different browser and feature detections for internal Leaflet use.
+ */
+
+(function () {
+
+ var ie = !!window.ActiveXObject,
+ ie6 = ie && !window.XMLHttpRequest,
+ ie7 = ie && !document.querySelector,
+
+ // terrible browser detection to work around Safari / iOS / Android browser bugs
+ ua = navigator.userAgent.toLowerCase(),
+ webkit = ua.indexOf('webkit') !== -1,
+ chrome = ua.indexOf('chrome') !== -1,
+ android = ua.indexOf('android') !== -1,
+ android23 = ua.search('android [23]') !== -1,
+
+ mobile = typeof orientation !== undefined + '',
+ msTouch = window.navigator && window.navigator.msPointerEnabled &&
+ window.navigator.msMaxTouchPoints,
+ retina = ('devicePixelRatio' in window && window.devicePixelRatio > 1) ||
+ ('matchMedia' in window && window.matchMedia('(min-resolution:144dpi)') &&
+ window.matchMedia('(min-resolution:144dpi)').matches),
+
+ doc = document.documentElement,
+ ie3d = ie && ('transition' in doc.style),
+ webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()),
+ gecko3d = 'MozPerspective' in doc.style,
+ opera3d = 'OTransition' in doc.style,
+ any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d || opera3d);
+
+
+ var touch = !window.L_NO_TOUCH && (function () {
+
+ var startName = 'ontouchstart';
+
+ // IE10+ (We simulate these into touch* events in L.DomEvent and L.DomEvent.MsTouch) or WebKit, etc.
+ if (msTouch || (startName in doc)) {
+ return true;
+ }
+
+ // Firefox/Gecko
+ var div = document.createElement('div'),
+ supported = false;
+
+ if (!div.setAttribute) {
+ return false;
+ }
+ div.setAttribute(startName, 'return;');
+
+ if (typeof div[startName] === 'function') {
+ supported = true;
+ }
+
+ div.removeAttribute(startName);
+ div = null;
+
+ return supported;
+ }());
+
+
+ L.Browser = {
+ ie: ie,
+ ie6: ie6,
+ ie7: ie7,
+ webkit: webkit,
+
+ android: android,
+ android23: android23,
+
+ chrome: chrome,
+
+ ie3d: ie3d,
+ webkit3d: webkit3d,
+ gecko3d: gecko3d,
+ opera3d: opera3d,
+ any3d: any3d,
+
+ mobile: mobile,
+ mobileWebkit: mobile && webkit,
+ mobileWebkit3d: mobile && webkit3d,
+ mobileOpera: mobile && window.opera,
+
+ touch: touch,
+ msTouch: msTouch,
+
+ retina: retina
+ };
+
+}());
+
+
+/*
+ * L.Point represents a point with x and y coordinates.
+ */
+
+L.Point = function (/*Number*/ x, /*Number*/ y, /*Boolean*/ round) {
+ this.x = (round ? Math.round(x) : x);
+ this.y = (round ? Math.round(y) : y);
+};
+
+L.Point.prototype = {
+
+ clone: function () {
+ return new L.Point(this.x, this.y);
+ },
+
+ // non-destructive, returns a new point
+ add: function (point) {
+ return this.clone()._add(L.point(point));
+ },
+
+ // destructive, used directly for performance in situations where it's safe to modify existing point
+ _add: function (point) {
+ this.x += point.x;
+ this.y += point.y;
+ return this;
+ },
+
+ subtract: function (point) {
+ return this.clone()._subtract(L.point(point));
+ },
+
+ _subtract: function (point) {
+ this.x -= point.x;
+ this.y -= point.y;
+ return this;
+ },
+
+ divideBy: function (num) {
+ return this.clone()._divideBy(num);
+ },
+
+ _divideBy: function (num) {
+ this.x /= num;
+ this.y /= num;
+ return this;
+ },
+
+ multiplyBy: function (num) {
+ return this.clone()._multiplyBy(num);
+ },
+
+ _multiplyBy: function (num) {
+ this.x *= num;
+ this.y *= num;
+ return this;
+ },
+
+ round: function () {
+ return this.clone()._round();
+ },
+
+ _round: function () {
+ this.x = Math.round(this.x);
+ this.y = Math.round(this.y);
+ return this;
+ },
+
+ floor: function () {
+ return this.clone()._floor();
+ },
+
+ _floor: function () {
+ this.x = Math.floor(this.x);
+ this.y = Math.floor(this.y);
+ return this;
+ },
+
+ distanceTo: function (point) {
+ point = L.point(point);
+
+ var x = point.x - this.x,
+ y = point.y - this.y;
+
+ return Math.sqrt(x * x + y * y);
+ },
+
+ equals: function (point) {
+ return point.x === this.x &&
+ point.y === this.y;
+ },
+
+ toString: function () {
+ return 'Point(' +
+ L.Util.formatNum(this.x) + ', ' +
+ L.Util.formatNum(this.y) + ')';
+ }
+};
+
+L.point = function (x, y, round) {
+ if (x instanceof L.Point) {
+ return x;
+ }
+ if (L.Util.isArray(x)) {
+ return new L.Point(x[0], x[1]);
+ }
+ if (isNaN(x)) {
+ return x;
+ }
+ return new L.Point(x, y, round);
+};
+
+
+/*
+ * L.Bounds represents a rectangular area on the screen in pixel coordinates.
+ */
+
+L.Bounds = function (a, b) { //(Point, Point) or Point[]
+ if (!a) { return; }
+
+ var points = b ? [a, b] : a;
+
+ for (var i = 0, len = points.length; i < len; i++) {
+ this.extend(points[i]);
+ }
+};
+
+L.Bounds.prototype = {
+ // extend the bounds to contain the given point
+ extend: function (point) { // (Point)
+ point = L.point(point);
+
+ if (!this.min && !this.max) {
+ this.min = point.clone();
+ this.max = point.clone();
+ } else {
+ this.min.x = Math.min(point.x, this.min.x);
+ this.max.x = Math.max(point.x, this.max.x);
+ this.min.y = Math.min(point.y, this.min.y);
+ this.max.y = Math.max(point.y, this.max.y);
+ }
+ return this;
+ },
+
+ getCenter: function (round) { // (Boolean) -> Point
+ return new L.Point(
+ (this.min.x + this.max.x) / 2,
+ (this.min.y + this.max.y) / 2, round);
+ },
+
+ getBottomLeft: function () { // -> Point
+ return new L.Point(this.min.x, this.max.y);
+ },
+
+ getTopRight: function () { // -> Point
+ return new L.Point(this.max.x, this.min.y);
+ },
+
+ getSize: function () {
+ return this.max.subtract(this.min);
+ },
+
+ contains: function (obj) { // (Bounds) or (Point) -> Boolean
+ var min, max;
+
+ if (typeof obj[0] === 'number' || obj instanceof L.Point) {
+ obj = L.point(obj);
+ } else {
+ obj = L.bounds(obj);
+ }
+
+ if (obj instanceof L.Bounds) {
+ min = obj.min;
+ max = obj.max;
+ } else {
+ min = max = obj;
+ }
+
+ return (min.x >= this.min.x) &&
+ (max.x <= this.max.x) &&
+ (min.y >= this.min.y) &&
+ (max.y <= this.max.y);
+ },
+
+ intersects: function (bounds) { // (Bounds) -> Boolean
+ bounds = L.bounds(bounds);
+
+ var min = this.min,
+ max = this.max,
+ min2 = bounds.min,
+ max2 = bounds.max,
+ xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
+ yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
+
+ return xIntersects && yIntersects;
+ },
+
+ isValid: function () {
+ return !!(this.min && this.max);
+ }
+};
+
+L.bounds = function (a, b) { // (Bounds) or (Point, Point) or (Point[])
+ if (!a || a instanceof L.Bounds) {
+ return a;
+ }
+ return new L.Bounds(a, b);
+};
+
+
+/*
+ * L.Transformation is an utility class to perform simple point transformations through a 2d-matrix.
+ */
+
+L.Transformation = function (a, b, c, d) {
+ this._a = a;
+ this._b = b;
+ this._c = c;
+ this._d = d;
+};
+
+L.Transformation.prototype = {
+ transform: function (point, scale) { // (Point, Number) -> Point
+ return this._transform(point.clone(), scale);
+ },
+
+ // destructive transform (faster)
+ _transform: function (point, scale) {
+ scale = scale || 1;
+ point.x = scale * (this._a * point.x + this._b);
+ point.y = scale * (this._c * point.y + this._d);
+ return point;
+ },
+
+ untransform: function (point, scale) {
+ scale = scale || 1;
+ return new L.Point(
+ (point.x / scale - this._b) / this._a,
+ (point.y / scale - this._d) / this._c);
+ }
+};
+
+
+/*
+ * L.DomUtil contains various utility functions for working with DOM.
+ */
+
+L.DomUtil = {
+ get: function (id) {
+ return (typeof id === 'string' ? document.getElementById(id) : id);
+ },
+
+ getStyle: function (el, style) {
+
+ var value = el.style[style];
+
+ if (!value && el.currentStyle) {
+ value = el.currentStyle[style];
+ }
+
+ if ((!value || value === 'auto') && document.defaultView) {
+ var css = document.defaultView.getComputedStyle(el, null);
+ value = css ? css[style] : null;
+ }
+
+ return value === 'auto' ? null : value;
+ },
+
+ getViewportOffset: function (element) {
+
+ var top = 0,
+ left = 0,
+ el = element,
+ docBody = document.body,
+ pos,
+ ie7 = L.Browser.ie7;
+
+ do {
+ top += el.offsetTop || 0;
+ left += el.offsetLeft || 0;
+
+ //add borders
+ top += parseInt(L.DomUtil.getStyle(el, "borderTopWidth"), 10) || 0;
+ left += parseInt(L.DomUtil.getStyle(el, "borderLeftWidth"), 10) || 0;
+
+ pos = L.DomUtil.getStyle(el, 'position');
+
+ if (el.offsetParent === docBody && pos === 'absolute') { break; }
+
+ if (pos === 'fixed') {
+ top += docBody.scrollTop || 0;
+ left += docBody.scrollLeft || 0;
+ break;
+ }
+ el = el.offsetParent;
+
+ } while (el);
+
+ el = element;
+
+ do {
+ if (el === docBody) { break; }
+
+ top -= el.scrollTop || 0;
+ left -= el.scrollLeft || 0;
+
+ // webkit (and ie <= 7) handles RTL scrollLeft different to everyone else
+ // https://code.google.com/p/closure-library/source/browse/trunk/closure/goog/…
+ if (!L.DomUtil.documentIsLtr() && (L.Browser.webkit || ie7)) {
+ left += el.scrollWidth - el.clientWidth;
+
+ // ie7 shows the scrollbar by default and provides clientWidth counting it, so we
+ // need to add it back in if it is visible; scrollbar is on the left as we are RTL
+ if (ie7 && L.DomUtil.getStyle(el, 'overflow-y') !== 'hidden' &&
+ L.DomUtil.getStyle(el, 'overflow') !== 'hidden') {
+ left += 17;
+ }
+ }
+
+ el = el.parentNode;
+ } while (el);
+
+ return new L.Point(left, top);
+ },
+
+ documentIsLtr: function () {
+ if (!L.DomUtil._docIsLtrCached) {
+ L.DomUtil._docIsLtrCached = true;
+ L.DomUtil._docIsLtr = L.DomUtil.getStyle(document.body, 'direction') === "ltr";
+ }
+ return L.DomUtil._docIsLtr;
+ },
+
+ create: function (tagName, className, container) {
+
+ var el = document.createElement(tagName);
+ el.className = className;
+
+ if (container) {
+ container.appendChild(el);
+ }
+
+ return el;
+ },
+
+ disableTextSelection: function () {
+ if (document.selection && document.selection.empty) {
+ document.selection.empty();
+ }
+ if (!this._onselectstart) {
+ this._onselectstart = document.onselectstart || null;
+ document.onselectstart = L.Util.falseFn;
+ }
+ },
+
+ enableTextSelection: function () {
+ if (document.onselectstart === L.Util.falseFn) {
+ document.onselectstart = this._onselectstart;
+ this._onselectstart = null;
+ }
+ },
+
+ hasClass: function (el, name) {
+ return (el.className.length > 0) &&
+ new RegExp("(^|\\s)" + name + "(\\s|$)").test(el.className);
+ },
+
+ addClass: function (el, name) {
+ if (!L.DomUtil.hasClass(el, name)) {
+ el.className += (el.className ? ' ' : '') + name;
+ }
+ },
+
+ removeClass: function (el, name) {
+
+ function replaceFn(w, match) {
+ if (match === name) { return ''; }
+ return w;
+ }
+
+ el.className = el.className
+ .replace(/(\S+)\s*/g, replaceFn)
+ .replace(/(^\s+|\s+$)/, '');
+ },
+
+ setOpacity: function (el, value) {
+
+ if ('opacity' in el.style) {
+ el.style.opacity = value;
+
+ } else if ('filter' in el.style) {
+
+ var filter = false,
+ filterName = 'DXImageTransform.Microsoft.Alpha';
+
+ // filters collection throws an error if we try to retrieve a filter that doesn't exist
+ try { filter = el.filters.item(filterName); } catch (e) {}
+
+ value = Math.round(value * 100);
+
+ if (filter) {
+ filter.Enabled = (value !== 100);
+ filter.Opacity = value;
+ } else {
+ el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
+ }
+ }
+ },
+
+ testProp: function (props) {
+
+ var style = document.documentElement.style;
+
+ for (var i = 0; i < props.length; i++) {
+ if (props[i] in style) {
+ return props[i];
+ }
+ }
+ return false;
+ },
+
+ getTranslateString: function (point) {
+ // on WebKit browsers (Chrome/Safari/iOS Safari/Android) using translate3d instead of translate
+ // makes animation smoother as it ensures HW accel is used. Firefox 13 doesn't care
+ // (same speed either way), Opera 12 doesn't support translate3d
+
+ var is3d = L.Browser.webkit3d,
+ open = 'translate' + (is3d ? '3d' : '') + '(',
+ close = (is3d ? ',0' : '') + ')';
+
+ return open + point.x + 'px,' + point.y + 'px' + close;
+ },
+
+ getScaleString: function (scale, origin) {
+
+ var preTranslateStr = L.DomUtil.getTranslateString(origin.add(origin.multiplyBy(-1 * scale))),
+ scaleStr = ' scale(' + scale + ') ';
+
+ return preTranslateStr + scaleStr;
+ },
+
+ setPosition: function (el, point, disable3D) { // (HTMLElement, Point[, Boolean])
+
+ el._leaflet_pos = point;
+
+ if (!disable3D && L.Browser.any3d) {
+ el.style[L.DomUtil.TRANSFORM] = L.DomUtil.getTranslateString(point);
+
+ // workaround for Android 2/3 stability (https://github.com/CloudMade/Leaflet/issues/69)
+ if (L.Browser.mobileWebkit3d) {
+ el.style.WebkitBackfaceVisibility = 'hidden';
+ }
+ } else {
+ el.style.left = point.x + 'px';
+ el.style.top = point.y + 'px';
+ }
+ },
+
+ getPosition: function (el) {
+ // this method is only used for elements previously positioned using setPosition,
+ // so it's safe to cache the position for performance
+ return el._leaflet_pos;
+ }
+};
+
+
+// prefix style property names
+
+L.DomUtil.TRANSFORM = L.DomUtil.testProp(
+ ['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
+
+// webkitTransition comes first because some browser versions that drop vendor prefix don't do
+// the same for the transitionend event, in particular the Android 4.1 stock browser
+
+L.DomUtil.TRANSITION = L.DomUtil.testProp(
+ ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
+
+L.DomUtil.TRANSITION_END =
+ L.DomUtil.TRANSITION === 'webkitTransition' || L.DomUtil.TRANSITION === 'OTransition' ?
+ L.DomUtil.TRANSITION + 'End' : 'transitionend';
+
+
+/*
+ * L.LatLng represents a geographical point with latitude and longitude coordinates.
+ */
+
+L.LatLng = function (rawLat, rawLng) { // (Number, Number)
+ var lat = parseFloat(rawLat),
+ lng = parseFloat(rawLng);
+
+ if (isNaN(lat) || isNaN(lng)) {
+ throw new Error('Invalid LatLng object: (' + rawLat + ', ' + rawLng + ')');
+ }
+
+ this.lat = lat;
+ this.lng = lng;
+};
+
+L.extend(L.LatLng, {
+ DEG_TO_RAD: Math.PI / 180,
+ RAD_TO_DEG: 180 / Math.PI,
+ MAX_MARGIN: 1.0E-9 // max margin of error for the "equals" check
+});
+
+L.LatLng.prototype = {
+ equals: function (obj) { // (LatLng) -> Boolean
+ if (!obj) { return false; }
+
+ obj = L.latLng(obj);
+
+ var margin = Math.max(
+ Math.abs(this.lat - obj.lat),
+ Math.abs(this.lng - obj.lng));
+
+ return margin <= L.LatLng.MAX_MARGIN;
+ },
+
+ toString: function (precision) { // (Number) -> String
+ return 'LatLng(' +
+ L.Util.formatNum(this.lat, precision) + ', ' +
+ L.Util.formatNum(this.lng, precision) + ')';
+ },
+
+ // Haversine distance formula, see http://en.wikipedia.org/wiki/Haversine_formula
+ // TODO move to projection code, LatLng shouldn't know about Earth
+ distanceTo: function (other) { // (LatLng) -> Number
+ other = L.latLng(other);
+
+ var R = 6378137, // earth radius in meters
+ d2r = L.LatLng.DEG_TO_RAD,
+ dLat = (other.lat - this.lat) * d2r,
+ dLon = (other.lng - this.lng) * d2r,
+ lat1 = this.lat * d2r,
+ lat2 = other.lat * d2r,
+ sin1 = Math.sin(dLat / 2),
+ sin2 = Math.sin(dLon / 2);
+
+ var a = sin1 * sin1 + sin2 * sin2 * Math.cos(lat1) * Math.cos(lat2);
+
+ return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
+ },
+
+ wrap: function (a, b) { // (Number, Number) -> LatLng
+ var lng = this.lng;
+
+ a = a || -180;
+ b = b || 180;
+
+ lng = (lng + b) % (b - a) + (lng < a || lng === b ? b : a);
+
+ return new L.LatLng(this.lat, lng);
+ }
+};
+
+L.latLng = function (a, b) { // (LatLng) or ([Number, Number]) or (Number, Number)
+ if (a instanceof L.LatLng) {
+ return a;
+ }
+ if (L.Util.isArray(a)) {
+ return new L.LatLng(a[0], a[1]);
+ }
+ if (isNaN(a)) {
+ return a;
+ }
+ return new L.LatLng(a, b);
+};
+
+
+
+/*
+ * L.LatLngBounds represents a rectangular area on the map in geographical coordinates.
+ */
+
+L.LatLngBounds = function (southWest, northEast) { // (LatLng, LatLng) or (LatLng[])
+ if (!southWest) { return; }
+
+ var latlngs = northEast ? [southWest, northEast] : southWest;
+
+ for (var i = 0, len = latlngs.length; i < len; i++) {
+ this.extend(latlngs[i]);
+ }
+};
+
+L.LatLngBounds.prototype = {
+ // extend the bounds to contain the given point or bounds
+ extend: function (obj) { // (LatLng) or (LatLngBounds)
+ if (typeof obj[0] === 'number' || typeof obj[0] === 'string' || obj instanceof L.LatLng) {
+ obj = L.latLng(obj);
+ } else {
+ obj = L.latLngBounds(obj);
+ }
+
+ if (obj instanceof L.LatLng) {
+ if (!this._southWest && !this._northEast) {
+ this._southWest = new L.LatLng(obj.lat, obj.lng);
+ this._northEast = new L.LatLng(obj.lat, obj.lng);
+ } else {
+ this._southWest.lat = Math.min(obj.lat, this._southWest.lat);
+ this._southWest.lng = Math.min(obj.lng, this._southWest.lng);
+
+ this._northEast.lat = Math.max(obj.lat, this._northEast.lat);
+ this._northEast.lng = Math.max(obj.lng, this._northEast.lng);
+ }
+ } else if (obj instanceof L.LatLngBounds) {
+ this.extend(obj._southWest);
+ this.extend(obj._northEast);
+ }
+ return this;
+ },
+
+ // extend the bounds by a percentage
+ pad: function (bufferRatio) { // (Number) -> LatLngBounds
+ var sw = this._southWest,
+ ne = this._northEast,
+ heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio,
+ widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
+
+ return new L.LatLngBounds(
+ new L.LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
+ new L.LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
+ },
+
+ getCenter: function () { // -> LatLng
+ return new L.LatLng(
+ (this._southWest.lat + this._northEast.lat) / 2,
+ (this._southWest.lng + this._northEast.lng) / 2);
+ },
+
+ getSouthWest: function () {
+ return this._southWest;
+ },
+
+ getNorthEast: function () {
+ return this._northEast;
+ },
+
+ getNorthWest: function () {
+ return new L.LatLng(this._northEast.lat, this._southWest.lng);
+ },
+
+ getSouthEast: function () {
+ return new L.LatLng(this._southWest.lat, this._northEast.lng);
+ },
+
+ contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
+ if (typeof obj[0] === 'number' || obj instanceof L.LatLng) {
+ obj = L.latLng(obj);
+ } else {
+ obj = L.latLngBounds(obj);
+ }
+
+ var sw = this._southWest,
+ ne = this._northEast,
+ sw2, ne2;
+
+ if (obj instanceof L.LatLngBounds) {
+ sw2 = obj.getSouthWest();
+ ne2 = obj.getNorthEast();
+ } else {
+ sw2 = ne2 = obj;
+ }
+
+ return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
+ (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
+ },
+
+ intersects: function (bounds) { // (LatLngBounds)
+ bounds = L.latLngBounds(bounds);
+
+ var sw = this._southWest,
+ ne = this._northEast,
+ sw2 = bounds.getSouthWest(),
+ ne2 = bounds.getNorthEast(),
+
+ latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat),
+ lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng);
+
+ return latIntersects && lngIntersects;
+ },
+
+ toBBoxString: function () {
+ var sw = this._southWest,
+ ne = this._northEast;
+
+ return [sw.lng, sw.lat, ne.lng, ne.lat].join(',');
+ },
+
+ equals: function (bounds) { // (LatLngBounds)
+ if (!bounds) { return false; }
+
+ bounds = L.latLngBounds(bounds);
+
+ return this._southWest.equals(bounds.getSouthWest()) &&
+ this._northEast.equals(bounds.getNorthEast());
+ },
+
+ isValid: function () {
+ return !!(this._southWest && this._northEast);
+ }
+};
+
+//TODO International date line?
+
+L.latLngBounds = function (a, b) { // (LatLngBounds) or (LatLng, LatLng)
+ if (!a || a instanceof L.LatLngBounds) {
+ return a;
+ }
+ return new L.LatLngBounds(a, b);
+};
+
+
+/*
+ * L.Projection contains various geographical projections used by CRS classes.
+ */
+
+L.Projection = {};
+
+
+/*
+ * Spherical Mercator is the most popular map projection, used by EPSG:3857 CRS used by default.
+ */
+
+L.Projection.SphericalMercator = {
+ MAX_LATITUDE: 85.0511287798,
+
+ project: function (latlng) { // (LatLng) -> Point
+ var d = L.LatLng.DEG_TO_RAD,
+ max = this.MAX_LATITUDE,
+ lat = Math.max(Math.min(max, latlng.lat), -max),
+ x = latlng.lng * d,
+ y = lat * d;
+
+ y = Math.log(Math.tan((Math.PI / 4) + (y / 2)));
+
+ return new L.Point(x, y);
+ },
+
+ unproject: function (point) { // (Point, Boolean) -> LatLng
+ var d = L.LatLng.RAD_TO_DEG,
+ lng = point.x * d,
+ lat = (2 * Math.atan(Math.exp(point.y)) - (Math.PI / 2)) * d;
+
+ return new L.LatLng(lat, lng);
+ }
+};
+
+
+/*
+ * Simple equirectangular (Plate Carree) projection, used by CRS like EPSG:4326 and Simple.
+ */
+
+L.Projection.LonLat = {
+ project: function (latlng) {
+ return new L.Point(latlng.lng, latlng.lat);
+ },
+
+ unproject: function (point) {
+ return new L.LatLng(point.y, point.x);
+ }
+};
+
+
+/*
+ * L.CRS is a base object for all defined CRS (Coordinate Reference Systems) in Leaflet.
+ */
+
+L.CRS = {
+ latLngToPoint: function (latlng, zoom) { // (LatLng, Number) -> Point
+ var projectedPoint = this.projection.project(latlng),
+ scale = this.scale(zoom);
+
+ return this.transformation._transform(projectedPoint, scale);
+ },
+
+ pointToLatLng: function (point, zoom) { // (Point, Number[, Boolean]) -> LatLng
+ var scale = this.scale(zoom),
+ untransformedPoint = this.transformation.untransform(point, scale);
+
+ return this.projection.unproject(untransformedPoint);
+ },
+
+ project: function (latlng) {
+ return this.projection.project(latlng);
+ },
+
+ scale: function (zoom) {
+ return 256 * Math.pow(2, zoom);
+ }
+};
+
+
+/*
+ * A simple CRS that can be used for flat non-Earth maps like panoramas or game maps.
+ */
+
+L.CRS.Simple = L.extend({}, L.CRS, {
+ projection: L.Projection.LonLat,
+ transformation: new L.Transformation(1, 0, -1, 0),
+
+ scale: function (zoom) {
+ return Math.pow(2, zoom);
+ }
+});
+
+
+/*
+ * L.CRS.EPSG3857 (Spherical Mercator) is the most common CRS for web mapping
+ * and is used by Leaflet by default.
+ */
+
+L.CRS.EPSG3857 = L.extend({}, L.CRS, {
+ code: 'EPSG:3857',
+
+ projection: L.Projection.SphericalMercator,
+ transformation: new L.Transformation(0.5 / Math.PI, 0.5, -0.5 / Math.PI, 0.5),
+
+ project: function (latlng) { // (LatLng) -> Point
+ var projectedPoint = this.projection.project(latlng),
+ earthRadius = 6378137;
+ return projectedPoint.multiplyBy(earthRadius);
+ }
+});
+
+L.CRS.EPSG900913 = L.extend({}, L.CRS.EPSG3857, {
+ code: 'EPSG:900913'
+});
+
+
+/*
+ * L.CRS.EPSG4326 is a CRS popular among advanced GIS specialists.
+ */
+
+L.CRS.EPSG4326 = L.extend({}, L.CRS, {
+ code: 'EPSG:4326',
+
+ projection: L.Projection.LonLat,
+ transformation: new L.Transformation(1 / 360, 0.5, -1 / 360, 0.5)
+});
+
+
+/*
+ * L.Map is the central class of the API - it is used to create a map.
+ */
+
+L.Map = L.Class.extend({
+
+ includes: L.Mixin.Events,
+
+ options: {
+ crs: L.CRS.EPSG3857,
+
+ /*
+ center: LatLng,
+ zoom: Number,
+ layers: Array,
+ */
+
+ fadeAnimation: L.DomUtil.TRANSITION && !L.Browser.android23,
+ trackResize: true,
+ markerZoomAnimation: L.DomUtil.TRANSITION && L.Browser.any3d
+ },
+
+ initialize: function (id, options) { // (HTMLElement or String, Object)
+ options = L.setOptions(this, options);
+
+ this._initContainer(id);
+ this._initLayout();
+ this.callInitHooks();
+ this._initEvents();
+
+ if (options.maxBounds) {
+ this.setMaxBounds(options.maxBounds);
+ }
+
+ if (options.center && options.zoom !== undefined) {
+ this.setView(L.latLng(options.center), options.zoom, true);
+ }
+
+ this._initLayers(options.layers);
+ },
+
+
+ // public methods that modify map state
+
+ // replaced by animation-powered implementation in Map.PanAnimation.js
+ setView: function (center, zoom) {
+ this._resetView(L.latLng(center), this._limitZoom(zoom));
+ return this;
+ },
+
+ setZoom: function (zoom) { // (Number)
+ return this.setView(this.getCenter(), zoom);
+ },
+
+ zoomIn: function (delta) {
+ return this.setZoom(this._zoom + (delta || 1));
+ },
+
+ zoomOut: function (delta) {
+ return this.setZoom(this._zoom - (delta || 1));
+ },
+
+ fitBounds: function (bounds) { // (LatLngBounds)
+ var zoom = this.getBoundsZoom(bounds);
+ return this.setView(L.latLngBounds(bounds).getCenter(), zoom);
+ },
+
+ fitWorld: function () {
+ var sw = new L.LatLng(-60, -170),
+ ne = new L.LatLng(85, 179);
+
+ return this.fitBounds(new L.LatLngBounds(sw, ne));
+ },
+
+ panTo: function (center) { // (LatLng)
+ return this.setView(center, this._zoom);
+ },
+
+ panBy: function (offset) { // (Point)
+ // replaced with animated panBy in Map.Animation.js
+ this.fire('movestart');
+
+ this._rawPanBy(L.point(offset));
+
+ this.fire('move');
+ return this.fire('moveend');
+ },
+
+ setMaxBounds: function (bounds) {
+ bounds = L.latLngBounds(bounds);
+
+ this.options.maxBounds = bounds;
+
+ if (!bounds) {
+ this._boundsMinZoom = null;
+ return this;
+ }
+
+ var minZoom = this.getBoundsZoom(bounds, true);
+
+ this._boundsMinZoom = minZoom;
+
+ if (this._loaded) {
+ if (this._zoom < minZoom) {
+ this.setView(bounds.getCenter(), minZoom);
+ } else {
+ this.panInsideBounds(bounds);
+ }
+ }
+
+ return this;
+ },
+
+ panInsideBounds: function (bounds) {
+ bounds = L.latLngBounds(bounds);
+
+ var viewBounds = this.getBounds(),
+ viewSw = this.project(viewBounds.getSouthWest()),
+ viewNe = this.project(viewBounds.getNorthEast()),
+ sw = this.project(bounds.getSouthWest()),
+ ne = this.project(bounds.getNorthEast()),
+ dx = 0,
+ dy = 0;
+
+ if (viewNe.y < ne.y) { // north
+ dy = ne.y - viewNe.y;
+ }
+ if (viewNe.x > ne.x) { // east
+ dx = ne.x - viewNe.x;
+ }
+ if (viewSw.y > sw.y) { // south
+ dy = sw.y - viewSw.y;
+ }
+ if (viewSw.x < sw.x) { // west
+ dx = sw.x - viewSw.x;
+ }
+
+ return this.panBy(new L.Point(dx, dy, true));
+ },
+
+ addLayer: function (layer) {
+ // TODO method is too big, refactor
+
+ var id = L.stamp(layer);
+
+ if (this._layers[id]) { return this; }
+
+ this._layers[id] = layer;
+
+ // TODO getMaxZoom, getMinZoom in ILayer (instead of options)
+ if (layer.options && (!isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom))) {
+ this._zoomBoundLayers[id] = layer;
+ this._updateZoomLevels();
+ }
+
+ // TODO looks ugly, refactor!!!
+ if (this.options.zoomAnimation && L.TileLayer && (layer instanceof L.TileLayer)) {
+ this._tileLayersNum++;
+ this._tileLayersToLoad++;
+ layer.on('load', this._onTileLayerLoad, this);
+ }
+
+ this.whenReady(function () {
+ layer.onAdd(this);
+ this.fire('layeradd', {layer: layer});
+ }, this);
+
+ return this;
+ },
+
+ removeLayer: function (layer) {
+ var id = L.stamp(layer);
+
+ if (!this._layers[id]) { return; }
+
+ layer.onRemove(this);
+
+ delete this._layers[id];
+ if (this._zoomBoundLayers[id]) {
+ delete this._zoomBoundLayers[id];
+ this._updateZoomLevels();
+ }
+
+ // TODO looks ugly, refactor
+ if (this.options.zoomAnimation && L.TileLayer && (layer instanceof L.TileLayer)) {
+ this._tileLayersNum--;
+ this._tileLayersToLoad--;
+ layer.off('load', this._onTileLayerLoad, this);
+ }
+
+ return this.fire('layerremove', {layer: layer});
+ },
+
+ hasLayer: function (layer) {
+ var id = L.stamp(layer);
+ return this._layers.hasOwnProperty(id);
+ },
+
+ invalidateSize: function (animate) {
+ var oldSize = this.getSize();
+
+ this._sizeChanged = true;
+
+ if (this.options.maxBounds) {
+ this.setMaxBounds(this.options.maxBounds);
+ }
+
+ if (!this._loaded) { return this; }
+
+ var offset = oldSize._subtract(this.getSize())._divideBy(2)._round();
+
+ if (animate === true) {
+ this.panBy(offset);
+ } else {
+ this._rawPanBy(offset);
+
+ this.fire('move');
+
+ clearTimeout(this._sizeTimer);
+ this._sizeTimer = setTimeout(L.bind(this.fire, this, 'moveend'), 200);
+ }
+ return this;
+ },
+
+ // TODO handler.addTo
+ addHandler: function (name, HandlerClass) {
+ if (!HandlerClass) { return; }
+
+ this[name] = new HandlerClass(this);
+
+ if (this.options[name]) {
+ this[name].enable();
+ }
+
+ return this;
+ },
+
+
+ // public methods for getting map state
+
+ getCenter: function () { // (Boolean) -> LatLng
+ return this.layerPointToLatLng(this._getCenterLayerPoint());
+ },
+
+ getZoom: function () {
+ return this._zoom;
+ },
+
+ getBounds: function () {
+ var bounds = this.getPixelBounds(),
+ sw = this.unproject(bounds.getBottomLeft()),
+ ne = this.unproject(bounds.getTopRight());
+
+ return new L.LatLngBounds(sw, ne);
+ },
+
+ getMinZoom: function () {
+ var z1 = this.options.minZoom || 0,
+ z2 = this._layersMinZoom || 0,
+ z3 = this._boundsMinZoom || 0;
+
+ return Math.max(z1, z2, z3);
+ },
+
+ getMaxZoom: function () {
+ var z1 = this.options.maxZoom === undefined ? Infinity : this.options.maxZoom,
+ z2 = this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom;
+
+ return Math.min(z1, z2);
+ },
+
+ getBoundsZoom: function (bounds, inside) { // (LatLngBounds, Boolean) -> Number
+ bounds = L.latLngBounds(bounds);
+
+ var size = this.getSize(),
+ zoom = this.options.minZoom || 0,
+ maxZoom = this.getMaxZoom(),
+ ne = bounds.getNorthEast(),
+ sw = bounds.getSouthWest(),
+ boundsSize,
+ nePoint,
+ swPoint,
+ zoomNotFound = true;
+
+ if (inside) {
+ zoom--;
+ }
+
+ do {
+ zoom++;
+ nePoint = this.project(ne, zoom);
+ swPoint = this.project(sw, zoom);
+
+ boundsSize = new L.Point(
+ Math.abs(nePoint.x - swPoint.x),
+ Math.abs(swPoint.y - nePoint.y));
+
+ if (!inside) {
+ zoomNotFound = boundsSize.x <= size.x && boundsSize.y <= size.y;
+ } else {
+ zoomNotFound = boundsSize.x < size.x || boundsSize.y < size.y;
+ }
+ } while (zoomNotFound && zoom <= maxZoom);
+
+ if (zoomNotFound && inside) {
+ return null;
+ }
+
+ return inside ? zoom : zoom - 1;
+ },
+
+ getSize: function () {
+ if (!this._size || this._sizeChanged) {
+ this._size = new L.Point(
+ this._container.clientWidth,
+ this._container.clientHeight);
+
+ this._sizeChanged = false;
+ }
+ return this._size.clone();
+ },
+
+ getPixelBounds: function () {
+ var topLeftPoint = this._getTopLeftPoint();
+ return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
+ },
+
+ getPixelOrigin: function () {
+ return this._initialTopLeftPoint;
+ },
+
+ getPanes: function () {
+ return this._panes;
+ },
+
+ getContainer: function () {
+ return this._container;
+ },
+
+
+ // TODO replace with universal implementation after refactoring projections
+
+ getZoomScale: function (toZoom) {
+ var crs = this.options.crs;
+ return crs.scale(toZoom) / crs.scale(this._zoom);
+ },
+
+ getScaleZoom: function (scale) {
+ return this._zoom + (Math.log(scale) / Math.LN2);
+ },
+
+
+ // conversion methods
+
+ project: function (latlng, zoom) { // (LatLng[, Number]) -> Point
+ zoom = zoom === undefined ? this._zoom : zoom;
+ return this.options.crs.latLngToPoint(L.latLng(latlng), zoom);
+ },
+
+ unproject: function (point, zoom) { // (Point[, Number]) -> LatLng
+ zoom = zoom === undefined ? this._zoom : zoom;
+ return this.options.crs.pointToLatLng(L.point(point), zoom);
+ },
+
+ layerPointToLatLng: function (point) { // (Point)
+ var projectedPoint = L.point(point).add(this._initialTopLeftPoint);
+ return this.unproject(projectedPoint);
+ },
+
+ latLngToLayerPoint: function (latlng) { // (LatLng)
+ var projectedPoint = this.project(L.latLng(latlng))._round();
+ return projectedPoint._subtract(this._initialTopLeftPoint);
+ },
+
+ containerPointToLayerPoint: function (point) { // (Point)
+ return L.point(point).subtract(this._getMapPanePos());
+ },
+
+ layerPointToContainerPoint: function (point) { // (Point)
+ return L.point(point).add(this._getMapPanePos());
+ },
+
+ containerPointToLatLng: function (point) {
+ var layerPoint = this.containerPointToLayerPoint(L.point(point));
+ return this.layerPointToLatLng(layerPoint);
+ },
+
+ latLngToContainerPoint: function (latlng) {
+ return this.layerPointToContainerPoint(this.latLngToLayerPoint(L.latLng(latlng)));
+ },
+
+ mouseEventToContainerPoint: function (e) { // (MouseEvent)
+ return L.DomEvent.getMousePosition(e, this._container);
+ },
+
+ mouseEventToLayerPoint: function (e) { // (MouseEvent)
+ return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
+ },
+
+ mouseEventToLatLng: function (e) { // (MouseEvent)
+ return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
+ },
+
+
+ // map initialization methods
+
+ _initContainer: function (id) {
+ var container = this._container = L.DomUtil.get(id);
+
+ if (container._leaflet) {
+ throw new Error("Map container is already initialized.");
+ }
+
+ container._leaflet = true;
+ },
+
+ _initLayout: function () {
+ var container = this._container;
+
+ L.DomUtil.addClass(container, 'leaflet-container');
+
+ if (L.Browser.touch) {
+ L.DomUtil.addClass(container, 'leaflet-touch');
+ }
+
+ if (this.options.fadeAnimation) {
+ L.DomUtil.addClass(container, 'leaflet-fade-anim');
+ }
+
+ var position = L.DomUtil.getStyle(container, 'position');
+
+ if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') {
+ container.style.position = 'relative';
+ }
+
+ this._initPanes();
+
+ if (this._initControlPos) {
+ this._initControlPos();
+ }
+ },
+
+ _initPanes: function () {
+ var panes = this._panes = {};
+
+ this._mapPane = panes.mapPane = this._createPane('leaflet-map-pane', this._container);
+
+ this._tilePane = panes.tilePane = this._createPane('leaflet-tile-pane', this._mapPane);
+ panes.objectsPane = this._createPane('leaflet-objects-pane', this._mapPane);
+ panes.shadowPane = this._createPane('leaflet-shadow-pane');
+ panes.overlayPane = this._createPane('leaflet-overlay-pane');
+ panes.markerPane = this._createPane('leaflet-marker-pane');
+ panes.popupPane = this._createPane('leaflet-popup-pane');
+
+ var zoomHide = ' leaflet-zoom-hide';
+
+ if (!this.options.markerZoomAnimation) {
+ L.DomUtil.addClass(panes.markerPane, zoomHide);
+ L.DomUtil.addClass(panes.shadowPane, zoomHide);
+ L.DomUtil.addClass(panes.popupPane, zoomHide);
+ }
+ },
+
+ _createPane: function (className, container) {
+ return L.DomUtil.create('div', className, container || this._panes.objectsPane);
+ },
+
+ _initLayers: function (layers) {
+ layers = layers ? (L.Util.isArray(layers) ? layers : [layers]) : [];
+
+ this._layers = {};
+ this._zoomBoundLayers = {};
+ this._tileLayersNum = 0;
+
+ var i, len;
+
+ for (i = 0, len = layers.length; i < len; i++) {
+ this.addLayer(layers[i]);
+ }
+ },
+
+
+ // private methods that modify map state
+
+ _resetView: function (center, zoom, preserveMapOffset, afterZoomAnim) {
+
+ var zoomChanged = (this._zoom !== zoom);
+
+ if (!afterZoomAnim) {
+ this.fire('movestart');
+
+ if (zoomChanged) {
+ this.fire('zoomstart');
+ }
+ }
+
+ this._zoom = zoom;
+
+ this._initialTopLeftPoint = this._getNewTopLeftPoint(center);
+
+ if (!preserveMapOffset) {
+ L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
+ } else {
+ this._initialTopLeftPoint._add(this._getMapPanePos());
+ }
+
+ this._tileLayersToLoad = this._tileLayersNum;
+
+ var loading = !this._loaded;
+ this._loaded = true;
+
+ this.fire('viewreset', {hard: !preserveMapOffset});
+
+ this.fire('move');
+
+ if (zoomChanged || afterZoomAnim) {
+ this.fire('zoomend');
+ }
+
+ this.fire('moveend', {hard: !preserveMapOffset});
+
+ if (loading) {
+ this.fire('load');
+ }
+ },
+
+ _rawPanBy: function (offset) {
+ L.DomUtil.setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
+ },
+
+ _updateZoomLevels: function () {
+ var i,
+ minZoom = Infinity,
+ maxZoom = -Infinity;
+
+ for (i in this._zoomBoundLayers) {
+ if (this._zoomBoundLayers.hasOwnProperty(i)) {
+ var layer = this._zoomBoundLayers[i];
+ if (!isNaN(layer.options.minZoom)) {
+ minZoom = Math.min(minZoom, layer.options.minZoom);
+ }
+ if (!isNaN(layer.options.maxZoom)) {
+ maxZoom = Math.max(maxZoom, layer.options.maxZoom);
+ }
+ }
+ }
+
+ if (i === undefined) { // we have no tilelayers
+ this._layersMaxZoom = this._layersMinZoom = undefined;
+ } else {
+ this._layersMaxZoom = maxZoom;
+ this._layersMinZoom = minZoom;
+ }
+ },
+
+ // map events
+
+ _initEvents: function () {
+ if (!L.DomEvent) { return; }
+
+ L.DomEvent.on(this._container, 'click', this._onMouseClick, this);
+
+ var events = ['dblclick', 'mousedown', 'mouseup', 'mouseenter',
+ 'mouseleave', 'mousemove', 'contextmenu'],
+ i, len;
+
+ for (i = 0, len = events.length; i < len; i++) {
+ L.DomEvent.on(this._container, events[i], this._fireMouseEvent, this);
+ }
+
+ if (this.options.trackResize) {
+ L.DomEvent.on(window, 'resize', this._onResize, this);
+ }
+ },
+
+ _onResize: function () {
+ L.Util.cancelAnimFrame(this._resizeRequest);
+ this._resizeRequest = L.Util.requestAnimFrame(
+ this.invalidateSize, this, false, this._container);
+ },
+
+ _onMouseClick: function (e) {
+ if (!this._loaded || (this.dragging && this.dragging.moved())) { return; }
+
+ this.fire('preclick');
+ this._fireMouseEvent(e);
+ },
+
+ _fireMouseEvent: function (e) {
+ if (!this._loaded) { return; }
+
+ var type = e.type;
+
+ type = (type === 'mouseenter' ? 'mouseover' : (type === 'mouseleave' ? 'mouseout' : type));
+
+ if (!this.hasEventListeners(type)) { return; }
+
+ if (type === 'contextmenu') {
+ L.DomEvent.preventDefault(e);
+ }
+
+ var containerPoint = this.mouseEventToContainerPoint(e),
+ layerPoint = this.containerPointToLayerPoint(containerPoint),
+ latlng = this.layerPointToLatLng(layerPoint);
+
+ this.fire(type, {
+ latlng: latlng,
+ layerPoint: layerPoint,
+ containerPoint: containerPoint,
+ originalEvent: e
+ });
+ },
+
+ _onTileLayerLoad: function () {
+ // TODO super-ugly, refactor!!!
+ // clear scaled tiles after all new tiles are loaded (for performance)
+ this._tileLayersToLoad--;
+ if (this._tileLayersNum && !this._tileLayersToLoad && this._tileBg) {
+ clearTimeout(this._clearTileBgTimer);
+ this._clearTileBgTimer = setTimeout(L.bind(this._clearTileBg, this), 500);
+ }
+ },
+
+ whenReady: function (callback, context) {
+ if (this._loaded) {
+ callback.call(context || this, this);
+ } else {
+ this.on('load', callback, context);
+ }
+ return this;
+ },
+
+
+ // private methods for getting map state
+
+ _getMapPanePos: function () {
+ return L.DomUtil.getPosition(this._mapPane);
+ },
+
+ _getTopLeftPoint: function () {
+ if (!this._loaded) {
+ throw new Error('Set map center and zoom first.');
+ }
+
+ return this._initialTopLeftPoint.subtract(this._getMapPanePos());
+ },
+
+ _getNewTopLeftPoint: function (center, zoom) {
+ var viewHalf = this.getSize()._divideBy(2);
+ // TODO round on display, not calculation to increase precision?
+ return this.project(center, zoom)._subtract(viewHalf)._round();
+ },
+
+ _latLngToNewLayerPoint: function (latlng, newZoom, newCenter) {
+ var topLeft = this._getNewTopLeftPoint(newCenter, newZoom).add(this._getMapPanePos());
+ return this.project(latlng, newZoom)._subtract(topLeft);
+ },
+
+ _getCenterLayerPoint: function () {
+ return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
+ },
+
+ _getCenterOffset: function (center) {
+ return this.latLngToLayerPoint(center).subtract(this._getCenterLayerPoint());
+ },
+
+ _limitZoom: function (zoom) {
+ var min = this.getMinZoom(),
+ max = this.getMaxZoom();
+
+ return Math.max(min, Math.min(max, zoom));
+ }
+});
+
+L.map = function (id, options) {
+ return new L.Map(id, options);
+};
+
+
+/*
+ * Mercator projection that takes into account that the Earth is not a perfect sphere.
+ * Less popular than spherical mercator; used by projections like EPSG:3395.
+ */
+
+L.Projection.Mercator = {
+ MAX_LATITUDE: 85.0840591556,
+
+ R_MINOR: 6356752.3142,
+ R_MAJOR: 6378137,
+
+ project: function (latlng) { // (LatLng) -> Point
+ var d = L.LatLng.DEG_TO_RAD,
+ max = this.MAX_LATITUDE,
+ lat = Math.max(Math.min(max, latlng.lat), -max),
+ r = this.R_MAJOR,
+ r2 = this.R_MINOR,
+ x = latlng.lng * d * r,
+ y = lat * d,
+ tmp = r2 / r,
+ eccent = Math.sqrt(1.0 - tmp * tmp),
+ con = eccent * Math.sin(y);
+
+ con = Math.pow((1 - con) / (1 + con), eccent * 0.5);
+
+ var ts = Math.tan(0.5 * ((Math.PI * 0.5) - y)) / con;
+ y = -r2 * Math.log(ts);
+
+ return new L.Point(x, y);
+ },
+
+ unproject: function (point) { // (Point, Boolean) -> LatLng
+ var d = L.LatLng.RAD_TO_DEG,
+ r = this.R_MAJOR,
+ r2 = this.R_MINOR,
+ lng = point.x * d / r,
+ tmp = r2 / r,
+ eccent = Math.sqrt(1 - (tmp * tmp)),
+ ts = Math.exp(- point.y / r2),
+ phi = (Math.PI / 2) - 2 * Math.atan(ts),
+ numIter = 15,
+ tol = 1e-7,
+ i = numIter,
+ dphi = 0.1,
+ con;
+
+ while ((Math.abs(dphi) > tol) && (--i > 0)) {
+ con = eccent * Math.sin(phi);
+ dphi = (Math.PI / 2) - 2 * Math.atan(ts *
+ Math.pow((1.0 - con) / (1.0 + con), 0.5 * eccent)) - phi;
+ phi += dphi;
+ }
+
+ return new L.LatLng(phi * d, lng);
+ }
+};
+
+
+
+L.CRS.EPSG3395 = L.extend({}, L.CRS, {
+ code: 'EPSG:3395',
+
+ projection: L.Projection.Mercator,
+
+ transformation: (function () {
+ var m = L.Projection.Mercator,
+ r = m.R_MAJOR,
+ r2 = m.R_MINOR;
+
+ return new L.Transformation(0.5 / (Math.PI * r), 0.5, -0.5 / (Math.PI * r2), 0.5);
+ }())
+});
+
+
+/*
+ * L.TileLayer is used for standard xyz-numbered tile layers.
+ */
+
+L.TileLayer = L.Class.extend({
+ includes: L.Mixin.Events,
+
+ options: {
+ minZoom: 0,
+ maxZoom: 18,
+ tileSize: 256,
+ subdomains: 'abc',
+ errorTileUrl: '',
+ attribution: '',
+ zoomOffset: 0,
+ opacity: 1,
+ /* (undefined works too)
+ zIndex: null,
+ tms: false,
+ continuousWorld: false,
+ noWrap: false,
+ zoomReverse: false,
+ detectRetina: false,
+ reuseTiles: false,
+ */
+ unloadInvisibleTiles: L.Browser.mobile,
+ updateWhenIdle: L.Browser.mobile
+ },
+
+ initialize: function (url, options) {
+ options = L.setOptions(this, options);
+
+ // detecting retina displays, adjusting tileSize and zoom levels
+ if (options.detectRetina && L.Browser.retina && options.maxZoom > 0) {
+
+ options.tileSize = Math.floor(options.tileSize / 2);
+ options.zoomOffset++;
+
+ if (options.minZoom > 0) {
+ options.minZoom--;
+ }
+ this.options.maxZoom--;
+ }
+
+ this._url = url;
+
+ var subdomains = this.options.subdomains;
+
+ if (typeof subdomains === 'string') {
+ this.options.subdomains = subdomains.split('');
+ }
+ },
+
+ onAdd: function (map) {
+ this._map = map;
+
+ // create a container div for tiles
+ this._initContainer();
+
+ // create an image to clone for tiles
+ this._createTileProto();
+
+ // set up events
+ map.on({
+ 'viewreset': this._resetCallback,
+ 'moveend': this._update
+ }, this);
+
+ if (!this.options.updateWhenIdle) {
+ this._limitedUpdate = L.Util.limitExecByInterval(this._update, 150, this);
+ map.on('move', this._limitedUpdate, this);
+ }
+
+ this._reset();
+ this._update();
+ },
+
+ addTo: function (map) {
+ map.addLayer(this);
+ return this;
+ },
+
+ onRemove: function (map) {
+ this._container.parentNode.removeChild(this._container);
+
+ map.off({
+ 'viewreset': this._resetCallback,
+ 'moveend': this._update
+ }, this);
+
+ if (!this.options.updateWhenIdle) {
+ map.off('move', this._limitedUpdate, this);
+ }
+
+ this._container = null;
+ this._map = null;
+ },
+
+ bringToFront: function () {
+ var pane = this._map._panes.tilePane;
+
+ if (this._container) {
+ pane.appendChild(this._container);
+ this._setAutoZIndex(pane, Math.max);
+ }
+
+ return this;
+ },
+
+ bringToBack: function () {
+ var pane = this._map._panes.tilePane;
+
+ if (this._container) {
+ pane.insertBefore(this._container, pane.firstChild);
+ this._setAutoZIndex(pane, Math.min);
+ }
+
+ return this;
+ },
+
+ getAttribution: function () {
+ return this.options.attribution;
+ },
+
+ setOpacity: function (opacity) {
+ this.options.opacity = opacity;
+
+ if (this._map) {
+ this._updateOpacity();
+ }
+
+ return this;
+ },
+
+ setZIndex: function (zIndex) {
+ this.options.zIndex = zIndex;
+ this._updateZIndex();
+
+ return this;
+ },
+
+ setUrl: function (url, noRedraw) {
+ this._url = url;
+
+ if (!noRedraw) {
+ this.redraw();
+ }
+
+ return this;
+ },
+
+ redraw: function () {
+ if (this._map) {
+ this._map._panes.tilePane.empty = false;
+ this._reset(true);
+ this._update();
+ }
+ return this;
+ },
+
+ _updateZIndex: function () {
+ if (this._container && this.options.zIndex !== undefined) {
+ this._container.style.zIndex = this.options.zIndex;
+ }
+ },
+
+ _setAutoZIndex: function (pane, compare) {
+
+ var layers = pane.children,
+ edgeZIndex = -compare(Infinity, -Infinity), // -Infinity for max, Infinity for min
+ zIndex, i, len;
+
+ for (i = 0, len = layers.length; i < len; i++) {
+
+ if (layers[i] !== this._container) {
+ zIndex = parseInt(layers[i].style.zIndex, 10);
+
+ if (!isNaN(zIndex)) {
+ edgeZIndex = compare(edgeZIndex, zIndex);
+ }
+ }
+ }
+
+ this.options.zIndex = this._container.style.zIndex =
+ (isFinite(edgeZIndex) ? edgeZIndex : 0) + compare(1, -1);
+ },
+
+ _updateOpacity: function () {
+ L.DomUtil.setOpacity(this._container, this.options.opacity);
+
+ // stupid webkit hack to force redrawing of tiles
+ var i,
+ tiles = this._tiles;
+
+ if (L.Browser.webkit) {
+ for (i in tiles) {
+ if (tiles.hasOwnProperty(i)) {
+ tiles[i].style.webkitTransform += ' translate(0,0)';
+ }
+ }
+ }
+ },
+
+ _initContainer: function () {
+ var tilePane = this._map._panes.tilePane;
+
+ if (!this._container || tilePane.empty) {
+ this._container = L.DomUtil.create('div', 'leaflet-layer');
+
+ this._updateZIndex();
+
+ tilePane.appendChild(this._container);
+
+ if (this.options.opacity < 1) {
+ this._updateOpacity();
+ }
+ }
+ },
+
+ _resetCallback: function (e) {
+ this._reset(e.hard);
+ },
+
+ _reset: function (clearOldContainer) {
+ var tiles = this._tiles;
+
+ for (var key in tiles) {
+ if (tiles.hasOwnProperty(key)) {
+ this.fire('tileunload', {tile: tiles[key]});
+ }
+ }
+
+ this._tiles = {};
+ this._tilesToLoad = 0;
+
+ if (this.options.reuseTiles) {
+ this._unusedTiles = [];
+ }
+
+ if (clearOldContainer && this._container) {
+ this._container.innerHTML = "";
+ }
+
+ this._initContainer();
+ },
+
+ _update: function () {
+
+ if (!this._map) { return; }
+
+ var bounds = this._map.getPixelBounds(),
+ zoom = this._map.getZoom(),
+ tileSize = this.options.tileSize;
+
+ if (zoom > this.options.maxZoom || zoom < this.options.minZoom) {
+ return;
+ }
+
+ var nwTilePoint = new L.Point(
+ Math.floor(bounds.min.x / tileSize),
+ Math.floor(bounds.min.y / tileSize)),
+
+ seTilePoint = new L.Point(
+ Math.floor(bounds.max.x / tileSize),
+ Math.floor(bounds.max.y / tileSize)),
+
+ tileBounds = new L.Bounds(nwTilePoint, seTilePoint);
+
+ this._addTilesFromCenterOut(tileBounds);
+
+ if (this.options.unloadInvisibleTiles || this.options.reuseTiles) {
+ this._removeOtherTiles(tileBounds);
+ }
+ },
+
+ _addTilesFromCenterOut: function (bounds) {
+ var queue = [],
+ center = bounds.getCenter();
+
+ var j, i, point;
+
+ for (j = bounds.min.y; j <= bounds.max.y; j++) {
+ for (i = bounds.min.x; i <= bounds.max.x; i++) {
+ point = new L.Point(i, j);
+
+ if (this._tileShouldBeLoaded(point)) {
+ queue.push(point);
+ }
+ }
+ }
+
+ var tilesToLoad = queue.length;
+
+ if (tilesToLoad === 0) { return; }
+
+ // load tiles in order of their distance to center
+ queue.sort(function (a, b) {
+ return a.distanceTo(center) - b.distanceTo(center);
+ });
+
+ var fragment = document.createDocumentFragment();
+
+ // if its the first batch of tiles to load
+ if (!this._tilesToLoad) {
+ this.fire('loading');
+ }
+
+ this._tilesToLoad += tilesToLoad;
+
+ for (i = 0; i < tilesToLoad; i++) {
+ this._addTile(queue[i], fragment);
+ }
+
+ this._container.appendChild(fragment);
+ },
+
+ _tileShouldBeLoaded: function (tilePoint) {
+ if ((tilePoint.x + ':' + tilePoint.y) in this._tiles) {
+ return false; // already loaded
+ }
+
+ if (!this.options.continuousWorld) {
+ var limit = this._getWrapTileNum();
+
+ if (this.options.noWrap && (tilePoint.x < 0 || tilePoint.x >= limit) ||
+ tilePoint.y < 0 || tilePoint.y >= limit) {
+ return false; // exceeds world bounds
+ }
+ }
+
+ return true;
+ },
+
+ _removeOtherTiles: function (bounds) {
+ var kArr, x, y, key;
+
+ for (key in this._tiles) {
+ if (this._tiles.hasOwnProperty(key)) {
+ kArr = key.split(':');
+ x = parseInt(kArr[0], 10);
+ y = parseInt(kArr[1], 10);
+
+ // remove tile if it's out of bounds
+ if (x < bounds.min.x || x > bounds.max.x || y < bounds.min.y || y > bounds.max.y) {
+ this._removeTile(key);
+ }
+ }
+ }
+ },
+
+ _removeTile: function (key) {
+ var tile = this._tiles[key];
+
+ this.fire("tileunload", {tile: tile, url: tile.src});
+
+ if (this.options.reuseTiles) {
+ L.DomUtil.removeClass(tile, 'leaflet-tile-loaded');
+ this._unusedTiles.push(tile);
+
+ } else if (tile.parentNode === this._container) {
+ this._container.removeChild(tile);
+ }
+
+ // for https://github.com/CloudMade/Leaflet/issues/137
+ if (!L.Browser.android) {
+ tile.src = L.Util.emptyImageUrl;
+ }
+
+ delete this._tiles[key];
+ },
+
+ _addTile: function (tilePoint, container) {
+ var tilePos = this._getTilePos(tilePoint);
+
+ // get unused tile - or create a new tile
+ var tile = this._getTile();
+
+ /*
+ Chrome 20 layouts much faster with top/left (verify with timeline, frames)
+ Android 4 browser has display issues with top/left and requires transform instead
+ Android 3 browser not tested
+ Android 2 browser requires top/left or tiles disappear on load or first drag
+ (reappear after zoom) https://github.com/CloudMade/Leaflet/issues/866
+ (other browsers don't currently care) - see debug/hacks/jitter.html for an example
+ */
+ L.DomUtil.setPosition(tile, tilePos, L.Browser.chrome || L.Browser.android23);
+
+ this._tiles[tilePoint.x + ':' + tilePoint.y] = tile;
+
+ this._loadTile(tile, tilePoint);
+
+ if (tile.parentNode !== this._container) {
+ container.appendChild(tile);
+ }
+ },
+
+ _getZoomForUrl: function () {
+
+ var options = this.options,
+ zoom = this._map.getZoom();
+
+ if (options.zoomReverse) {
+ zoom = options.maxZoom - zoom;
+ }
+
+ return zoom + options.zoomOffset;
+ },
+
+ _getTilePos: function (tilePoint) {
+ var origin = this._map.getPixelOrigin(),
+ tileSize = this.options.tileSize;
+
+ return tilePoint.multiplyBy(tileSize).subtract(origin);
+ },
+
+ // image-specific code (override to implement e.g. Canvas or SVG tile layer)
+
+ getTileUrl: function (tilePoint) {
+ this._adjustTilePoint(tilePoint);
+
+ return L.Util.template(this._url, L.extend({
+ s: this._getSubdomain(tilePoint),
+ z: this._getZoomForUrl(),
+ x: tilePoint.x,
+ y: tilePoint.y
+ }, this.options));
+ },
+
+ _getWrapTileNum: function () {
+ // TODO refactor, limit is not valid for non-standard projections
+ return Math.pow(2, this._getZoomForUrl());
+ },
+
+ _adjustTilePoint: function (tilePoint) {
+
+ var limit = this._getWrapTileNum();
+
+ // wrap tile coordinates
+ if (!this.options.continuousWorld && !this.options.noWrap) {
+ tilePoint.x = ((tilePoint.x % limit) + limit) % limit;
+ }
+
+ if (this.options.tms) {
+ tilePoint.y = limit - tilePoint.y - 1;
+ }
+ },
+
+ _getSubdomain: function (tilePoint) {
+ var index = (tilePoint.x + tilePoint.y) % this.options.subdomains.length;
+ return this.options.subdomains[index];
+ },
+
+ _createTileProto: function () {
+ var img = this._tileImg = L.DomUtil.create('img', 'leaflet-tile');
+ img.style.width = img.style.height = this.options.tileSize + 'px';
+ img.galleryimg = 'no';
+ },
+
+ _getTile: function () {
+ if (this.options.reuseTiles && this._unusedTiles.length > 0) {
+ var tile = this._unusedTiles.pop();
+ this._resetTile(tile);
+ return tile;
+ }
+ return this._createTile();
+ },
+
+ // Override if data stored on a tile needs to be cleaned up before reuse
+ _resetTile: function (/*tile*/) {},
+
+ _createTile: function () {
+ var tile = this._tileImg.cloneNode(false);
+ tile.onselectstart = tile.onmousemove = L.Util.falseFn;
+ return tile;
+ },
+
+ _loadTile: function (tile, tilePoint) {
+ tile._layer = this;
+ tile.onload = this._tileOnLoad;
+ tile.onerror = this._tileOnError;
+
+ tile.src = this.getTileUrl(tilePoint);
+ },
+
+ _tileLoaded: function () {
+ this._tilesToLoad--;
+ if (!this._tilesToLoad) {
+ this.fire('load');
+ }
+ },
+
+ _tileOnLoad: function () {
+ var layer = this._layer;
+
+ //Only if we are loading an actual image
+ if (this.src !== L.Util.emptyImageUrl) {
+ L.DomUtil.addClass(this, 'leaflet-tile-loaded');
+
+ layer.fire('tileload', {
+ tile: this,
+ url: this.src
+ });
+ }
+
+ layer._tileLoaded();
+ },
+
+ _tileOnError: function () {
+ var layer = this._layer;
+
+ layer.fire('tileerror', {
+ tile: this,
+ url: this.src
+ });
+
+ var newUrl = layer.options.errorTileUrl;
+ if (newUrl) {
+ this.src = newUrl;
+ }
+
+ layer._tileLoaded();
+ }
+});
+
+L.tileLayer = function (url, options) {
+ return new L.TileLayer(url, options);
+};
+
+
+/*
+ * L.TileLayer.WMS is used for putting WMS tile layers on the map.
+ */
+
+L.TileLayer.WMS = L.TileLayer.extend({
+
+ defaultWmsParams: {
+ service: 'WMS',
+ request: 'GetMap',
+ version: '1.1.1',
+ layers: '',
+ styles: '',
+ format: 'image/jpeg',
+ transparent: false
+ },
+
+ initialize: function (url, options) { // (String, Object)
+
+ this._url = url;
+
+ var wmsParams = L.extend({}, this.defaultWmsParams);
+
+ if (options.detectRetina && L.Browser.retina) {
+ wmsParams.width = wmsParams.height = this.options.tileSize * 2;
+ } else {
+ wmsParams.width = wmsParams.height = this.options.tileSize;
+ }
+
+ for (var i in options) {
+ // all keys that are not TileLayer options go to WMS params
+ if (!this.options.hasOwnProperty(i)) {
+ wmsParams[i] = options[i];
+ }
+ }
+
+ this.wmsParams = wmsParams;
+
+ L.setOptions(this, options);
+ },
+
+ onAdd: function (map) {
+
+ var projectionKey = parseFloat(this.wmsParams.version) >= 1.3 ? 'crs' : 'srs';
+ this.wmsParams[projectionKey] = map.options.crs.code;
+
+ L.TileLayer.prototype.onAdd.call(this, map);
+ },
+
+ getTileUrl: function (tilePoint, zoom) { // (Point, Number) -> String
+
+ this._adjustTilePoint(tilePoint);
+
+ var map = this._map,
+ crs = map.options.crs,
+ tileSize = this.options.tileSize,
+
+ nwPoint = tilePoint.multiplyBy(tileSize),
+ sePoint = nwPoint.add(new L.Point(tileSize, tileSize)),
+
+ nw = crs.project(map.unproject(nwPoint, zoom)),
+ se = crs.project(map.unproject(sePoint, zoom)),
+
+ bbox = [nw.x, se.y, se.x, nw.y].join(','),
+
+ url = L.Util.template(this._url, {s: this._getSubdomain(tilePoint)});
+
+ return url + L.Util.getParamString(this.wmsParams, url) + "&bbox=" + bbox;
+ },
+
+ setParams: function (params, noRedraw) {
+
+ L.extend(this.wmsParams, params);
+
+ if (!noRedraw) {
+ this.redraw();
+ }
+
+ return this;
+ }
+});
+
+L.tileLayer.wms = function (url, options) {
+ return new L.TileLayer.WMS(url, options);
+};
+
+
+/*
+ * L.TileLayer.Canvas is a class that you can use as a base for creating
+ * dynamically drawn Canvas-based tile layers.
+ */
+
+L.TileLayer.Canvas = L.TileLayer.extend({
+ options: {
+ async: false
+ },
+
+ initialize: function (options) {
+ L.setOptions(this, options);
+ },
+
+ redraw: function () {
+ var tiles = this._tiles;
+
+ for (var i in tiles) {
+ if (tiles.hasOwnProperty(i)) {
+ this._redrawTile(tiles[i]);
+ }
+ }
+ },
+
+ _redrawTile: function (tile) {
+ this.drawTile(tile, tile._tilePoint, this._map._zoom);
+ },
+
+ _createTileProto: function () {
+ var proto = this._canvasProto = L.DomUtil.create('canvas', 'leaflet-tile');
+ proto.width = proto.height = this.options.tileSize;
+ },
+
+ _createTile: function () {
+ var tile = this._canvasProto.cloneNode(false);
+ tile.onselectstart = tile.onmousemove = L.Util.falseFn;
+ return tile;
+ },
+
+ _loadTile: function (tile, tilePoint) {
+ tile._layer = this;
+ tile._tilePoint = tilePoint;
+
+ this._redrawTile(tile);
+
+ if (!this.options.async) {
+ this.tileDrawn(tile);
+ }
+ },
+
+ drawTile: function (/*tile, tilePoint*/) {
+ // override with rendering code
+ },
+
+ tileDrawn: function (tile) {
+ this._tileOnLoad.call(tile);
+ }
+});
+
+
+L.tileLayer.canvas = function (options) {
+ return new L.TileLayer.Canvas(options);
+};
+
+
+/*
+ * L.ImageOverlay is used to overlay images over the map (to specific geographical bounds).
+ */
+
+L.ImageOverlay = L.Class.extend({
+ includes: L.Mixin.Events,
+
+ options: {
+ opacity: 1
+ },
+
+ initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
+ this._url = url;
+ this._bounds = L.latLngBounds(bounds);
+
+ L.setOptions(this, options);
+ },
+
+ onAdd: function (map) {
+ this._map = map;
+
+ if (!this._image) {
+ this._initImage();
+ }
+
+ map._panes.overlayPane.appendChild(this._image);
+
+ map.on('viewreset', this._reset, this);
+
+ if (map.options.zoomAnimation && L.Browser.any3d) {
+ map.on('zoomanim', this._animateZoom, this);
+ }
+
+ this._reset();
+ },
+
+ onRemove: function (map) {
+ map.getPanes().overlayPane.removeChild(this._image);
+
+ map.off('viewreset', this._reset, this);
+
+ if (map.options.zoomAnimation) {
+ map.off('zoomanim', this._animateZoom, this);
+ }
+ },
+
+ addTo: function (map) {
+ map.addLayer(this);
+ return this;
+ },
+
+ setOpacity: function (opacity) {
+ this.options.opacity = opacity;
+ this._updateOpacity();
+ return this;
+ },
+
+ // TODO remove bringToFront/bringToBack duplication from TileLayer/Path
+ bringToFront: function () {
+ if (this._image) {
+ this._map._panes.overlayPane.appendChild(this._image);
+ }
+ return this;
+ },
+
+ bringToBack: function () {
+ var pane = this._map._panes.overlayPane;
+ if (this._image) {
+ pane.insertBefore(this._image, pane.firstChild);
+ }
+ return this;
+ },
+
+ _initImage: function () {
+ this._image = L.DomUtil.create('img', 'leaflet-image-layer');
+
+ if (this._map.options.zoomAnimation && L.Browser.any3d) {
+ L.DomUtil.addClass(this._image, 'leaflet-zoom-animated');
+ } else {
+ L.DomUtil.addClass(this._image, 'leaflet-zoom-hide');
+ }
+
+ this._updateOpacity();
+
+ //TODO createImage util method to remove duplication
+ L.extend(this._image, {
+ galleryimg: 'no',
+ onselectstart: L.Util.falseFn,
+ onmousemove: L.Util.falseFn,
+ onload: L.bind(this._onImageLoad, this),
+ src: this._url
+ });
+ },
+
+ _animateZoom: function (e) {
+ var map = this._map,
+ image = this._image,
+ scale = map.getZoomScale(e.zoom),
+ nw = this._bounds.getNorthWest(),
+ se = this._bounds.getSouthEast(),
+
+ topLeft = map._latLngToNewLayerPoint(nw, e.zoom, e.center),
+ size = map._latLngToNewLayerPoint(se, e.zoom, e.center)._subtract(topLeft),
+ origin = topLeft._add(size._multiplyBy((1 / 2) * (1 - 1 / scale)));
+
+ image.style[L.DomUtil.TRANSFORM] =
+ L.DomUtil.getTranslateString(origin) + ' scale(' + scale + ') ';
+ },
+
+ _reset: function () {
+ var image = this._image,
+ topLeft = this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
+ size = this._map.latLngToLayerPoint(this._bounds.getSouthEast())._subtract(topLeft);
+
+ L.DomUtil.setPosition(image, topLeft);
+
+ image.style.width = size.x + 'px';
+ image.style.height = size.y + 'px';
+ },
+
+ _onImageLoad: function () {
+ this.fire('load');
+ },
+
+ _updateOpacity: function () {
+ L.DomUtil.setOpacity(this._image, this.options.opacity);
+ }
+});
+
+L.imageOverlay = function (url, bounds, options) {
+ return new L.ImageOverlay(url, bounds, options);
+};
+
+
+/*
+ * L.Icon is an image-based icon class that you can use with L.Marker for custom markers.
+ */
+
+L.Icon = L.Class.extend({
+ options: {
+ /*
+ iconUrl: (String) (required)
+ iconRetinaUrl: (String) (optional, used for retina devices if detected)
+ iconSize: (Point) (can be set through CSS)
+ iconAnchor: (Point) (centered by default, can be set in CSS with negative margins)
+ popupAnchor: (Point) (if not specified, popup opens in the anchor point)
+ shadowUrl: (Point) (no shadow by default)
+ shadowRetinaUrl: (String) (optional, used for retina devices if detected)
+ shadowSize: (Point)
+ shadowAnchor: (Point)
+ */
+ className: ''
+ },
+
+ initialize: function (options) {
+ L.setOptions(this, options);
+ },
+
+ createIcon: function () {
+ return this._createIcon('icon');
+ },
+
+ createShadow: function () {
+ return this._createIcon('shadow');
+ },
+
+ _createIcon: function (name) {
+ var src = this._getIconUrl(name);
+
+ if (!src) {
+ if (name === 'icon') {
+ throw new Error("iconUrl not set in Icon options (see the docs).");
+ }
+ return null;
+ }
+
+ var img = this._createImg(src);
+ this._setIconStyles(img, name);
+
+ return img;
+ },
+
+ _setIconStyles: function (img, name) {
+ var options = this.options,
+ size = L.point(options[name + 'Size']),
+ anchor;
+
+ if (name === 'shadow') {
+ anchor = L.point(options.shadowAnchor || options.iconAnchor);
+ } else {
+ anchor = L.point(options.iconAnchor);
+ }
+
+ if (!anchor && size) {
+ anchor = size.divideBy(2, true);
+ }
+
+ img.className = 'leaflet-marker-' + name + ' ' + options.className;
+
+ if (anchor) {
+ img.style.marginLeft = (-anchor.x) + 'px';
+ img.style.marginTop = (-anchor.y) + 'px';
+ }
+
+ if (size) {
+ img.style.width = size.x + 'px';
+ img.style.height = size.y + 'px';
+ }
+ },
+
+ _createImg: function (src) {
+ var el;
+
+ if (!L.Browser.ie6) {
+ el = document.createElement('img');
+ el.src = src;
+ } else {
+ el = document.createElement('div');
+ el.style.filter =
+ 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + src + '")';
+ }
+ return el;
+ },
+
+ _getIconUrl: function (name) {
+ if (L.Browser.retina && this.options[name + 'RetinaUrl']) {
+ return this.options[name + 'RetinaUrl'];
+ }
+ return this.options[name + 'Url'];
+ }
+});
+
+L.icon = function (options) {
+ return new L.Icon(options);
+};
+
+
+/*
+ * L.Icon.Default is the blue marker icon used by default in Leaflet.
+ */
+
+L.Icon.Default = L.Icon.extend({
+
+ options: {
+ iconSize: new L.Point(25, 41),
+ iconAnchor: new L.Point(12, 41),
+ popupAnchor: new L.Point(1, -34),
+
+ shadowSize: new L.Point(41, 41)
+ },
+
+ _getIconUrl: function (name) {
+ var key = name + 'Url';
+
+ if (this.options[key]) {
+ return this.options[key];
+ }
+
+ if (L.Browser.retina && name === 'icon') {
+ name += '@2x';
+ }
+
+ var path = L.Icon.Default.imagePath;
+
+ if (!path) {
+ throw new Error("Couldn't autodetect L.Icon.Default.imagePath, set it manually.");
+ }
+
+ return path + '/marker-' + name + '.png';
+ }
+});
+
+L.Icon.Default.imagePath = (function () {
+ var scripts = document.getElementsByTagName('script'),
+ leafletRe = /\/?leaflet[\-\._]?([\w\-\._]*)\.js\??/;
+
+ var i, len, src, matches;
+
+ for (i = 0, len = scripts.length; i < len; i++) {
+ src = scripts[i].src;
+ matches = src.match(leafletRe);
+
+ if (matches) {
+ return src.split(leafletRe)[0] + '/images';
+ }
+ }
+}());
+
+
+/*
+ * L.Marker is used to display clickable/draggable icons on the map.
+ */
+
+L.Marker = L.Class.extend({
+
+ includes: L.Mixin.Events,
+
+ options: {
+ icon: new L.Icon.Default(),
+ title: '',
+ clickable: true,
+ draggable: false,
+ zIndexOffset: 0,
+ opacity: 1,
+ riseOnHover: false,
+ riseOffset: 250
+ },
+
+ initialize: function (latlng, options) {
+ L.setOptions(this, options);
+ this._latlng = L.latLng(latlng);
+ },
+
+ onAdd: function (map) {
+ this._map = map;
+
+ map.on('viewreset', this.update, this);
+
+ this._initIcon();
+ this.update();
+
+ if (map.options.zoomAnimation && map.options.markerZoomAnimation) {
+ map.on('zoomanim', this._animateZoom, this);
+ }
+ },
+
+ addTo: function (map) {
+ map.addLayer(this);
+ return this;
+ },
+
+ onRemove: function (map) {
+ this._removeIcon();
+
+ this.fire('remove');
+
+ map.off({
+ 'viewreset': this.update,
+ 'zoomanim': this._animateZoom
+ }, this);
+
+ this._map = null;
+ },
+
+ getLatLng: function () {
+ return this._latlng;
+ },
+
+ setLatLng: function (latlng) {
+ this._latlng = L.latLng(latlng);
+
+ this.update();
+
+ return this.fire('move', { latlng: this._latlng });
+ },
+
+ setZIndexOffset: function (offset) {
+ this.options.zIndexOffset = offset;
+ this.update();
+
+ return this;
+ },
+
+ setIcon: function (icon) {
+ if (this._map) {
+ this._removeIcon();
+ }
+
+ this.options.icon = icon;
+
+ if (this._map) {
+ this._initIcon();
+ this.update();
+ }
+
+ return this;
+ },
+
+ update: function () {
+ if (this._icon) {
+ var pos = this._map.latLngToLayerPoint(this._latlng).round();
+ this._setPos(pos);
+ }
+
+ return this;
+ },
+
+ _initIcon: function () {
+ var options = this.options,
+ map = this._map,
+ animation = (map.options.zoomAnimation && map.options.markerZoomAnimation),
+ classToAdd = animation ? 'leaflet-zoom-animated' : 'leaflet-zoom-hide',
+ needOpacityUpdate = false;
+
+ if (!this._icon) {
+ this._icon = options.icon.createIcon();
+
+ if (options.title) {
+ this._icon.title = options.title;
+ }
+
+ this._initInteraction();
+ needOpacityUpdate = (this.options.opacity < 1);
+
+ L.DomUtil.addClass(this._icon, classToAdd);
+
+ if (options.riseOnHover) {
+ L.DomEvent
+ .on(this._icon, 'mouseover', this._bringToFront, this)
+ .on(this._icon, 'mouseout', this._resetZIndex, this);
+ }
+ }
+
+ if (!this._shadow) {
+ this._shadow = options.icon.createShadow();
+
+ if (this._shadow) {
+ L.DomUtil.addClass(this._shadow, classToAdd);
+ needOpacityUpdate = (this.options.opacity < 1);
+ }
+ }
+
+ if (needOpacityUpdate) {
+ this._updateOpacity();
+ }
+
+ var panes = this._map._panes;
+
+ panes.markerPane.appendChild(this._icon);
+
+ if (this._shadow) {
+ panes.shadowPane.appendChild(this._shadow);
+ }
+ },
+
+ _removeIcon: function () {
+ var panes = this._map._panes;
+
+ if (this.options.riseOnHover) {
+ L.DomEvent
+ .off(this._icon, 'mouseover', this._bringToFront)
+ .off(this._icon, 'mouseout', this._resetZIndex);
+ }
+
+ panes.markerPane.removeChild(this._icon);
+
+ if (this._shadow) {
+ panes.shadowPane.removeChild(this._shadow);
+ }
+
+ this._icon = this._shadow = null;
+ },
+
+ _setPos: function (pos) {
+ L.DomUtil.setPosition(this._icon, pos);
+
+ if (this._shadow) {
+ L.DomUtil.setPosition(this._shadow, pos);
+ }
+
+ this._zIndex = pos.y + this.options.zIndexOffset;
+
+ this._resetZIndex();
+ },
+
+ _updateZIndex: function (offset) {
+ this._icon.style.zIndex = this._zIndex + offset;
+ },
+
+ _animateZoom: function (opt) {
+ var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center);
+
+ this._setPos(pos);
+ },
+
+ _initInteraction: function () {
+
+ if (!this.options.clickable) { return; }
+
+ // TODO refactor into something shared with Map/Path/etc. to DRY it up
+
+ var icon = this._icon,
+ events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu'];
+
+ L.DomUtil.addClass(icon, 'leaflet-clickable');
+ L.DomEvent.on(icon, 'click', this._onMouseClick, this);
+
+ for (var i = 0; i < events.length; i++) {
+ L.DomEvent.on(icon, events[i], this._fireMouseEvent, this);
+ }
+
+ if (L.Handler.MarkerDrag) {
+ this.dragging = new L.Handler.MarkerDrag(this);
+
+ if (this.options.draggable) {
+ this.dragging.enable();
+ }
+ }
+ },
+
+ _onMouseClick: function (e) {
+ var wasDragged = this.dragging && this.dragging.moved();
+
+ if (this.hasEventListeners(e.type) || wasDragged) {
+ L.DomEvent.stopPropagation(e);
+ }
+
+ if (wasDragged) { return; }
+
+ if ((!this.dragging || !this.dragging._enabled) && this._map.dragging && this._map.dragging.moved()) { return; }
+
+ this.fire(e.type, {
+ originalEvent: e
+ });
+ },
+
+ _fireMouseEvent: function (e) {
+
+ this.fire(e.type, {
+ originalEvent: e
+ });
+
+ // TODO proper custom event propagation
+ // this line will always be called if marker is in a FeatureGroup
+ if (e.type === 'contextmenu' && this.hasEventListeners(e.type)) {
+ L.DomEvent.preventDefault(e);
+ }
+ if (e.type !== 'mousedown') {
+ L.DomEvent.stopPropagation(e);
+ }
+ },
+
+ setOpacity: function (opacity) {
+ this.options.opacity = opacity;
+ if (this._map) {
+ this._updateOpacity();
+ }
+ },
+
+ _updateOpacity: function () {
+ L.DomUtil.setOpacity(this._icon, this.options.opacity);
+ if (this._shadow) {
+ L.DomUtil.setOpacity(this._shadow, this.options.opacity);
+ }
+ },
+
+ _bringToFront: function () {
+ this._updateZIndex(this.options.riseOffset);
+ },
+
+ _resetZIndex: function () {
+ this._updateZIndex(0);
+ }
+});
+
+L.marker = function (latlng, options) {
+ return new L.Marker(latlng, options);
+};
+
+
+/*
+ * L.DivIcon is a lightweight HTML-based icon class (as opposed to the image-based L.Icon)
+ * to use with L.Marker.
+ */
+
+L.DivIcon = L.Icon.extend({
+ options: {
+ iconSize: new L.Point(12, 12), // also can be set through CSS
+ /*
+ iconAnchor: (Point)
+ popupAnchor: (Point)
+ html: (String)
+ bgPos: (Point)
+ */
+ className: 'leaflet-div-icon'
+ },
+
+ createIcon: function () {
+ var div = document.createElement('div'),
+ options = this.options;
+
+ if (options.html) {
+ div.innerHTML = options.html;
+ }
+
+ if (options.bgPos) {
+ div.style.backgroundPosition =
+ (-options.bgPos.x) + 'px ' + (-options.bgPos.y) + 'px';
+ }
+
+ this._setIconStyles(div, 'icon');
+ return div;
+ },
+
+ createShadow: function () {
+ return null;
+ }
+});
+
+L.divIcon = function (options) {
+ return new L.DivIcon(options);
+};
+
+
+/*
+ * L.Popup is used for displaying popups on the map.
+ */
+
+L.Map.mergeOptions({
+ closePopupOnClick: true
+});
+
+L.Popup = L.Class.extend({
+ includes: L.Mixin.Events,
+
+ options: {
+ minWidth: 50,
+ maxWidth: 300,
+ maxHeight: null,
+ autoPan: true,
+ closeButton: true,
+ offset: new L.Point(0, 6),
+ autoPanPadding: new L.Point(5, 5),
+ className: '',
+ zoomAnimation: true
+ },
+
+ initialize: function (options, source) {
+ L.setOptions(this, options);
+
+ this._source = source;
+ this._animated = L.Browser.any3d && this.options.zoomAnimation;
+ },
+
+ onAdd: function (map) {
+ this._map = map;
+
+ if (!this._container) {
+ this._initLayout();
+ }
+ this._updateContent();
+
+ var animFade = map.options.fadeAnimation;
+
+ if (animFade) {
+ L.DomUtil.setOpacity(this._container, 0);
+ }
+ map._panes.popupPane.appendChild(this._container);
+
+ map.on('viewreset', this._updatePosition, this);
+
+ if (this._animated) {
+ map.on('zoomanim', this._zoomAnimation, this);
+ }
+
+ if (map.options.closePopupOnClick) {
+ map.on('preclick', this._close, this);
+ }
+
+ this._update();
+
+ if (animFade) {
+ L.DomUtil.setOpacity(this._container, 1);
+ }
+ },
+
+ addTo: function (map) {
+ map.addLayer(this);
+ return this;
+ },
+
+ openOn: function (map) {
+ map.openPopup(this);
+ return this;
+ },
+
+ onRemove: function (map) {
+ map._panes.popupPane.removeChild(this._container);
+
+ L.Util.falseFn(this._container.offsetWidth); // force reflow
+
+ map.off({
+ viewreset: this._updatePosition,
+ preclick: this._close,
+ zoomanim: this._zoomAnimation
+ }, this);
+
+ if (map.options.fadeAnimation) {
+ L.DomUtil.setOpacity(this._container, 0);
+ }
+
+ this._map = null;
+ },
+
+ setLatLng: function (latlng) {
+ this._latlng = L.latLng(latlng);
+ this._update();
+ return this;
+ },
+
+ setContent: function (content) {
+ this._content = content;
+ this._update();
+ return this;
+ },
+
+ _close: function () {
+ var map = this._map;
+
+ if (map) {
+ map._popup = null;
+
+ map
+ .removeLayer(this)
+ .fire('popupclose', {popup: this});
+ }
+ },
+
+ _initLayout: function () {
+ var prefix = 'leaflet-popup',
+ containerClass = prefix + ' ' + this.options.className + ' leaflet-zoom-' +
+ (this._animated ? 'animated' : 'hide'),
+ container = this._container = L.DomUtil.create('div', containerClass),
+ closeButton;
+
+ if (this.options.closeButton) {
+ closeButton = this._closeButton =
+ L.DomUtil.create('a', prefix + '-close-button', container);
+ closeButton.href = '#close';
+ closeButton.innerHTML = '×';
+
+ L.DomEvent.on(closeButton, 'click', this._onCloseButtonClick, this);
+ }
+
+ var wrapper = this._wrapper =
+ L.DomUtil.create('div', prefix + '-content-wrapper', container);
+ L.DomEvent.disableClickPropagation(wrapper);
+
+ this._contentNode = L.DomUtil.create('div', prefix + '-content', wrapper);
+ L.DomEvent.on(this._contentNode, 'mousewheel', L.DomEvent.stopPropagation);
+
+ this._tipContainer = L.DomUtil.create('div', prefix + '-tip-container', container);
+ this._tip = L.DomUtil.create('div', prefix + '-tip', this._tipContainer);
+ },
+
+ _update: function () {
+ if (!this._map) { return; }
+
+ this._container.style.visibility = 'hidden';
+
+ this._updateContent();
+ this._updateLayout();
+ this._updatePosition();
+
+ this._container.style.visibility = '';
+
+ this._adjustPan();
+ },
+
+ _updateContent: function () {
+ if (!this._content) { return; }
+
+ if (typeof this._content === 'string') {
+ this._contentNode.innerHTML = this._content;
+ } else {
+ while (this._contentNode.hasChildNodes()) {
+ this._contentNode.removeChild(this._contentNode.firstChild);
+ }
+ this._contentNode.appendChild(this._content);
+ }
+ this.fire('contentupdate');
+ },
+
+ _updateLayout: function () {
+ var container = this._contentNode,
+ style = container.style;
+
+ style.width = '';
+ style.whiteSpace = 'nowrap';
+
+ var width = container.offsetWidth;
+ width = Math.min(width, this.options.maxWidth);
+ width = Math.max(width, this.options.minWidth);
+
+ style.width = (width + 1) + 'px';
+ style.whiteSpace = '';
+
+ style.height = '';
+
+ var height = container.offsetHeight,
+ maxHeight = this.options.maxHeight,
+ scrolledClass = 'leaflet-popup-scrolled';
+
+ if (maxHeight && height > maxHeight) {
+ style.height = maxHeight + 'px';
+ L.DomUtil.addClass(container, scrolledClass);
+ } else {
+ L.DomUtil.removeClass(container, scrolledClass);
+ }
+
+ this._containerWidth = this._container.offsetWidth;
+ },
+
+ _updatePosition: function () {
+ if (!this._map) { return; }
+
+ var pos = this._map.latLngToLayerPoint(this._latlng),
+ animated = this._animated,
+ offset = this.options.offset;
+
+ if (animated) {
+ L.DomUtil.setPosition(this._container, pos);
+ }
+
+ this._containerBottom = -offset.y - (animated ? 0 : pos.y);
+ this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x + (animated ? 0 : pos.x);
+
+ //Bottom position the popup in case the height of the popup changes (images loading etc)
+ this._container.style.bottom = this._containerBottom + 'px';
+ this._container.style.left = this._containerLeft + 'px';
+ },
+
+ _zoomAnimation: function (opt) {
+ var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center);
+
+ L.DomUtil.setPosition(this._container, pos);
+ },
+
+ _adjustPan: function () {
+ if (!this.options.autoPan) { return; }
+
+ var map = this._map,
+ containerHeight = this._container.offsetHeight,
+ containerWidth = this._containerWidth,
+
+ layerPos = new L.Point(this._containerLeft, -containerHeight - this._containerBottom);
+
+ if (this._animated) {
+ layerPos._add(L.DomUtil.getPosition(this._container));
+ }
+
+ var containerPos = map.layerPointToContainerPoint(layerPos),
+ padding = this.options.autoPanPadding,
+ size = map.getSize(),
+ dx = 0,
+ dy = 0;
+
+ if (containerPos.x < 0) {
+ dx = containerPos.x - padding.x;
+ }
+ if (containerPos.x + containerWidth > size.x) {
+ dx = containerPos.x + containerWidth - size.x + padding.x;
+ }
+ if (containerPos.y < 0) {
+ dy = containerPos.y - padding.y;
+ }
+ if (containerPos.y + containerHeight > size.y) {
+ dy = containerPos.y + containerHeight - size.y + padding.y;
+ }
+
+ if (dx || dy) {
+ map.panBy(new L.Point(dx, dy));
+ }
+ },
+
+ _onCloseButtonClick: function (e) {
+ this._close();
+ L.DomEvent.stop(e);
+ }
+});
+
+L.popup = function (options, source) {
+ return new L.Popup(options, source);
+};
+
+
+/*
+ * Popup extension to L.Marker, adding popup-related methods.
+ */
+
+L.Marker.include({
+ openPopup: function () {
+ if (this._popup && this._map) {
+ this._popup.setLatLng(this._latlng);
+ this._map.openPopup(this._popup);
+ }
+
+ return this;
+ },
+
+ closePopup: function () {
+ if (this._popup) {
+ this._popup._close();
+ }
+ return this;
+ },
+
+ bindPopup: function (content, options) {
+ var anchor = L.point(this.options.icon.options.popupAnchor) || new L.Point(0, 0);
+
+ anchor = anchor.add(L.Popup.prototype.options.offset);
+
+ if (options && options.offset) {
+ anchor = anchor.add(options.offset);
+ }
+
+ options = L.extend({offset: anchor}, options);
+
+ if (!this._popup) {
+ this
+ .on('click', this.openPopup, this)
+ .on('remove', this.closePopup, this)
+ .on('move', this._movePopup, this);
+ }
+
+ this._popup = new L.Popup(options, this)
+ .setContent(content);
+
+ return this;
+ },
+
+ unbindPopup: function () {
+ if (this._popup) {
+ this._popup = null;
+ this
+ .off('click', this.openPopup)
+ .off('remove', this.closePopup)
+ .off('move', this._movePopup);
+ }
+ return this;
+ },
+
+ _movePopup: function (e) {
+ this._popup.setLatLng(e.latlng);
+ }
+});
+
+
+/*
+ * Adds popup-related methods to L.Map.
+ */
+
+L.Map.include({
+ openPopup: function (popup) {
+ this.closePopup();
+
+ this._popup = popup;
+
+ return this
+ .addLayer(popup)
+ .fire('popupopen', {popup: this._popup});
+ },
+
+ closePopup: function () {
+ if (this._popup) {
+ this._popup._close();
+ }
+ return this;
+ }
+});
+
+
+/*
+ * L.LayerGroup is a class to combine several layers into one so that
+ * you can manipulate the group (e.g. add/remove it) as one layer.
+ */
+
+L.LayerGroup = L.Class.extend({
+ initialize: function (layers) {
+ this._layers = {};
+
+ var i, len;
+
+ if (layers) {
+ for (i = 0, len = layers.length; i < len; i++) {
+ this.addLayer(layers[i]);
+ }
+ }
+ },
+
+ addLayer: function (layer) {
+ var id = L.stamp(layer);
+
+ this._layers[id] = layer;
+
+ if (this._map) {
+ this._map.addLayer(layer);
+ }
+
+ return this;
+ },
+
+ removeLayer: function (layer) {
+ var id = L.stamp(layer);
+
+ delete this._layers[id];
+
+ if (this._map) {
+ this._map.removeLayer(layer);
+ }
+
+ return this;
+ },
+
+ clearLayers: function () {
+ this.eachLayer(this.removeLayer, this);
+ return this;
+ },
+
+ invoke: function (methodName) {
+ var args = Array.prototype.slice.call(arguments, 1),
+ i, layer;
+
+ for (i in this._layers) {
+ if (this._layers.hasOwnProperty(i)) {
+ layer = this._layers[i];
+
+ if (layer[methodName]) {
+ layer[methodName].apply(layer, args);
+ }
+ }
+ }
+
+ return this;
+ },
+
+ onAdd: function (map) {
+ this._map = map;
+ this.eachLayer(map.addLayer, map);
+ },
+
+ onRemove: function (map) {
+ this.eachLayer(map.removeLayer, map);
+ this._map = null;
+ },
+
+ addTo: function (map) {
+ map.addLayer(this);
+ return this;
+ },
+
+ eachLayer: function (method, context) {
+ for (var i in this._layers) {
+ if (this._layers.hasOwnProperty(i)) {
+ method.call(context, this._layers[i]);
+ }
+ }
+ },
+
+ setZIndex: function (zIndex) {
+ return this.invoke('setZIndex', zIndex);
+ }
+});
+
+L.layerGroup = function (layers) {
+ return new L.LayerGroup(layers);
+};
+
+
+/*
+ * L.FeatureGroup extends L.LayerGroup by introducing mouse events and additional methods
+ * shared between a group of interactive layers (like vectors or markers).
+ */
+
+L.FeatureGroup = L.LayerGroup.extend({
+ includes: L.Mixin.Events,
+
+ statics: {
+ EVENTS: 'click dblclick mouseover mouseout mousemove contextmenu'
+ },
+
+ addLayer: function (layer) {
+ if (this._layers[L.stamp(layer)]) {
+ return this;
+ }
+
+ layer.on(L.FeatureGroup.EVENTS, this._propagateEvent, this);
+
+ L.LayerGroup.prototype.addLayer.call(this, layer);
+
+ if (this._popupContent && layer.bindPopup) {
+ layer.bindPopup(this._popupContent, this._popupOptions);
+ }
+
+ return this.fire('layeradd', {layer: layer});
+ },
+
+ removeLayer: function (layer) {
+ layer.off(L.FeatureGroup.EVENTS, this._propagateEvent, this);
+
+ L.LayerGroup.prototype.removeLayer.call(this, layer);
+
+
+ if (this._popupContent) {
+ this.invoke('unbindPopup');
+ }
+
+ return this.fire('layerremove', {layer: layer});
+ },
+
+ bindPopup: function (content, options) {
+ this._popupContent = content;
+ this._popupOptions = options;
+ return this.invoke('bindPopup', content, options);
+ },
+
+ setStyle: function (style) {
+ return this.invoke('setStyle', style);
+ },
+
+ bringToFront: function () {
+ return this.invoke('bringToFront');
+ },
+
+ bringToBack: function () {
+ return this.invoke('bringToBack');
+ },
+
+ getBounds: function () {
+ var bounds = new L.LatLngBounds();
+
+ this.eachLayer(function (layer) {
+ bounds.extend(layer instanceof L.Marker ? layer.getLatLng() : layer.getBounds());
+ });
+
+ return bounds;
+ },
+
+ _propagateEvent: function (e) {
+ e.layer = e.target;
+ e.target = this;
+
+ this.fire(e.type, e);
+ }
+});
+
+L.featureGroup = function (layers) {
+ return new L.FeatureGroup(layers);
+};
+
+
+/*
+ * L.Path is a base class for rendering vector paths on a map. Inherited by Polyline, Circle, etc.
+ */
+
+L.Path = L.Class.extend({
+ includes: [L.Mixin.Events],
+
+ statics: {
+ // how much to extend the clip area around the map view
+ // (relative to its size, e.g. 0.5 is half the screen in each direction)
+ // set it so that SVG element doesn't exceed 1280px (vectors flicker on dragend if it is)
+ CLIP_PADDING: L.Browser.mobile ?
+ Math.max(0, Math.min(0.5,
+ (1280 / Math.max(window.innerWidth, window.innerHeight) - 1) / 2)) : 0.5
+ },
+
+ options: {
+ stroke: true,
+ color: '#0033ff',
+ dashArray: null,
+ weight: 5,
+ opacity: 0.5,
+
+ fill: false,
+ fillColor: null, //same as color by default
+ fillOpacity: 0.2,
+
+ clickable: true
+ },
+
+ initialize: function (options) {
+ L.setOptions(this, options);
+ },
+
+ onAdd: function (map) {
+ this._map = map;
+
+ if (!this._container) {
+ this._initElements();
+ this._initEvents();
+ }
+
+ this.projectLatlngs();
+ this._updatePath();
+
+ if (this._container) {
+ this._map._pathRoot.appendChild(this._container);
+ }
+
+ this.fire('add');
+
+ map.on({
+ 'viewreset': this.projectLatlngs,
+ 'moveend': this._updatePath
+ }, this);
+ },
+
+ addTo: function (map) {
+ map.addLayer(this);
+ return this;
+ },
+
+ onRemove: function (map) {
+ map._pathRoot.removeChild(this._container);
+
+ // Need to fire remove event before we set _map to null as the event hooks might need the object
+ this.fire('remove');
+ this._map = null;
+
+ if (L.Browser.vml) {
+ this._container = null;
+ this._stroke = null;
+ this._fill = null;
+ }
+
+ map.off({
+ 'viewreset': this.projectLatlngs,
+ 'moveend': this._updatePath
+ }, this);
+ },
+
+ projectLatlngs: function () {
+ // do all projection stuff here
+ },
+
+ setStyle: function (style) {
+ L.setOptions(this, style);
+
+ if (this._container) {
+ this._updateStyle();
+ }
+
+ return this;
+ },
+
+ redraw: function () {
+ if (this._map) {
+ this.projectLatlngs();
+ this._updatePath();
+ }
+ return this;
+ }
+});
+
+L.Map.include({
+ _updatePathViewport: function () {
+ var p = L.Path.CLIP_PADDING,
+ size = this.getSize(),
+ panePos = L.DomUtil.getPosition(this._mapPane),
+ min = panePos.multiplyBy(-1)._subtract(size.multiplyBy(p)._round()),
+ max = min.add(size.multiplyBy(1 + p * 2)._round());
+
+ this._pathViewport = new L.Bounds(min, max);
+ }
+});
+
+
+/*
+ * Extends L.Path with SVG-specific rendering code.
+ */
+
+L.Path.SVG_NS = 'http://www.w3.org/2000/svg';
+
+L.Browser.svg = !!(document.createElementNS && document.createElementNS(L.Path.SVG_NS, 'svg').createSVGRect);
+
+L.Path = L.Path.extend({
+ statics: {
+ SVG: L.Browser.svg
+ },
+
+ bringToFront: function () {
+ var root = this._map._pathRoot,
+ path = this._container;
+
+ if (path && root.lastChild !== path) {
+ root.appendChild(path);
+ }
+ return this;
+ },
+
+ bringToBack: function () {
+ var root = this._map._pathRoot,
+ path = this._container,
+ first = root.firstChild;
+
+ if (path && first !== path) {
+ root.insertBefore(path, first);
+ }
+ return this;
+ },
+
+ getPathString: function () {
+ // form path string here
+ },
+
+ _createElement: function (name) {
+ return document.createElementNS(L.Path.SVG_NS, name);
+ },
+
+ _initElements: function () {
+ this._map._initPathRoot();
+ this._initPath();
+ this._initStyle();
+ },
+
+ _initPath: function () {
+ this._container = this._createElement('g');
+
+ this._path = this._createElement('path');
+ this._container.appendChild(this._path);
+ },
+
+ _initStyle: function () {
+ if (this.options.stroke) {
+ this._path.setAttribute('stroke-linejoin', 'round');
+ this._path.setAttribute('stroke-linecap', 'round');
+ }
+ if (this.options.fill) {
+ this._path.setAttribute('fill-rule', 'evenodd');
+ }
+ this._updateStyle();
+ },
+
+ _updateStyle: function () {
+ if (this.options.stroke) {
+ this._path.setAttribute('stroke', this.options.color);
+ this._path.setAttribute('stroke-opacity', this.options.opacity);
+ this._path.setAttribute('stroke-width', this.options.weight);
+ if (this.options.dashArray) {
+ this._path.setAttribute('stroke-dasharray', this.options.dashArray);
+ } else {
+ this._path.removeAttribute('stroke-dasharray');
+ }
+ } else {
+ this._path.setAttribute('stroke', 'none');
+ }
+ if (this.options.fill) {
+ this._path.setAttribute('fill', this.options.fillColor || this.options.color);
+ this._path.setAttribute('fill-opacity', this.options.fillOpacity);
+ } else {
+ this._path.setAttribute('fill', 'none');
+ }
+ },
+
+ _updatePath: function () {
+ var str = this.getPathString();
+ if (!str) {
+ // fix webkit empty string parsing bug
+ str = 'M0 0';
+ }
+ this._path.setAttribute('d', str);
+ },
+
+ // TODO remove duplication with L.Map
+ _initEvents: function () {
+ if (this.options.clickable) {
+ if (L.Browser.svg || !L.Browser.vml) {
+ this._path.setAttribute('class', 'leaflet-clickable');
+ }
+
+ L.DomEvent.on(this._container, 'click', this._onMouseClick, this);
+
+ var events = ['dblclick', 'mousedown', 'mouseover',
+ 'mouseout', 'mousemove', 'contextmenu'];
+ for (var i = 0; i < events.length; i++) {
+ L.DomEvent.on(this._container, events[i], this._fireMouseEvent, this);
+ }
+ }
+ },
+
+ _onMouseClick: function (e) {
+ if (this._map.dragging && this._map.dragging.moved()) { return; }
+
+ this._fireMouseEvent(e);
+ },
+
+ _fireMouseEvent: function (e) {
+ if (!this.hasEventListeners(e.type)) { return; }
+
+ var map = this._map,
+ containerPoint = map.mouseEventToContainerPoint(e),
+ layerPoint = map.containerPointToLayerPoint(containerPoint),
+ latlng = map.layerPointToLatLng(layerPoint);
+
+ this.fire(e.type, {
+ latlng: latlng,
+ layerPoint: layerPoint,
+ containerPoint: containerPoint,
+ originalEvent: e
+ });
+
+ if (e.type === 'contextmenu') {
+ L.DomEvent.preventDefault(e);
+ }
+ if (e.type !== 'mousemove') {
+ L.DomEvent.stopPropagation(e);
+ }
+ }
+});
+
+L.Map.include({
+ _initPathRoot: function () {
+ if (!this._pathRoot) {
+ this._pathRoot = L.Path.prototype._createElement('svg');
+ this._panes.overlayPane.appendChild(this._pathRoot);
+
+ if (this.options.zoomAnimation && L.Browser.any3d) {
+ this._pathRoot.setAttribute('class', ' leaflet-zoom-animated');
+
+ this.on({
+ 'zoomanim': this._animatePathZoom,
+ 'zoomend': this._endPathZoom
+ });
+ } else {
+ this._pathRoot.setAttribute('class', ' leaflet-zoom-hide');
+ }
+
+ this.on('moveend', this._updateSvgViewport);
+ this._updateSvgViewport();
+ }
+ },
+
+ _animatePathZoom: function (e) {
+ var scale = this.getZoomScale(e.zoom),
+ offset = this._getCenterOffset(e.center)._multiplyBy(-scale)._add(this._pathViewport.min);
+
+ this._pathRoot.style[L.DomUtil.TRANSFORM] =
+ L.DomUtil.getTranslateString(offset) + ' scale(' + scale + ') ';
+
+ this._pathZooming = true;
+ },
+
+ _endPathZoom: function () {
+ this._pathZooming = false;
+ },
+
+ _updateSvgViewport: function () {
+
+ if (this._pathZooming) {
+ // Do not update SVGs while a zoom animation is going on otherwise the animation will break.
+ // When the zoom animation ends we will be updated again anyway
+ // This fixes the case where you do a momentum move and zoom while the move is still ongoing.
+ return;
+ }
+
+ this._updatePathViewport();
+
+ var vp = this._pathViewport,
+ min = vp.min,
+ max = vp.max,
+ width = max.x - min.x,
+ height = max.y - min.y,
+ root = this._pathRoot,
+ pane = this._panes.overlayPane;
+
+ // Hack to make flicker on drag end on mobile webkit less irritating
+ if (L.Browser.mobileWebkit) {
+ pane.removeChild(root);
+ }
+
+ L.DomUtil.setPosition(root, min);
+ root.setAttribute('width', width);
+ root.setAttribute('height', height);
+ root.setAttribute('viewBox', [min.x, min.y, width, height].join(' '));
+
+ if (L.Browser.mobileWebkit) {
+ pane.appendChild(root);
+ }
+ }
+});
+
+
+/*
+ * Popup extension to L.Path (polylines, polygons, circles), adding popup-related methods.
+ */
+
+L.Path.include({
+
+ bindPopup: function (content, options) {
+
+ if (!this._popup || options) {
+ this._popup = new L.Popup(options, this);
+ }
+
+ this._popup.setContent(content);
+
+ if (!this._popupHandlersAdded) {
+ this
+ .on('click', this._openPopup, this)
+ .on('remove', this.closePopup, this);
+
+ this._popupHandlersAdded = true;
+ }
+
+ return this;
+ },
+
+ unbindPopup: function () {
+ if (this._popup) {
+ this._popup = null;
+ this
+ .off('click', this._openPopup)
+ .off('remove', this.closePopup);
+
+ this._popupHandlersAdded = false;
+ }
+ return this;
+ },
+
+ openPopup: function (latlng) {
+
+ if (this._popup) {
+ // open the popup from one of the path's points if not specified
+ latlng = latlng || this._latlng ||
+ this._latlngs[Math.floor(this._latlngs.length / 2)];
+
+ this._openPopup({latlng: latlng});
+ }
+
+ return this;
+ },
+
+ closePopup: function () {
+ if (this._popup) {
+ this._popup._close();
+ }
+ return this;
+ },
+
+ _openPopup: function (e) {
+ this._popup.setLatLng(e.latlng);
+ this._map.openPopup(this._popup);
+ }
+});
+
+
+/*
+ * Vector rendering for IE6-8 through VML.
+ * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
+ */
+
+L.Browser.vml = !L.Browser.svg && (function () {
+ try {
+ var div = document.createElement('div');
+ div.innerHTML = '<v:shape adj="1"/>';
+
+ var shape = div.firstChild;
+ shape.style.behavior = 'url(#default#VML)';
+
+ return shape && (typeof shape.adj === 'object');
+
+ } catch (e) {
+ return false;
+ }
+}());
+
+L.Path = L.Browser.svg || !L.Browser.vml ? L.Path : L.Path.extend({
+ statics: {
+ VML: true,
+ CLIP_PADDING: 0.02
+ },
+
+ _createElement: (function () {
+ try {
+ document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
+ return function (name) {
+ return document.createElement('<lvml:' + name + ' class="lvml">');
+ };
+ } catch (e) {
+ return function (name) {
+ return document.createElement(
+ '<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
+ };
+ }
+ }()),
+
+ _initPath: function () {
+ var container = this._container = this._createElement('shape');
+ L.DomUtil.addClass(container, 'leaflet-vml-shape');
+ if (this.options.clickable) {
+ L.DomUtil.addClass(container, 'leaflet-clickable');
+ }
+ container.coordsize = '1 1';
+
+ this._path = this._createElement('path');
+ container.appendChild(this._path);
+
+ this._map._pathRoot.appendChild(container);
+ },
+
+ _initStyle: function () {
+ this._updateStyle();
+ },
+
+ _updateStyle: function () {
+ var stroke = this._stroke,
+ fill = this._fill,
+ options = this.options,
+ container = this._container;
+
+ container.stroked = options.stroke;
+ container.filled = options.fill;
+
+ if (options.stroke) {
+ if (!stroke) {
+ stroke = this._stroke = this._createElement('stroke');
+ stroke.endcap = 'round';
+ container.appendChild(stroke);
+ }
+ stroke.weight = options.weight + 'px';
+ stroke.color = options.color;
+ stroke.opacity = options.opacity;
+
+ if (options.dashArray) {
+ stroke.dashStyle = options.dashArray instanceof Array ?
+ options.dashArray.join(' ') :
+ options.dashArray.replace(/ *, */g, ' ');
+ } else {
+ stroke.dashStyle = '';
+ }
+
+ } else if (stroke) {
+ container.removeChild(stroke);
+ this._stroke = null;
+ }
+
+ if (options.fill) {
+ if (!fill) {
+ fill = this._fill = this._createElement('fill');
+ container.appendChild(fill);
+ }
+ fill.color = options.fillColor || options.color;
+ fill.opacity = options.fillOpacity;
+
+ } else if (fill) {
+ container.removeChild(fill);
+ this._fill = null;
+ }
+ },
+
+ _updatePath: function () {
+ var style = this._container.style;
+
+ style.display = 'none';
+ this._path.v = this.getPathString() + ' '; // the space fixes IE empty path string bug
+ style.display = '';
+ }
+});
+
+L.Map.include(L.Browser.svg || !L.Browser.vml ? {} : {
+ _initPathRoot: function () {
+ if (this._pathRoot) { return; }
+
+ var root = this._pathRoot = document.createElement('div');
+ root.className = 'leaflet-vml-container';
+ this._panes.overlayPane.appendChild(root);
+
+ this.on('moveend', this._updatePathViewport);
+ this._updatePathViewport();
+ }
+});
+
+
+/*
+ * Vector rendering for all browsers that support canvas.
+ */
+
+L.Browser.canvas = (function () {
+ return !!document.createElement('canvas').getContext;
+}());
+
+L.Path = (L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? L.Path : L.Path.extend({
+ statics: {
+ //CLIP_PADDING: 0.02, // not sure if there's a need to set it to a small value
+ CANVAS: true,
+ SVG: false
+ },
+
+ redraw: function () {
+ if (this._map) {
+ this.projectLatlngs();
+ this._requestUpdate();
+ }
+ return this;
+ },
+
+ setStyle: function (style) {
+ L.setOptions(this, style);
+
+ if (this._map) {
+ this._updateStyle();
+ this._requestUpdate();
+ }
+ return this;
+ },
+
+ onRemove: function (map) {
+ map
+ .off('viewreset', this.projectLatlngs, this)
+ .off('moveend', this._updatePath, this);
+
+ if (this.options.clickable) {
+ this._map.off('click', this._onClick, this);
+ }
+
+ this._requestUpdate();
+
+ this._map = null;
+ },
+
+ _requestUpdate: function () {
+ if (this._map && !L.Path._updateRequest) {
+ L.Path._updateRequest = L.Util.requestAnimFrame(this._fireMapMoveEnd, this._map);
+ }
+ },
+
+ _fireMapMoveEnd: function () {
+ L.Path._updateRequest = null;
+ this.fire('moveend');
+ },
+
+ _initElements: function () {
+ this._map._initPathRoot();
+ this._ctx = this._map._canvasCtx;
+ },
+
+ _updateStyle: function () {
+ var options = this.options;
+
+ if (options.stroke) {
+ this._ctx.lineWidth = options.weight;
+ this._ctx.strokeStyle = options.color;
+ }
+ if (options.fill) {
+ this._ctx.fillStyle = options.fillColor || options.color;
+ }
+ },
+
+ _drawPath: function () {
+ var i, j, len, len2, point, drawMethod;
+
+ this._ctx.beginPath();
+
+ for (i = 0, len = this._parts.length; i < len; i++) {
+ for (j = 0, len2 = this._parts[i].length; j < len2; j++) {
+ point = this._parts[i][j];
+ drawMethod = (j === 0 ? 'move' : 'line') + 'To';
+
+ this._ctx[drawMethod](point.x, point.y);
+ }
+ // TODO refactor ugly hack
+ if (this instanceof L.Polygon) {
+ this._ctx.closePath();
+ }
+ }
+ },
+
+ _checkIfEmpty: function () {
+ return !this._parts.length;
+ },
+
+ _updatePath: function () {
+ if (this._checkIfEmpty()) { return; }
+
+ var ctx = this._ctx,
+ options = this.options;
+
+ this._drawPath();
+ ctx.save();
+ this._updateStyle();
+
+ if (options.fill) {
+ ctx.globalAlpha = options.fillOpacity;
+ ctx.fill();
+ }
+
+ if (options.stroke) {
+ ctx.globalAlpha = options.opacity;
+ ctx.stroke();
+ }
+
+ ctx.restore();
+
+ // TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
+ },
+
+ _initEvents: function () {
+ if (this.options.clickable) {
+ // TODO hand cursor
+ // TODO mouseover, mouseout, dblclick
+ this._map.on('click', this._onClick, this);
+ }
+ },
+
+ _onClick: function (e) {
+ if (this._containsPoint(e.layerPoint)) {
+ this.fire('click', {
+ latlng: e.latlng,
+ layerPoint: e.layerPoint,
+ containerPoint: e.containerPoint,
+ originalEvent: e
+ });
+ }
+ }
+});
+
+L.Map.include((L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? {} : {
+ _initPathRoot: function () {
+ var root = this._pathRoot,
+ ctx;
+
+ if (!root) {
+ root = this._pathRoot = document.createElement("canvas");
+ root.style.position = 'absolute';
+ ctx = this._canvasCtx = root.getContext('2d');
+
+ ctx.lineCap = "round";
+ ctx.lineJoin = "round";
+
+ this._panes.overlayPane.appendChild(root);
+
+ if (this.options.zoomAnimation) {
+ this._pathRoot.className = 'leaflet-zoom-animated';
+ this.on('zoomanim', this._animatePathZoom);
+ this.on('zoomend', this._endPathZoom);
+ }
+ this.on('moveend', this._updateCanvasViewport);
+ this._updateCanvasViewport();
+ }
+ },
+
+ _updateCanvasViewport: function () {
+ // don't redraw while zooming. See _updateSvgViewport for more details
+ if (this._pathZooming) { return; }
+ this._updatePathViewport();
+
+ var vp = this._pathViewport,
+ min = vp.min,
+ size = vp.max.subtract(min),
+ root = this._pathRoot;
+
+ //TODO check if this works properly on mobile webkit
+ L.DomUtil.setPosition(root, min);
+ root.width = size.x;
+ root.height = size.y;
+ root.getContext('2d').translate(-min.x, -min.y);
+ }
+});
+
+
+/*
+ * L.LineUtil contains different utility functions for line segments
+ * and polylines (clipping, simplification, distances, etc.)
+ */
+
+/*jshint bitwise:false */ // allow bitwise oprations for this file
+
+L.LineUtil = {
+
+ // Simplify polyline with vertex reduction and Douglas-Peucker simplification.
+ // Improves rendering performance dramatically by lessening the number of points to draw.
+
+ simplify: function (/*Point[]*/ points, /*Number*/ tolerance) {
+ if (!tolerance || !points.length) {
+ return points.slice();
+ }
+
+ var sqTolerance = tolerance * tolerance;
+
+ // stage 1: vertex reduction
+ points = this._reducePoints(points, sqTolerance);
+
+ // stage 2: Douglas-Peucker simplification
+ points = this._simplifyDP(points, sqTolerance);
+
+ return points;
+ },
+
+ // distance from a point to a segment between two points
+ pointToSegmentDistance: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) {
+ return Math.sqrt(this._sqClosestPointOnSegment(p, p1, p2, true));
+ },
+
+ closestPointOnSegment: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) {
+ return this._sqClosestPointOnSegment(p, p1, p2);
+ },
+
+ // Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm
+ _simplifyDP: function (points, sqTolerance) {
+
+ var len = points.length,
+ ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
+ markers = new ArrayConstructor(len);
+
+ markers[0] = markers[len - 1] = 1;
+
+ this._simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
+
+ var i,
+ newPoints = [];
+
+ for (i = 0; i < len; i++) {
+ if (markers[i]) {
+ newPoints.push(points[i]);
+ }
+ }
+
+ return newPoints;
+ },
+
+ _simplifyDPStep: function (points, markers, sqTolerance, first, last) {
+
+ var maxSqDist = 0,
+ index, i, sqDist;
+
+ for (i = first + 1; i <= last - 1; i++) {
+ sqDist = this._sqClosestPointOnSegment(points[i], points[first], points[last], true);
+
+ if (sqDist > maxSqDist) {
+ index = i;
+ maxSqDist = sqDist;
+ }
+ }
+
+ if (maxSqDist > sqTolerance) {
+ markers[index] = 1;
+
+ this._simplifyDPStep(points, markers, sqTolerance, first, index);
+ this._simplifyDPStep(points, markers, sqTolerance, index, last);
+ }
+ },
+
+ // reduce points that are too close to each other to a single point
+ _reducePoints: function (points, sqTolerance) {
+ var reducedPoints = [points[0]];
+
+ for (var i = 1, prev = 0, len = points.length; i < len; i++) {
+ if (this._sqDist(points[i], points[prev]) > sqTolerance) {
+ reducedPoints.push(points[i]);
+ prev = i;
+ }
+ }
+ if (prev < len - 1) {
+ reducedPoints.push(points[len - 1]);
+ }
+ return reducedPoints;
+ },
+
+ // Cohen-Sutherland line clipping algorithm.
+ // Used to avoid rendering parts of a polyline that are not currently visible.
+
+ clipSegment: function (a, b, bounds, useLastCode) {
+ var codeA = useLastCode ? this._lastCode : this._getBitCode(a, bounds),
+ codeB = this._getBitCode(b, bounds),
+
+ codeOut, p, newCode;
+
+ // save 2nd code to avoid calculating it on the next segment
+ this._lastCode = codeB;
+
+ while (true) {
+ // if a,b is inside the clip window (trivial accept)
+ if (!(codeA | codeB)) {
+ return [a, b];
+ // if a,b is outside the clip window (trivial reject)
+ } else if (codeA & codeB) {
+ return false;
+ // other cases
+ } else {
+ codeOut = codeA || codeB,
+ p = this._getEdgeIntersection(a, b, codeOut, bounds),
+ newCode = this._getBitCode(p, bounds);
+
+ if (codeOut === codeA) {
+ a = p;
+ codeA = newCode;
+ } else {
+ b = p;
+ codeB = newCode;
+ }
+ }
+ }
+ },
+
+ _getEdgeIntersection: function (a, b, code, bounds) {
+ var dx = b.x - a.x,
+ dy = b.y - a.y,
+ min = bounds.min,
+ max = bounds.max;
+
+ if (code & 8) { // top
+ return new L.Point(a.x + dx * (max.y - a.y) / dy, max.y);
+ } else if (code & 4) { // bottom
+ return new L.Point(a.x + dx * (min.y - a.y) / dy, min.y);
+ } else if (code & 2) { // right
+ return new L.Point(max.x, a.y + dy * (max.x - a.x) / dx);
+ } else if (code & 1) { // left
+ return new L.Point(min.x, a.y + dy * (min.x - a.x) / dx);
+ }
+ },
+
+ _getBitCode: function (/*Point*/ p, bounds) {
+ var code = 0;
+
+ if (p.x < bounds.min.x) { // left
+ code |= 1;
+ } else if (p.x > bounds.max.x) { // right
+ code |= 2;
+ }
+ if (p.y < bounds.min.y) { // bottom
+ code |= 4;
+ } else if (p.y > bounds.max.y) { // top
+ code |= 8;
+ }
+
+ return code;
+ },
+
+ // square distance (to avoid unnecessary Math.sqrt calls)
+ _sqDist: function (p1, p2) {
+ var dx = p2.x - p1.x,
+ dy = p2.y - p1.y;
+ return dx * dx + dy * dy;
+ },
+
+ // return closest point on segment or distance to that point
+ _sqClosestPointOnSegment: function (p, p1, p2, sqDist) {
+ var x = p1.x,
+ y = p1.y,
+ dx = p2.x - x,
+ dy = p2.y - y,
+ dot = dx * dx + dy * dy,
+ t;
+
+ if (dot > 0) {
+ t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
+
+ if (t > 1) {
+ x = p2.x;
+ y = p2.y;
+ } else if (t > 0) {
+ x += dx * t;
+ y += dy * t;
+ }
+ }
+
+ dx = p.x - x;
+ dy = p.y - y;
+
+ return sqDist ? dx * dx + dy * dy : new L.Point(x, y);
+ }
+};
+
+
+/*
+ * L.Polygon is used to display polylines on a map.
+ */
+
+L.Polyline = L.Path.extend({
+ initialize: function (latlngs, options) {
+ L.Path.prototype.initialize.call(this, options);
+
+ this._latlngs = this._convertLatLngs(latlngs);
+ },
+
+ options: {
+ // how much to simplify the polyline on each zoom level
+ // more = better performance and smoother look, less = more accurate
+ smoothFactor: 1.0,
+ noClip: false
+ },
+
+ projectLatlngs: function () {
+ this._originalPoints = [];
+
+ for (var i = 0, len = this._latlngs.length; i < len; i++) {
+ this._originalPoints[i] = this._map.latLngToLayerPoint(this._latlngs[i]);
+ }
+ },
+
+ getPathString: function () {
+ for (var i = 0, len = this._parts.length, str = ''; i < len; i++) {
+ str += this._getPathPartStr(this._parts[i]);
+ }
+ return str;
+ },
+
+ getLatLngs: function () {
+ return this._latlngs;
+ },
+
+ setLatLngs: function (latlngs) {
+ this._latlngs = this._convertLatLngs(latlngs);
+ return this.redraw();
+ },
+
+ addLatLng: function (latlng) {
+ this._latlngs.push(L.latLng(latlng));
+ return this.redraw();
+ },
+
+ spliceLatLngs: function () { // (Number index, Number howMany)
+ var removed = [].splice.apply(this._latlngs, arguments);
+ this._convertLatLngs(this._latlngs);
+ this.redraw();
+ return removed;
+ },
+
+ closestLayerPoint: function (p) {
+ var minDistance = Infinity, parts = this._parts, p1, p2, minPoint = null;
+
+ for (var j = 0, jLen = parts.length; j < jLen; j++) {
+ var points = parts[j];
+ for (var i = 1, len = points.length; i < len; i++) {
+ p1 = points[i - 1];
+ p2 = points[i];
+ var sqDist = L.LineUtil._sqClosestPointOnSegment(p, p1, p2, true);
+ if (sqDist < minDistance) {
+ minDistance = sqDist;
+ minPoint = L.LineUtil._sqClosestPointOnSegment(p, p1, p2);
+ }
+ }
+ }
+ if (minPoint) {
+ minPoint.distance = Math.sqrt(minDistance);
+ }
+ return minPoint;
+ },
+
+ getBounds: function () {
+ var bounds = new L.LatLngBounds(),
+ latLngs = this.getLatLngs(),
+ i, len;
+
+ for (i = 0, len = latLngs.length; i < len; i++) {
+ bounds.extend(latLngs[i]);
+ }
+
+ return bounds;
+ },
+
+ _convertLatLngs: function (latlngs) {
+ var i, len;
+ for (i = 0, len = latlngs.length; i < len; i++) {
+ if (L.Util.isArray(latlngs[i]) && typeof latlngs[i][0] !== 'number') {
+ return;
+ }
+ latlngs[i] = L.latLng(latlngs[i]);
+ }
+ return latlngs;
+ },
+
+ _initEvents: function () {
+ L.Path.prototype._initEvents.call(this);
+ },
+
+ _getPathPartStr: function (points) {
+ var round = L.Path.VML;
+
+ for (var j = 0, len2 = points.length, str = '', p; j < len2; j++) {
+ p = points[j];
+ if (round) {
+ p._round();
+ }
+ str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
+ }
+ return str;
+ },
+
+ _clipPoints: function () {
+ var points = this._originalPoints,
+ len = points.length,
+ i, k, segment;
+
+ if (this.options.noClip) {
+ this._parts = [points];
+ return;
+ }
+
+ this._parts = [];
+
+ var parts = this._parts,
+ vp = this._map._pathViewport,
+ lu = L.LineUtil;
+
+ for (i = 0, k = 0; i < len - 1; i++) {
+ segment = lu.clipSegment(points[i], points[i + 1], vp, i);
+ if (!segment) {
+ continue;
+ }
+
+ parts[k] = parts[k] || [];
+ parts[k].push(segment[0]);
+
+ // if segment goes out of screen, or it's the last one, it's the end of the line part
+ if ((segment[1] !== points[i + 1]) || (i === len - 2)) {
+ parts[k].push(segment[1]);
+ k++;
+ }
+ }
+ },
+
+ // simplify each clipped part of the polyline
+ _simplifyPoints: function () {
+ var parts = this._parts,
+ lu = L.LineUtil;
+
+ for (var i = 0, len = parts.length; i < len; i++) {
+ parts[i] = lu.simplify(parts[i], this.options.smoothFactor);
+ }
+ },
+
+ _updatePath: function () {
+ if (!this._map) { return; }
+
+ this._clipPoints();
+ this._simplifyPoints();
+
+ L.Path.prototype._updatePath.call(this);
+ }
+});
+
+L.polyline = function (latlngs, options) {
+ return new L.Polyline(latlngs, options);
+};
+
+
+/*
+ * L.PolyUtil contains utility functions for polygons (clipping, etc.).
+ */
+
+/*jshint bitwise:false */ // allow bitwise operations here
+
+L.PolyUtil = {};
+
+/*
+ * Sutherland-Hodgeman polygon clipping algorithm.
+ * Used to avoid rendering parts of a polygon that are not currently visible.
+ */
+L.PolyUtil.clipPolygon = function (points, bounds) {
+ var clippedPoints,
+ edges = [1, 4, 2, 8],
+ i, j, k,
+ a, b,
+ len, edge, p,
+ lu = L.LineUtil;
+
+ for (i = 0, len = points.length; i < len; i++) {
+ points[i]._code = lu._getBitCode(points[i], bounds);
+ }
+
+ // for each edge (left, bottom, right, top)
+ for (k = 0; k < 4; k++) {
+ edge = edges[k];
+ clippedPoints = [];
+
+ for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
+ a = points[i];
+ b = points[j];
+
+ // if a is inside the clip window
+ if (!(a._code & edge)) {
+ // if b is outside the clip window (a->b goes out of screen)
+ if (b._code & edge) {
+ p = lu._getEdgeIntersection(b, a, edge, bounds);
+ p._code = lu._getBitCode(p, bounds);
+ clippedPoints.push(p);
+ }
+ clippedPoints.push(a);
+
+ // else if b is inside the clip window (a->b enters the screen)
+ } else if (!(b._code & edge)) {
+ p = lu._getEdgeIntersection(b, a, edge, bounds);
+ p._code = lu._getBitCode(p, bounds);
+ clippedPoints.push(p);
+ }
+ }
+ points = clippedPoints;
+ }
+
+ return points;
+};
+
+
+/*
+ * L.Polygon is used to display polygons on a map.
+ */
+
+L.Polygon = L.Polyline.extend({
+ options: {
+ fill: true
+ },
+
+ initialize: function (latlngs, options) {
+ L.Polyline.prototype.initialize.call(this, latlngs, options);
+
+ if (latlngs && L.Util.isArray(latlngs[0]) && (typeof latlngs[0][0] !== 'number')) {
+ this._latlngs = this._convertLatLngs(latlngs[0]);
+ this._holes = latlngs.slice(1);
+ }
+ },
+
+ projectLatlngs: function () {
+ L.Polyline.prototype.projectLatlngs.call(this);
+
+ // project polygon holes points
+ // TODO move this logic to Polyline to get rid of duplication
+ this._holePoints = [];
+
+ if (!this._holes) { return; }
+
+ var i, j, len, len2;
+
+ for (i = 0, len = this._holes.length; i < len; i++) {
+ this._holePoints[i] = [];
+
+ for (j = 0, len2 = this._holes[i].length; j < len2; j++) {
+ this._holePoints[i][j] = this._map.latLngToLayerPoint(this._holes[i][j]);
+ }
+ }
+ },
+
+ _clipPoints: function () {
+ var points = this._originalPoints,
+ newParts = [];
+
+ this._parts = [points].concat(this._holePoints);
+
+ if (this.options.noClip) { return; }
+
+ for (var i = 0, len = this._parts.length; i < len; i++) {
+ var clipped = L.PolyUtil.clipPolygon(this._parts[i], this._map._pathViewport);
+ if (clipped.length) {
+ newParts.push(clipped);
+ }
+ }
+
+ this._parts = newParts;
+ },
+
+ _getPathPartStr: function (points) {
+ var str = L.Polyline.prototype._getPathPartStr.call(this, points);
+ return str + (L.Browser.svg ? 'z' : 'x');
+ }
+});
+
+L.polygon = function (latlngs, options) {
+ return new L.Polygon(latlngs, options);
+};
+
+
+/*
+ * Contains L.MultiPolyline and L.MultiPolygon layers.
+ */
+
+(function () {
+ function createMulti(Klass) {
+
+ return L.FeatureGroup.extend({
+
+ initialize: function (latlngs, options) {
+ this._layers = {};
+ this._options = options;
+ this.setLatLngs(latlngs);
+ },
+
+ setLatLngs: function (latlngs) {
+ var i = 0,
+ len = latlngs.length;
+
+ this.eachLayer(function (layer) {
+ if (i < len) {
+ layer.setLatLngs(latlngs[i++]);
+ } else {
+ this.removeLayer(layer);
+ }
+ }, this);
+
+ while (i < len) {
+ this.addLayer(new Klass(latlngs[i++], this._options));
+ }
+
+ return this;
+ }
+ });
+ }
+
+ L.MultiPolyline = createMulti(L.Polyline);
+ L.MultiPolygon = createMulti(L.Polygon);
+
+ L.multiPolyline = function (latlngs, options) {
+ return new L.MultiPolyline(latlngs, options);
+ };
+
+ L.multiPolygon = function (latlngs, options) {
+ return new L.MultiPolygon(latlngs, options);
+ };
+}());
+
+
+/*
+ * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object.
+ */
+
+L.Rectangle = L.Polygon.extend({
+ initialize: function (latLngBounds, options) {
+ L.Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
+ },
+
+ setBounds: function (latLngBounds) {
+ this.setLatLngs(this._boundsToLatLngs(latLngBounds));
+ },
+
+ _boundsToLatLngs: function (latLngBounds) {
+ latLngBounds = L.latLngBounds(latLngBounds);
+ return [
+ latLngBounds.getSouthWest(),
+ latLngBounds.getNorthWest(),
+ latLngBounds.getNorthEast(),
+ latLngBounds.getSouthEast()
+ ];
+ }
+});
+
+L.rectangle = function (latLngBounds, options) {
+ return new L.Rectangle(latLngBounds, options);
+};
+
+
+/*
+ * L.Circle is a circle overlay (with a certain radius in meters).
+ */
+
+L.Circle = L.Path.extend({
+ initialize: function (latlng, radius, options) {
+ L.Path.prototype.initialize.call(this, options);
+
+ this._latlng = L.latLng(latlng);
+ this._mRadius = radius;
+ },
+
+ options: {
+ fill: true
+ },
+
+ setLatLng: function (latlng) {
+ this._latlng = L.latLng(latlng);
+ return this.redraw();
+ },
+
+ setRadius: function (radius) {
+ this._mRadius = radius;
+ return this.redraw();
+ },
+
+ projectLatlngs: function () {
+ var lngRadius = this._getLngRadius(),
+ latlng2 = new L.LatLng(this._latlng.lat, this._latlng.lng - lngRadius),
+ point2 = this._map.latLngToLayerPoint(latlng2);
+
+ this._point = this._map.latLngToLayerPoint(this._latlng);
+ this._radius = Math.max(Math.round(this._point.x - point2.x), 1);
+ },
+
+ getBounds: function () {
+ var lngRadius = this._getLngRadius(),
+ latRadius = (this._mRadius / 40075017) * 360,
+ latlng = this._latlng,
+ sw = new L.LatLng(latlng.lat - latRadius, latlng.lng - lngRadius),
+ ne = new L.LatLng(latlng.lat + latRadius, latlng.lng + lngRadius);
+
+ return new L.LatLngBounds(sw, ne);
+ },
+
+ getLatLng: function () {
+ return this._latlng;
+ },
+
+ getPathString: function () {
+ var p = this._point,
+ r = this._radius;
+
+ if (this._checkIfEmpty()) {
+ return '';
+ }
+
+ if (L.Browser.svg) {
+ return "M" + p.x + "," + (p.y - r) +
+ "A" + r + "," + r + ",0,1,1," +
+ (p.x - 0.1) + "," + (p.y - r) + " z";
+ } else {
+ p._round();
+ r = Math.round(r);
+ return "AL " + p.x + "," + p.y + " " + r + "," + r + " 0," + (65535 * 360);
+ }
+ },
+
+ getRadius: function () {
+ return this._mRadius;
+ },
+
+ // TODO Earth hardcoded, move into projection code!
+
+ _getLatRadius: function () {
+ return (this._mRadius / 40075017) * 360;
+ },
+
+ _getLngRadius: function () {
+ return this._getLatRadius() / Math.cos(L.LatLng.DEG_TO_RAD * this._latlng.lat);
+ },
+
+ _checkIfEmpty: function () {
+ if (!this._map) {
+ return false;
+ }
+ var vp = this._map._pathViewport,
+ r = this._radius,
+ p = this._point;
+
+ return p.x - r > vp.max.x || p.y - r > vp.max.y ||
+ p.x + r < vp.min.x || p.y + r < vp.min.y;
+ }
+});
+
+L.circle = function (latlng, radius, options) {
+ return new L.Circle(latlng, radius, options);
+};
+
+
+/*
+ * L.CircleMarker is a circle overlay with a permanent pixel radius.
+ */
+
+L.CircleMarker = L.Circle.extend({
+ options: {
+ radius: 10,
+ weight: 2
+ },
+
+ initialize: function (latlng, options) {
+ L.Circle.prototype.initialize.call(this, latlng, null, options);
+ this._radius = this.options.radius;
+ },
+
+ projectLatlngs: function () {
+ this._point = this._map.latLngToLayerPoint(this._latlng);
+ },
+
+ _updateStyle : function () {
+ L.Circle.prototype._updateStyle.call(this);
+ this.setRadius(this.options.radius);
+ },
+
+ setRadius: function (radius) {
+ this.options.radius = this._radius = radius;
+ return this.redraw();
+ }
+});
+
+L.circleMarker = function (latlng, options) {
+ return new L.CircleMarker(latlng, options);
+};
+
+
+/*
+ * Extends L.Polyline to be able to manually detect clicks on Canvas-rendered polylines.
+ */
+
+L.Polyline.include(!L.Path.CANVAS ? {} : {
+ _containsPoint: function (p, closed) {
+ var i, j, k, len, len2, dist, part,
+ w = this.options.weight / 2;
+
+ if (L.Browser.touch) {
+ w += 10; // polyline click tolerance on touch devices
+ }
+
+ for (i = 0, len = this._parts.length; i < len; i++) {
+ part = this._parts[i];
+ for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
+ if (!closed && (j === 0)) {
+ continue;
+ }
+
+ dist = L.LineUtil.pointToSegmentDistance(p, part[k], part[j]);
+
+ if (dist <= w) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+});
+
+
+/*
+ * Extends L.Polygon to be able to manually detect clicks on Canvas-rendered polygons.
+ */
+
+L.Polygon.include(!L.Path.CANVAS ? {} : {
+ _containsPoint: function (p) {
+ var inside = false,
+ part, p1, p2,
+ i, j, k,
+ len, len2;
+
+ // TODO optimization: check if within bounds first
+
+ if (L.Polyline.prototype._containsPoint.call(this, p, true)) {
+ // click on polygon border
+ return true;
+ }
+
+ // ray casting algorithm for detecting if point is in polygon
+
+ for (i = 0, len = this._parts.length; i < len; i++) {
+ part = this._parts[i];
+
+ for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
+ p1 = part[j];
+ p2 = part[k];
+
+ if (((p1.y > p.y) !== (p2.y > p.y)) &&
+ (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) {
+ inside = !inside;
+ }
+ }
+ }
+
+ return inside;
+ }
+});
+
+
+/*
+ * Extends L.Circle with Canvas-specific code.
+ */
+
+L.Circle.include(!L.Path.CANVAS ? {} : {
+ _drawPath: function () {
+ var p = this._point;
+ this._ctx.beginPath();
+ this._ctx.arc(p.x, p.y, this._radius, 0, Math.PI * 2, false);
+ },
+
+ _containsPoint: function (p) {
+ var center = this._point,
+ w2 = this.options.stroke ? this.options.weight / 2 : 0;
+
+ return (p.distanceTo(center) <= this._radius + w2);
+ }
+});
+
+
+/*
+ * L.GeoJSON turns any GeoJSON data into a Leaflet layer.
+ */
+
+L.GeoJSON = L.FeatureGroup.extend({
+
+ initialize: function (geojson, options) {
+ L.setOptions(this, options);
+
+ this._layers = {};
+
+ if (geojson) {
+ this.addData(geojson);
+ }
+ },
+
+ addData: function (geojson) {
+ var features = L.Util.isArray(geojson) ? geojson : geojson.features,
+ i, len;
+
+ if (features) {
+ for (i = 0, len = features.length; i < len; i++) {
+ // Only add this if geometry or geometries are set and not null
+ if (features[i].geometries || features[i].geometry || features[i].features) {
+ this.addData(features[i]);
+ }
+ }
+ return this;
+ }
+
+ var options = this.options;
+
+ if (options.filter && !options.filter(geojson)) { return; }
+
+ var layer = L.GeoJSON.geometryToLayer(geojson, options.pointToLayer);
+ layer.feature = geojson;
+
+ layer.defaultOptions = layer.options;
+ this.resetStyle(layer);
+
+ if (options.onEachFeature) {
+ options.onEachFeature(geojson, layer);
+ }
+
+ return this.addLayer(layer);
+ },
+
+ resetStyle: function (layer) {
+ var style = this.options.style;
+ if (style) {
+ // reset any custom styles
+ L.Util.extend(layer.options, layer.defaultOptions);
+
+ this._setLayerStyle(layer, style);
+ }
+ },
+
+ setStyle: function (style) {
+ this.eachLayer(function (layer) {
+ this._setLayerStyle(layer, style);
+ }, this);
+ },
+
+ _setLayerStyle: function (layer, style) {
+ if (typeof style === 'function') {
+ style = style(layer.feature);
+ }
+ if (layer.setStyle) {
+ layer.setStyle(style);
+ }
+ }
+});
+
+L.extend(L.GeoJSON, {
+ geometryToLayer: function (geojson, pointToLayer) {
+ var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
+ coords = geometry.coordinates,
+ layers = [],
+ latlng, latlngs, i, len, layer;
+
+ switch (geometry.type) {
+ case 'Point':
+ latlng = this.coordsToLatLng(coords);
+ return pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng);
+
+ case 'MultiPoint':
+ for (i = 0, len = coords.length; i < len; i++) {
+ latlng = this.coordsToLatLng(coords[i]);
+ layer = pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng);
+ layers.push(layer);
+ }
+ return new L.FeatureGroup(layers);
+
+ case 'LineString':
+ latlngs = this.coordsToLatLngs(coords);
+ return new L.Polyline(latlngs);
+
+ case 'Polygon':
+ latlngs = this.coordsToLatLngs(coords, 1);
+ return new L.Polygon(latlngs);
+
+ case 'MultiLineString':
+ latlngs = this.coordsToLatLngs(coords, 1);
+ return new L.MultiPolyline(latlngs);
+
+ case 'MultiPolygon':
+ latlngs = this.coordsToLatLngs(coords, 2);
+ return new L.MultiPolygon(latlngs);
+
+ case 'GeometryCollection':
+ for (i = 0, len = geometry.geometries.length; i < len; i++) {
+ layer = this.geometryToLayer({
+ geometry: geometry.geometries[i],
+ type: 'Feature',
+ properties: geojson.properties
+ }, pointToLayer);
+ layers.push(layer);
+ }
+ return new L.FeatureGroup(layers);
+
+ default:
+ throw new Error('Invalid GeoJSON object.');
+ }
+ },
+
+ coordsToLatLng: function (coords, reverse) { // (Array, Boolean) -> LatLng
+ var lat = parseFloat(coords[reverse ? 0 : 1]),
+ lng = parseFloat(coords[reverse ? 1 : 0]);
+
+ return new L.LatLng(lat, lng);
+ },
+
+ coordsToLatLngs: function (coords, levelsDeep, reverse) { // (Array, Number, Boolean) -> Array
+ var latlng,
+ latlngs = [],
+ i, len;
+
+ for (i = 0, len = coords.length; i < len; i++) {
+ latlng = levelsDeep ?
+ this.coordsToLatLngs(coords[i], levelsDeep - 1, reverse) :
+ this.coordsToLatLng(coords[i], reverse);
+
+ latlngs.push(latlng);
+ }
+
+ return latlngs;
+ }
+});
+
+L.geoJson = function (geojson, options) {
+ return new L.GeoJSON(geojson, options);
+};
+
+
+/*
+ * L.DomEvent contains functions for working with DOM events.
+ */
+
+L.DomEvent = {
+ /* inspired by John Resig, Dean Edwards and YUI addEvent implementations */
+ addListener: function (obj, type, fn, context) { // (HTMLElement, String, Function[, Object])
+
+ var id = L.stamp(fn),
+ key = '_leaflet_' + type + id,
+ handler, originalHandler, newType;
+
+ if (obj[key]) { return this; }
+
+ handler = function (e) {
+ return fn.call(context || obj, e || L.DomEvent._getEvent());
+ };
+
+ if (L.Browser.msTouch && type.indexOf('touch') === 0) {
+ return this.addMsTouchListener(obj, type, handler, id);
+ }
+ if (L.Browser.touch && (type === 'dblclick') && this.addDoubleTapListener) {
+ this.addDoubleTapListener(obj, handler, id);
+ }
+
+ if ('addEventListener' in obj) {
+
+ if (type === 'mousewheel') {
+ obj.addEventListener('DOMMouseScroll', handler, false);
+ obj.addEventListener(type, handler, false);
+
+ } else if ((type === 'mouseenter') || (type === 'mouseleave')) {
+
+ originalHandler = handler;
+ newType = (type === 'mouseenter' ? 'mouseover' : 'mouseout');
+
+ handler = function (e) {
+ if (!L.DomEvent._checkMouse(obj, e)) { return; }
+ return originalHandler(e);
+ };
+
+ obj.addEventListener(newType, handler, false);
+
+ } else {
+ obj.addEventListener(type, handler, false);
+ }
+
+ } else if ('attachEvent' in obj) {
+ obj.attachEvent("on" + type, handler);
+ }
+
+ obj[key] = handler;
+
+ return this;
+ },
+
+ removeListener: function (obj, type, fn) { // (HTMLElement, String, Function)
+
+ var id = L.stamp(fn),
+ key = '_leaflet_' + type + id,
+ handler = obj[key];
+
+ if (!handler) { return; }
+
+ if (L.Browser.msTouch && type.indexOf('touch') === 0) {
+ this.removeMsTouchListener(obj, type, id);
+ } else if (L.Browser.touch && (type === 'dblclick') && this.removeDoubleTapListener) {
+ this.removeDoubleTapListener(obj, id);
+
+ } else if ('removeEventListener' in obj) {
+
+ if (type === 'mousewheel') {
+ obj.removeEventListener('DOMMouseScroll', handler, false);
+ obj.removeEventListener(type, handler, false);
+
+ } else if ((type === 'mouseenter') || (type === 'mouseleave')) {
+ obj.removeEventListener((type === 'mouseenter' ? 'mouseover' : 'mouseout'), handler, false);
+ } else {
+ obj.removeEventListener(type, handler, false);
+ }
+ } else if ('detachEvent' in obj) {
+ obj.detachEvent("on" + type, handler);
+ }
+
+ obj[key] = null;
+
+ return this;
+ },
+
+ stopPropagation: function (e) {
+
+ if (e.stopPropagation) {
+ e.stopPropagation();
+ } else {
+ e.cancelBubble = true;
+ }
+ return this;
+ },
+
+ disableClickPropagation: function (el) {
+
+ var stop = L.DomEvent.stopPropagation;
+
+ for (var i = L.Draggable.START.length - 1; i >= 0; i--) {
+ L.DomEvent.addListener(el, L.Draggable.START[i], stop);
+ }
+
+ return L.DomEvent
+ .addListener(el, 'click', stop)
+ .addListener(el, 'dblclick', stop);
+ },
+
+ preventDefault: function (e) {
+
+ if (e.preventDefault) {
+ e.preventDefault();
+ } else {
+ e.returnValue = false;
+ }
+ return this;
+ },
+
+ stop: function (e) {
+ return L.DomEvent.preventDefault(e).stopPropagation(e);
+ },
+
+ getMousePosition: function (e, container) {
+
+ var body = document.body,
+ docEl = document.documentElement,
+ x = e.pageX ? e.pageX : e.clientX + body.scrollLeft + docEl.scrollLeft,
+ y = e.pageY ? e.pageY : e.clientY + body.scrollTop + docEl.scrollTop,
+ pos = new L.Point(x, y);
+
+ return (container ? pos._subtract(L.DomUtil.getViewportOffset(container)) : pos);
+ },
+
+ getWheelDelta: function (e) {
+
+ var delta = 0;
+
+ if (e.wheelDelta) {
+ delta = e.wheelDelta / 120;
+ }
+ if (e.detail) {
+ delta = -e.detail / 3;
+ }
+ return delta;
+ },
+
+ // check if element really left/entered the event target (for mouseenter/mouseleave)
+ _checkMouse: function (el, e) {
+
+ var related = e.relatedTarget;
+
+ if (!related) { return true; }
+
+ try {
+ while (related && (related !== el)) {
+ related = related.parentNode;
+ }
+ } catch (err) {
+ return false;
+ }
+ return (related !== el);
+ },
+
+ _getEvent: function () { // evil magic for IE
+ /*jshint noarg:false */
+ var e = window.event;
+ if (!e) {
+ var caller = arguments.callee.caller;
+ while (caller) {
+ e = caller['arguments'][0];
+ if (e && window.Event === e.constructor) {
+ break;
+ }
+ caller = caller.caller;
+ }
+ }
+ return e;
+ }
+};
+
+L.DomEvent.on = L.DomEvent.addListener;
+L.DomEvent.off = L.DomEvent.removeListener;
+
+
+/*
+ * L.Draggable allows you to add dragging capabilities to any element. Supports mobile devices too.
+ */
+
+L.Draggable = L.Class.extend({
+ includes: L.Mixin.Events,
+
+ statics: {
+ START: L.Browser.touch ? ['touchstart', 'mousedown'] : ['mousedown'],
+ END: {
+ mousedown: 'mouseup',
+ touchstart: 'touchend',
+ MSPointerDown: 'touchend'
+ },
+ MOVE: {
+ mousedown: 'mousemove',
+ touchstart: 'touchmove',
+ MSPointerDown: 'touchmove'
+ },
+ TAP_TOLERANCE: 15
+ },
+
+ initialize: function (element, dragStartTarget, longPress) {
+ this._element = element;
+ this._dragStartTarget = dragStartTarget || element;
+ this._longPress = longPress && !L.Browser.msTouch;
+ },
+
+ enable: function () {
+ if (this._enabled) { return; }
+
+ for (var i = L.Draggable.START.length - 1; i >= 0; i--) {
+ L.DomEvent.on(this._dragStartTarget, L.Draggable.START[i], this._onDown, this);
+ }
+ this._enabled = true;
+ },
+
+ disable: function () {
+ if (!this._enabled) { return; }
+
+ for (var i = L.Draggable.START.length - 1; i >= 0; i--) {
+ L.DomEvent.off(this._dragStartTarget, L.Draggable.START[i], this._onDown, this);
+ }
+ this._enabled = false;
+ this._moved = false;
+ },
+
+ _onDown: function (e) {
+ if ((!L.Browser.touch && e.shiftKey) ||
+ ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
+
+ L.DomEvent.preventDefault(e);
+ L.DomEvent.stopPropagation(e);
+
+ if (L.Draggable._disabled) { return; }
+
+ this._simulateClick = true;
+
+ if (e.touches && e.touches.length > 1) {
+ this._simulateClick = false;
+ clearTimeout(this._longPressTimeout);
+ return;
+ }
+
+ var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
+ el = first.target;
+
+ if (L.Browser.touch && el.tagName.toLowerCase() === 'a') {
+ L.DomUtil.addClass(el, 'leaflet-active');
+ }
+
+ this._moved = false;
+ if (this._moving) { return; }
+
+ this._startPoint = new L.Point(first.clientX, first.clientY);
+ this._startPos = this._newPos = L.DomUtil.getPosition(this._element);
+
+ //Touch contextmenu event emulation
+ if (e.touches && e.touches.length === 1 && L.Browser.touch && this._longPress) {
+ this._longPressTimeout = setTimeout(L.bind(function () {
+ var dist = (this._newPos && this._newPos.distanceTo(this._startPos)) || 0;
+
+ if (dist < L.Draggable.TAP_TOLERANCE) {
+ this._simulateClick = false;
+ this._onUp();
+ this._simulateEvent('contextmenu', first);
+ }
+ }, this), 1000);
+ }
+
+ L.DomEvent.on(document, L.Draggable.MOVE[e.type], this._onMove, this);
+ L.DomEvent.on(document, L.Draggable.END[e.type], this._onUp, this);
+ },
+
+ _onMove: function (e) {
+ if (e.touches && e.touches.length > 1) { return; }
+
+ var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
+ newPoint = new L.Point(first.clientX, first.clientY),
+ diffVec = newPoint.subtract(this._startPoint);
+
+ if (!diffVec.x && !diffVec.y) { return; }
+
+ L.DomEvent.preventDefault(e);
+
+ if (!this._moved) {
+ this.fire('dragstart');
+ this._moved = true;
+
+ this._startPos = L.DomUtil.getPosition(this._element).subtract(diffVec);
+
+ if (!L.Browser.touch) {
+ L.DomUtil.disableTextSelection();
+ this._setMovingCursor();
+ }
+ }
+
+ this._newPos = this._startPos.add(diffVec);
+ this._moving = true;
+
+ L.Util.cancelAnimFrame(this._animRequest);
+ this._animRequest = L.Util.requestAnimFrame(this._updatePosition, this, true, this._dragStartTarget);
+ },
+
+ _updatePosition: function () {
+ this.fire('predrag');
+ L.DomUtil.setPosition(this._element, this._newPos);
+ this.fire('drag');
+ },
+
+ _onUp: function (e) {
+ var simulateClickTouch;
+ clearTimeout(this._longPressTimeout);
+ if (this._simulateClick && e.changedTouches) {
+ var first = e.changedTouches[0],
+ el = first.target,
+ dist = (this._newPos && this._newPos.distanceTo(this._startPos)) || 0;
+
+ if (el.tagName.toLowerCase() === 'a') {
+ L.DomUtil.removeClass(el, 'leaflet-active');
+ }
+
+ if (dist < L.Draggable.TAP_TOLERANCE) {
+ simulateClickTouch = first;
+ }
+ }
+
+ if (!L.Browser.touch) {
+ L.DomUtil.enableTextSelection();
+ this._restoreCursor();
+ }
+
+ for (var i in L.Draggable.MOVE) {
+ if (L.Draggable.MOVE.hasOwnProperty(i)) {
+ L.DomEvent.off(document, L.Draggable.MOVE[i], this._onMove);
+ L.DomEvent.off(document, L.Draggable.END[i], this._onUp);
+ }
+ }
+
+ if (this._moved) {
+ // ensure drag is not fired after dragend
+ L.Util.cancelAnimFrame(this._animRequest);
+
+ this.fire('dragend');
+ }
+ this._moving = false;
+
+ if (simulateClickTouch) {
+ this._moved = false;
+ this._simulateEvent('click', simulateClickTouch);
+ }
+ },
+
+ _setMovingCursor: function () {
+ L.DomUtil.addClass(document.body, 'leaflet-dragging');
+ },
+
+ _restoreCursor: function () {
+ L.DomUtil.removeClass(document.body, 'leaflet-dragging');
+ },
+
+ _simulateEvent: function (type, e) {
+ var simulatedEvent = document.createEvent('MouseEvents');
+
+ simulatedEvent.initMouseEvent(
+ type, true, true, window, 1,
+ e.screenX, e.screenY,
+ e.clientX, e.clientY,
+ false, false, false, false, 0, null);
+
+ e.target.dispatchEvent(simulatedEvent);
+ }
+});
+
+
+/*
+ L.Handler is a base class for handler classes that are used internally to inject
+ interaction features like dragging to classes like Map and Marker.
+*/
+
+L.Handler = L.Class.extend({
+ initialize: function (map) {
+ this._map = map;
+ },
+
+ enable: function () {
+ if (this._enabled) { return; }
+
+ this._enabled = true;
+ this.addHooks();
+ },
+
+ disable: function () {
+ if (!this._enabled) { return; }
+
+ this._enabled = false;
+ this.removeHooks();
+ },
+
+ enabled: function () {
+ return !!this._enabled;
+ }
+});
+
+
+/*
+ * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default.
+ */
+
+L.Map.mergeOptions({
+ dragging: true,
+
+ inertia: !L.Browser.android23,
+ inertiaDeceleration: 3400, // px/s^2
+ inertiaMaxSpeed: Infinity, // px/s
+ inertiaThreshold: L.Browser.touch ? 32 : 18, // ms
+ easeLinearity: 0.25,
+
+ longPress: true,
+
+ // TODO refactor, move to CRS
+ worldCopyJump: false
+});
+
+L.Map.Drag = L.Handler.extend({
+ addHooks: function () {
+ if (!this._draggable) {
+ var map = this._map;
+
+ this._draggable = new L.Draggable(map._mapPane, map._container, map.options.longPress);
+
+ this._draggable.on({
+ 'dragstart': this._onDragStart,
+ 'drag': this._onDrag,
+ 'dragend': this._onDragEnd
+ }, this);
+
+ if (map.options.worldCopyJump) {
+ this._draggable.on('predrag', this._onPreDrag, this);
+ map.on('viewreset', this._onViewReset, this);
+ }
+ }
+ this._draggable.enable();
+ },
+
+ removeHooks: function () {
+ this._draggable.disable();
+ },
+
+ moved: function () {
+ return this._draggable && this._draggable._moved;
+ },
+
+ _onDragStart: function () {
+ var map = this._map;
+
+ if (map._panAnim) {
+ map._panAnim.stop();
+ }
+
+ map
+ .fire('movestart')
+ .fire('dragstart');
+
+ if (map.options.inertia) {
+ this._positions = [];
+ this._times = [];
+ }
+ },
+
+ _onDrag: function () {
+ if (this._map.options.inertia) {
+ var time = this._lastTime = +new Date(),
+ pos = this._lastPos = this._draggable._newPos;
+
+ this._positions.push(pos);
+ this._times.push(time);
+
+ if (time - this._times[0] > 200) {
+ this._positions.shift();
+ this._times.shift();
+ }
+ }
+
+ this._map
+ .fire('move')
+ .fire('drag');
+ },
+
+ _onViewReset: function () {
+ // TODO fix hardcoded Earth values
+ var pxCenter = this._map.getSize()._divideBy(2),
+ pxWorldCenter = this._map.latLngToLayerPoint(new L.LatLng(0, 0));
+
+ this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
+ this._worldWidth = this._map.project(new L.LatLng(0, 180)).x;
+ },
+
+ _onPreDrag: function () {
+ // TODO refactor to be able to adjust map pane position after zoom
+ var worldWidth = this._worldWidth,
+ halfWidth = Math.round(worldWidth / 2),
+ dx = this._initialWorldOffset,
+ x = this._draggable._newPos.x,
+ newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
+ newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
+ newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
+
+ this._draggable._newPos.x = newX;
+ },
+
+ _onDragEnd: function () {
+ var map = this._map,
+ options = map.options,
+ delay = +new Date() - this._lastTime,
+
+ noInertia = !options.inertia || delay > options.inertiaThreshold || !this._positions[0];
+
+ if (noInertia) {
+ map.fire('moveend');
+
+ } else {
+
+ var direction = this._lastPos.subtract(this._positions[0]),
+ duration = (this._lastTime + delay - this._times[0]) / 1000,
+ ease = options.easeLinearity,
+
+ speedVector = direction.multiplyBy(ease / duration),
+ speed = speedVector.distanceTo(new L.Point(0, 0)),
+
+ limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
+ limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
+
+ decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease),
+ offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
+
+ L.Util.requestAnimFrame(function () {
+ map.panBy(offset, decelerationDuration, ease);
+ });
+ }
+
+ map.fire('dragend');
+
+ if (options.maxBounds) {
+ // TODO predrag validation instead of animation
+ L.Util.requestAnimFrame(this._panInsideMaxBounds, map, true, map._container);
+ }
+ },
+
+ _panInsideMaxBounds: function () {
+ this.panInsideBounds(this.options.maxBounds);
+ }
+});
+
+L.Map.addInitHook('addHandler', 'dragging', L.Map.Drag);
+
+
+/*
+ * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default.
+ */
+
+L.Map.mergeOptions({
+ doubleClickZoom: true
+});
+
+L.Map.DoubleClickZoom = L.Handler.extend({
+ addHooks: function () {
+ this._map.on('dblclick', this._onDoubleClick);
+ },
+
+ removeHooks: function () {
+ this._map.off('dblclick', this._onDoubleClick);
+ },
+
+ _onDoubleClick: function (e) {
+ this.setView(e.latlng, this._zoom + 1);
+ }
+});
+
+L.Map.addInitHook('addHandler', 'doubleClickZoom', L.Map.DoubleClickZoom);
+
+
+/*
+ * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
+ */
+
+L.Map.mergeOptions({
+ scrollWheelZoom: true
+});
+
+L.Map.ScrollWheelZoom = L.Handler.extend({
+ addHooks: function () {
+ L.DomEvent.on(this._map._container, 'mousewheel', this._onWheelScroll, this);
+ this._delta = 0;
+ },
+
+ removeHooks: function () {
+ L.DomEvent.off(this._map._container, 'mousewheel', this._onWheelScroll);
+ },
+
+ _onWheelScroll: function (e) {
+ var delta = L.DomEvent.getWheelDelta(e);
+
+ this._delta += delta;
+ this._lastMousePos = this._map.mouseEventToContainerPoint(e);
+
+ if (!this._startTime) {
+ this._startTime = +new Date();
+ }
+
+ var left = Math.max(40 - (+new Date() - this._startTime), 0);
+
+ clearTimeout(this._timer);
+ this._timer = setTimeout(L.bind(this._performZoom, this), left);
+
+ L.DomEvent.preventDefault(e);
+ L.DomEvent.stopPropagation(e);
+ },
+
+ _performZoom: function () {
+ var map = this._map,
+ delta = this._delta,
+ zoom = map.getZoom();
+
+ delta = delta > 0 ? Math.ceil(delta) : Math.round(delta);
+ delta = Math.max(Math.min(delta, 4), -4);
+ delta = map._limitZoom(zoom + delta) - zoom;
+
+ this._delta = 0;
+
+ this._startTime = null;
+
+ if (!delta) { return; }
+
+ var newZoom = zoom + delta,
+ newCenter = this._getCenterForScrollWheelZoom(newZoom);
+
+ map.setView(newCenter, newZoom);
+ },
+
+ _getCenterForScrollWheelZoom: function (newZoom) {
+ var map = this._map,
+ scale = map.getZoomScale(newZoom),
+ viewHalf = map.getSize()._divideBy(2),
+ centerOffset = this._lastMousePos._subtract(viewHalf)._multiplyBy(1 - 1 / scale),
+ newCenterPoint = map._getTopLeftPoint()._add(viewHalf)._add(centerOffset);
+
+ return map.unproject(newCenterPoint);
+ }
+});
+
+L.Map.addInitHook('addHandler', 'scrollWheelZoom', L.Map.ScrollWheelZoom);
+
+
+/*
+ * Extends the event handling code with double tap support for mobile browsers.
+ */
+
+L.extend(L.DomEvent, {
+
+ _touchstart: L.Browser.msTouch ? 'MSPointerDown' : 'touchstart',
+ _touchend: L.Browser.msTouch ? 'MSPointerUp' : 'touchend',
+
+ // inspired by Zepto touch code by Thomas Fuchs
+ addDoubleTapListener: function (obj, handler, id) {
+ var last,
+ doubleTap = false,
+ delay = 250,
+ touch,
+ pre = '_leaflet_',
+ touchstart = this._touchstart,
+ touchend = this._touchend,
+ trackedTouches = [];
+
+ function onTouchStart(e) {
+ var count;
+ if (L.Browser.msTouch) {
+ trackedTouches.push(e.pointerId);
+ count = trackedTouches.length;
+ } else {
+ count = e.touches.length;
+ }
+ if (count > 1) {
+ return;
+ }
+
+ var now = Date.now(),
+ delta = now - (last || now);
+
+ touch = e.touches ? e.touches[0] : e;
+ doubleTap = (delta > 0 && delta <= delay);
+ last = now;
+ }
+
+ function onTouchEnd(e) {
+ /*jshint forin:false */
+ if (L.Browser.msTouch) {
+ var idx = trackedTouches.indexOf(e.pointerId);
+ if (idx === -1) {
+ return;
+ }
+ trackedTouches.splice(idx, 1);
+ }
+
+ if (doubleTap) {
+ if (L.Browser.msTouch) {
+ //Work around .type being readonly with MSPointer* events
+ var newTouch = { },
+ prop;
+
+ for (var i in touch) {
+ prop = touch[i];
+ if (typeof prop === 'function') {
+ newTouch[i] = prop.bind(touch);
+ } else {
+ newTouch[i] = prop;
+ }
+ }
+ touch = newTouch;
+ }
+ touch.type = 'dblclick';
+ handler(touch);
+ last = null;
+ }
+ }
+ obj[pre + touchstart + id] = onTouchStart;
+ obj[pre + touchend + id] = onTouchEnd;
+
+ //On msTouch we need to listen on the document otherwise a drag starting on the map and moving off screen will not come through to us
+ // so we will lose track of how many touches are ongoing
+ var endElement = L.Browser.msTouch ? document.documentElement : obj;
+
+ obj.addEventListener(touchstart, onTouchStart, false);
+ endElement.addEventListener(touchend, onTouchEnd, false);
+ if (L.Browser.msTouch) {
+ endElement.addEventListener('MSPointerCancel', onTouchEnd, false);
+ }
+ return this;
+ },
+
+ removeDoubleTapListener: function (obj, id) {
+ var pre = '_leaflet_';
+ obj.removeEventListener(this._touchstart, obj[pre + this._touchstart + id], false);
+ (L.Browser.msTouch ? document.documentElement : obj).removeEventListener(this._touchend, obj[pre + this._touchend + id], false);
+ if (L.Browser.msTouch) {
+ document.documentElement.removeEventListener('MSPointerCancel', obj[pre + this._touchend + id], false);
+ }
+ return this;
+ }
+});
+
+
+/*
+ * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices.
+ */
+
+L.extend(L.DomEvent, {
+
+ _msTouches: [],
+ _msDocumentListener: false,
+
+ // Provides a touch events wrapper for msPointer events.
+ // Based on changes by veproza https://github.com/CloudMade/Leaflet/pull/1019
+
+ addMsTouchListener: function (obj, type, handler, id) {
+
+ switch (type) {
+ case 'touchstart':
+ return this.addMsTouchListenerStart(obj, type, handler, id);
+ case 'touchend':
+ return this.addMsTouchListenerEnd(obj, type, handler, id);
+ case 'touchmove':
+ return this.addMsTouchListenerMove(obj, type, handler, id);
+ default:
+ throw 'Unknown touch event type';
+ }
+ },
+
+ addMsTouchListenerStart: function (obj, type, handler, id) {
+ var pre = '_leaflet_',
+ touches = this._msTouches;
+
+ var cb = function (e) {
+
+ var alreadyInArray = false;
+ for (var i = 0; i < touches.length; i++) {
+ if (touches[i].pointerId === e.pointerId) {
+ alreadyInArray = true;
+ break;
+ }
+ }
+ if (!alreadyInArray) {
+ touches.push(e);
+ }
+
+ e.touches = touches.slice();
+ e.changedTouches = [e];
+
+ handler(e);
+ };
+
+ obj[pre + 'touchstart' + id] = cb;
+ obj.addEventListener('MSPointerDown', cb, false);
+
+ // need to also listen for end events to keep the _msTouches list accurate
+ // this needs to be on the body and never go away
+ if (!this._msDocumentListener) {
+ var internalCb = function (e) {
+ for (var i = 0; i < touches.length; i++) {
+ if (touches[i].pointerId === e.pointerId) {
+ touches.splice(i, 1);
+ break;
+ }
+ }
+ };
+ //We listen on the documentElement as any drags that end by moving the touch off the screen get fired there
+ document.documentElement.addEventListener('MSPointerUp', internalCb, false);
+ document.documentElement.addEventListener('MSPointerCancel', internalCb, false);
+
+ this._msDocumentListener = true;
+ }
+
+ return this;
+ },
+
+ addMsTouchListenerMove: function (obj, type, handler, id) {
+ var pre = '_leaflet_',
+ touches = this._msTouches;
+
+ function cb(e) {
+
+ // don't fire touch moves when mouse isn't down
+ if (e.pointerType === e.MSPOINTER_TYPE_MOUSE && e.buttons === 0) { return; }
+
+ for (var i = 0; i < touches.length; i++) {
+ if (touches[i].pointerId === e.pointerId) {
+ touches[i] = e;
+ break;
+ }
+ }
+
+ e.touches = touches.slice();
+ e.changedTouches = [e];
+
+ handler(e);
+ }
+
+ obj[pre + 'touchmove' + id] = cb;
+ obj.addEventListener('MSPointerMove', cb, false);
+
+ return this;
+ },
+
+ addMsTouchListenerEnd: function (obj, type, handler, id) {
+ var pre = '_leaflet_',
+ touches = this._msTouches;
+
+ var cb = function (e) {
+ for (var i = 0; i < touches.length; i++) {
+ if (touches[i].pointerId === e.pointerId) {
+ touches.splice(i, 1);
+ break;
+ }
+ }
+
+ e.touches = touches.slice();
+ e.changedTouches = [e];
+
+ handler(e);
+ };
+
+ obj[pre + 'touchend' + id] = cb;
+ obj.addEventListener('MSPointerUp', cb, false);
+ obj.addEventListener('MSPointerCancel', cb, false);
+
+ return this;
+ },
+
+ removeMsTouchListener: function (obj, type, id) {
+ var pre = '_leaflet_',
+ cb = obj[pre + type + id];
+
+ switch (type) {
+ case 'touchstart':
+ obj.removeEventListener('MSPointerDown', cb, false);
+ break;
+ case 'touchmove':
+ obj.removeEventListener('MSPointerMove', cb, false);
+ break;
+ case 'touchend':
+ obj.removeEventListener('MSPointerUp', cb, false);
+ obj.removeEventListener('MSPointerCancel', cb, false);
+ break;
+ }
+
+ return this;
+ }
+});
+
+
+/*
+ * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.
+ */
+
+L.Map.mergeOptions({
+ touchZoom: L.Browser.touch && !L.Browser.android23
+});
+
+L.Map.TouchZoom = L.Handler.extend({
+ addHooks: function () {
+ L.DomEvent.on(this._map._container, 'touchstart', this._onTouchStart, this);
+ },
+
+ removeHooks: function () {
+ L.DomEvent.off(this._map._container, 'touchstart', this._onTouchStart, this);
+ },
+
+ _onTouchStart: function (e) {
+ var map = this._map;
+
+ if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
+
+ var p1 = map.mouseEventToLayerPoint(e.touches[0]),
+ p2 = map.mouseEventToLayerPoint(e.touches[1]),
+ viewCenter = map._getCenterLayerPoint();
+
+ this._startCenter = p1.add(p2)._divideBy(2);
+ this._startDist = p1.distanceTo(p2);
+
+ this._moved = false;
+ this._zooming = true;
+
+ this._centerOffset = viewCenter.subtract(this._startCenter);
+
+ if (map._panAnim) {
+ map._panAnim.stop();
+ }
+
+ L.DomEvent
+ .on(document, 'touchmove', this._onTouchMove, this)
+ .on(document, 'touchend', this._onTouchEnd, this);
+
+ L.DomEvent.preventDefault(e);
+ },
+
+ _onTouchMove: function (e) {
+ if (!e.touches || e.touches.length !== 2) { return; }
+
+ var map = this._map;
+
+ var p1 = map.mouseEventToLayerPoint(e.touches[0]),
+ p2 = map.mouseEventToLayerPoint(e.touches[1]);
+
+ this._scale = p1.distanceTo(p2) / this._startDist;
+ this._delta = p1._add(p2)._divideBy(2)._subtract(this._startCenter);
+
+ if (this._scale === 1) { return; }
+
+ if (!this._moved) {
+ L.DomUtil.addClass(map._mapPane, 'leaflet-zoom-anim leaflet-touching');
+
+ map
+ .fire('movestart')
+ .fire('zoomstart')
+ ._prepareTileBg();
+
+ this._moved = true;
+ }
+
+ L.Util.cancelAnimFrame(this._animRequest);
+ this._animRequest = L.Util.requestAnimFrame(
+ this._updateOnMove, this, true, this._map._container);
+
+ L.DomEvent.preventDefault(e);
+ },
+
+ _updateOnMove: function () {
+ var map = this._map,
+ origin = this._getScaleOrigin(),
+ center = map.layerPointToLatLng(origin);
+
+ map.fire('zoomanim', {
+ center: center,
+ zoom: map.getScaleZoom(this._scale)
+ });
+
+ // Used 2 translates instead of transform-origin because of a very strange bug -
+ // it didn't count the origin on the first touch-zoom but worked correctly afterwards
+
+ map._tileBg.style[L.DomUtil.TRANSFORM] =
+ L.DomUtil.getTranslateString(this._delta) + ' ' +
+ L.DomUtil.getScaleString(this._scale, this._startCenter);
+ },
+
+ _onTouchEnd: function () {
+ if (!this._moved || !this._zooming) { return; }
+
+ var map = this._map;
+
+ this._zooming = false;
+ L.DomUtil.removeClass(map._mapPane, 'leaflet-touching');
+
+ L.DomEvent
+ .off(document, 'touchmove', this._onTouchMove)
+ .off(document, 'touchend', this._onTouchEnd);
+
+ var origin = this._getScaleOrigin(),
+ center = map.layerPointToLatLng(origin),
+
+ oldZoom = map.getZoom(),
+ floatZoomDelta = map.getScaleZoom(this._scale) - oldZoom,
+ roundZoomDelta = (floatZoomDelta > 0 ?
+ Math.ceil(floatZoomDelta) : Math.floor(floatZoomDelta)),
+
+ zoom = map._limitZoom(oldZoom + roundZoomDelta);
+
+ map.fire('zoomanim', {
+ center: center,
+ zoom: zoom
+ });
+
+ map._runAnimation(center, zoom, map.getZoomScale(zoom) / this._scale, origin, true);
+ },
+
+ _getScaleOrigin: function () {
+ var centerOffset = this._centerOffset.subtract(this._delta).divideBy(this._scale);
+ return this._startCenter.add(centerOffset);
+ }
+});
+
+L.Map.addInitHook('addHandler', 'touchZoom', L.Map.TouchZoom);
+
+
+/*
+ * L.Handler.ShiftDragZoom is used to add shift-drag zoom interaction to the map
+ * (zoom to a selected bounding box), enabled by default.
+ */
+
+L.Map.mergeOptions({
+ boxZoom: true
+});
+
+L.Map.BoxZoom = L.Handler.extend({
+ initialize: function (map) {
+ this._map = map;
+ this._container = map._container;
+ this._pane = map._panes.overlayPane;
+ },
+
+ addHooks: function () {
+ L.DomEvent.on(this._container, 'mousedown', this._onMouseDown, this);
+ },
+
+ removeHooks: function () {
+ L.DomEvent.off(this._container, 'mousedown', this._onMouseDown);
+ },
+
+ _onMouseDown: function (e) {
+ if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
+
+ L.DomUtil.disableTextSelection();
+
+ this._startLayerPoint = this._map.mouseEventToLayerPoint(e);
+
+ this._box = L.DomUtil.create('div', 'leaflet-zoom-box', this._pane);
+ L.DomUtil.setPosition(this._box, this._startLayerPoint);
+
+ //TODO refactor: move cursor to styles
+ this._container.style.cursor = 'crosshair';
+
+ L.DomEvent
+ .on(document, 'mousemove', this._onMouseMove, this)
+ .on(document, 'mouseup', this._onMouseUp, this)
+ .preventDefault(e);
+
+ this._map.fire("boxzoomstart");
+ },
+
+ _onMouseMove: function (e) {
+ var startPoint = this._startLayerPoint,
+ box = this._box,
+
+ layerPoint = this._map.mouseEventToLayerPoint(e),
+ offset = layerPoint.subtract(startPoint),
+
+ newPos = new L.Point(
+ Math.min(layerPoint.x, startPoint.x),
+ Math.min(layerPoint.y, startPoint.y));
+
+ L.DomUtil.setPosition(box, newPos);
+
+ // TODO refactor: remove hardcoded 4 pixels
+ box.style.width = (Math.max(0, Math.abs(offset.x) - 4)) + 'px';
+ box.style.height = (Math.max(0, Math.abs(offset.y) - 4)) + 'px';
+ },
+
+ _onMouseUp: function (e) {
+ this._pane.removeChild(this._box);
+ this._container.style.cursor = '';
+
+ L.DomUtil.enableTextSelection();
+
+ L.DomEvent
+ .off(document, 'mousemove', this._onMouseMove)
+ .off(document, 'mouseup', this._onMouseUp);
+
+ var map = this._map,
+ layerPoint = map.mouseEventToLayerPoint(e);
+
+ if (this._startLayerPoint.equals(layerPoint)) { return; }
+
+ var bounds = new L.LatLngBounds(
+ map.layerPointToLatLng(this._startLayerPoint),
+ map.layerPointToLatLng(layerPoint));
+
+ map.fitBounds(bounds);
+
+ map.fire("boxzoomend", {
+ boxZoomBounds: bounds
+ });
+ }
+});
+
+L.Map.addInitHook('addHandler', 'boxZoom', L.Map.BoxZoom);
+
+
+/*
+ * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
+ */
+
+L.Map.mergeOptions({
+ keyboard: true,
+ keyboardPanOffset: 80,
+ keyboardZoomOffset: 1
+});
+
+L.Map.Keyboard = L.Handler.extend({
+
+ keyCodes: {
+ left: [37],
+ right: [39],
+ down: [40],
+ up: [38],
+ zoomIn: [187, 107, 61],
+ zoomOut: [189, 109, 173]
+ },
+
+ initialize: function (map) {
+ this._map = map;
+
+ this._setPanOffset(map.options.keyboardPanOffset);
+ this._setZoomOffset(map.options.keyboardZoomOffset);
+ },
+
+ addHooks: function () {
+ var container = this._map._container;
+
+ // make the container focusable by tabbing
+ if (container.tabIndex === -1) {
+ container.tabIndex = "0";
+ }
+
+ L.DomEvent
+ .on(container, 'focus', this._onFocus, this)
+ .on(container, 'blur', this._onBlur, this)
+ .on(container, 'mousedown', this._onMouseDown, this);
+
+ this._map
+ .on('focus', this._addHooks, this)
+ .on('blur', this._removeHooks, this);
+ },
+
+ removeHooks: function () {
+ this._removeHooks();
+
+ var container = this._map._container;
+
+ L.DomEvent
+ .off(container, 'focus', this._onFocus, this)
+ .off(container, 'blur', this._onBlur, this)
+ .off(container, 'mousedown', this._onMouseDown, this);
+
+ this._map
+ .off('focus', this._addHooks, this)
+ .off('blur', this._removeHooks, this);
+ },
+
+ _onMouseDown: function () {
+ if (!this._focused) {
+ this._map._container.focus();
+ }
+ },
+
+ _onFocus: function () {
+ this._focused = true;
+ this._map.fire('focus');
+ },
+
+ _onBlur: function () {
+ this._focused = false;
+ this._map.fire('blur');
+ },
+
+ _setPanOffset: function (pan) {
+ var keys = this._panKeys = {},
+ codes = this.keyCodes,
+ i, len;
+
+ for (i = 0, len = codes.left.length; i < len; i++) {
+ keys[codes.left[i]] = [-1 * pan, 0];
+ }
+ for (i = 0, len = codes.right.length; i < len; i++) {
+ keys[codes.right[i]] = [pan, 0];
+ }
+ for (i = 0, len = codes.down.length; i < len; i++) {
+ keys[codes.down[i]] = [0, pan];
+ }
+ for (i = 0, len = codes.up.length; i < len; i++) {
+ keys[codes.up[i]] = [0, -1 * pan];
+ }
+ },
+
+ _setZoomOffset: function (zoom) {
+ var keys = this._zoomKeys = {},
+ codes = this.keyCodes,
+ i, len;
+
+ for (i = 0, len = codes.zoomIn.length; i < len; i++) {
+ keys[codes.zoomIn[i]] = zoom;
+ }
+ for (i = 0, len = codes.zoomOut.length; i < len; i++) {
+ keys[codes.zoomOut[i]] = -zoom;
+ }
+ },
+
+ _addHooks: function () {
+ L.DomEvent.on(document, 'keydown', this._onKeyDown, this);
+ },
+
+ _removeHooks: function () {
+ L.DomEvent.off(document, 'keydown', this._onKeyDown, this);
+ },
+
+ _onKeyDown: function (e) {
+ var key = e.keyCode,
+ map = this._map;
+
+ if (this._panKeys.hasOwnProperty(key)) {
+ map.panBy(this._panKeys[key]);
+
+ if (map.options.maxBounds) {
+ map.panInsideBounds(map.options.maxBounds);
+ }
+
+ } else if (this._zoomKeys.hasOwnProperty(key)) {
+ map.setZoom(map.getZoom() + this._zoomKeys[key]);
+
+ } else {
+ return;
+ }
+
+ L.DomEvent.stop(e);
+ }
+});
+
+L.Map.addInitHook('addHandler', 'keyboard', L.Map.Keyboard);
+
+
+/*
+ * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
+ */
+
+L.Handler.MarkerDrag = L.Handler.extend({
+ initialize: function (marker) {
+ this._marker = marker;
+ },
+
+ addHooks: function () {
+ var icon = this._marker._icon;
+ if (!this._draggable) {
+ this._draggable = new L.Draggable(icon, icon)
+ .on('dragstart', this._onDragStart, this)
+ .on('drag', this._onDrag, this)
+ .on('dragend', this._onDragEnd, this);
+ }
+ this._draggable.enable();
+ },
+
+ removeHooks: function () {
+ this._draggable.disable();
+ },
+
+ moved: function () {
+ return this._draggable && this._draggable._moved;
+ },
+
+ _onDragStart: function () {
+ this._marker
+ .closePopup()
+ .fire('movestart')
+ .fire('dragstart');
+ },
+
+ _onDrag: function () {
+ var marker = this._marker,
+ shadow = marker._shadow,
+ iconPos = L.DomUtil.getPosition(marker._icon),
+ latlng = marker._map.layerPointToLatLng(iconPos);
+
+ // update shadow position
+ if (shadow) {
+ L.DomUtil.setPosition(shadow, iconPos);
+ }
+
+ marker._latlng = latlng;
+
+ marker
+ .fire('move', {latlng: latlng})
+ .fire('drag');
+ },
+
+ _onDragEnd: function () {
+ this._marker
+ .fire('moveend')
+ .fire('dragend');
+ }
+});
+
+
+/*
+ * L.Handler.PolyEdit is an editing handler for polylines and polygons.
+ */
+
+L.Handler.PolyEdit = L.Handler.extend({
+ options: {
+ icon: new L.DivIcon({
+ iconSize: new L.Point(8, 8),
+ className: 'leaflet-div-icon leaflet-editing-icon'
+ })
+ },
+
+ initialize: function (poly, options) {
+ this._poly = poly;
+ L.setOptions(this, options);
+ },
+
+ addHooks: function () {
+ if (this._poly._map) {
+ if (!this._markerGroup) {
+ this._initMarkers();
+ }
+ this._poly._map.addLayer(this._markerGroup);
+ }
+ },
+
+ removeHooks: function () {
+ if (this._poly._map) {
+ this._poly._map.removeLayer(this._markerGroup);
+ delete this._markerGroup;
+ delete this._markers;
+ }
+ },
+
+ updateMarkers: function () {
+ this._markerGroup.clearLayers();
+ this._initMarkers();
+ },
+
+ _initMarkers: function () {
+ if (!this._markerGroup) {
+ this._markerGroup = new L.LayerGroup();
+ }
+ this._markers = [];
+
+ var latlngs = this._poly._latlngs,
+ i, j, len, marker;
+
+ // TODO refactor holes implementation in Polygon to support it here
+
+ for (i = 0, len = latlngs.length; i < len; i++) {
+
+ marker = this._createMarker(latlngs[i], i);
+ marker.on('click', this._onMarkerClick, this);
+ this._markers.push(marker);
+ }
+
+ var markerLeft, markerRight;
+
+ for (i = 0, j = len - 1; i < len; j = i++) {
+ if (i === 0 && !(L.Polygon && (this._poly instanceof L.Polygon))) {
+ continue;
+ }
+
+ markerLeft = this._markers[j];
+ markerRight = this._markers[i];
+
+ this._createMiddleMarker(markerLeft, markerRight);
+ this._updatePrevNext(markerLeft, markerRight);
+ }
+ },
+
+ _createMarker: function (latlng, index) {
+ var marker = new L.Marker(latlng, {
+ draggable: true,
+ icon: this.options.icon
+ });
+
+ marker._origLatLng = latlng;
+ marker._index = index;
+
+ marker.on('drag', this._onMarkerDrag, this);
+ marker.on('dragend', this._fireEdit, this);
+
+ this._markerGroup.addLayer(marker);
+
+ return marker;
+ },
+
+ _fireEdit: function () {
+ this._poly.fire('edit');
+ },
+
+ _onMarkerDrag: function (e) {
+ var marker = e.target;
+
+ L.extend(marker._origLatLng, marker._latlng);
+
+ if (marker._middleLeft) {
+ marker._middleLeft.setLatLng(this._getMiddleLatLng(marker._prev, marker));
+ }
+ if (marker._middleRight) {
+ marker._middleRight.setLatLng(this._getMiddleLatLng(marker, marker._next));
+ }
+
+ this._poly.redraw();
+ },
+
+ _onMarkerClick: function (e) {
+ // we want to remove the marker on click, but if latlng count < 3, polyline would be invalid
+ if (this._poly._latlngs.length < 3) { return; }
+
+ var marker = e.target,
+ i = marker._index;
+
+ // remove the marker
+ this._markerGroup.removeLayer(marker);
+ this._markers.splice(i, 1);
+ this._poly.spliceLatLngs(i, 1);
+ this._updateIndexes(i, -1);
+
+ // update prev/next links of adjacent markers
+ this._updatePrevNext(marker._prev, marker._next);
+
+ // remove ghost markers near the removed marker
+ if (marker._middleLeft) {
+ this._markerGroup.removeLayer(marker._middleLeft);
+ }
+ if (marker._middleRight) {
+ this._markerGroup.removeLayer(marker._middleRight);
+ }
+
+ // create a ghost marker in place of the removed one
+ if (marker._prev && marker._next) {
+ this._createMiddleMarker(marker._prev, marker._next);
+
+ } else if (!marker._prev) {
+ marker._next._middleLeft = null;
+
+ } else if (!marker._next) {
+ marker._prev._middleRight = null;
+ }
+
+ this._poly.fire('edit');
+ },
+
+ _updateIndexes: function (index, delta) {
+ this._markerGroup.eachLayer(function (marker) {
+ if (marker._index > index) {
+ marker._index += delta;
+ }
+ });
+ },
+
+ _createMiddleMarker: function (marker1, marker2) {
+ var latlng = this._getMiddleLatLng(marker1, marker2),
+ marker = this._createMarker(latlng),
+ onClick,
+ onDragStart,
+ onDragEnd;
+
+ marker.setOpacity(0.6);
+
+ marker1._middleRight = marker2._middleLeft = marker;
+
+ onDragStart = function () {
+ var i = marker2._index;
+
+ marker._index = i;
+
+ marker
+ .off('click', onClick)
+ .on('click', this._onMarkerClick, this);
+
+ latlng.lat = marker.getLatLng().lat;
+ latlng.lng = marker.getLatLng().lng;
+ this._poly.spliceLatLngs(i, 0, latlng);
+ this._markers.splice(i, 0, marker);
+
+ marker.setOpacity(1);
+
+ this._updateIndexes(i, 1);
+ marker2._index++;
+ this._updatePrevNext(marker1, marker);
+ this._updatePrevNext(marker, marker2);
+ };
+
+ onDragEnd = function () {
+ marker.off('dragstart', onDragStart, this);
+ marker.off('dragend', onDragEnd, this);
+
+ this._createMiddleMarker(marker1, marker);
+ this._createMiddleMarker(marker, marker2);
+ };
+
+ onClick = function () {
+ onDragStart.call(this);
+ onDragEnd.call(this);
+ this._poly.fire('edit');
+ };
+
+ marker
+ .on('click', onClick, this)
+ .on('dragstart', onDragStart, this)
+ .on('dragend', onDragEnd, this);
+
+ this._markerGroup.addLayer(marker);
+ },
+
+ _updatePrevNext: function (marker1, marker2) {
+ if (marker1) {
+ marker1._next = marker2;
+ }
+ if (marker2) {
+ marker2._prev = marker1;
+ }
+ },
+
+ _getMiddleLatLng: function (marker1, marker2) {
+ var map = this._poly._map,
+ p1 = map.latLngToLayerPoint(marker1.getLatLng()),
+ p2 = map.latLngToLayerPoint(marker2.getLatLng());
+
+ return map.layerPointToLatLng(p1._add(p2)._divideBy(2));
+ }
+});
+
+L.Polyline.addInitHook(function () {
+
+ if (L.Handler.PolyEdit) {
+ this.editing = new L.Handler.PolyEdit(this);
+
+ if (this.options.editable) {
+ this.editing.enable();
+ }
+ }
+
+ this.on('add', function () {
+ if (this.editing && this.editing.enabled()) {
+ this.editing.addHooks();
+ }
+ });
+
+ this.on('remove', function () {
+ if (this.editing && this.editing.enabled()) {
+ this.editing.removeHooks();
+ }
+ });
+});
+
+
+/*
+ * L.Control is a base class for implementing map controls. Handles positioning.
+ * All other controls extend from this class.
+ */
+
+L.Control = L.Class.extend({
+ options: {
+ position: 'topright'
+ },
+
+ initialize: function (options) {
+ L.setOptions(this, options);
+ },
+
+ getPosition: function () {
+ return this.options.position;
+ },
+
+ setPosition: function (position) {
+ var map = this._map;
+
+ if (map) {
+ map.removeControl(this);
+ }
+
+ this.options.position = position;
+
+ if (map) {
+ map.addControl(this);
+ }
+
+ return this;
+ },
+
+ addTo: function (map) {
+ this._map = map;
+
+ var container = this._container = this.onAdd(map),
+ pos = this.getPosition(),
+ corner = map._controlCorners[pos];
+
+ L.DomUtil.addClass(container, 'leaflet-control');
+
+ if (pos.indexOf('bottom') !== -1) {
+ corner.insertBefore(container, corner.firstChild);
+ } else {
+ corner.appendChild(container);
+ }
+
+ return this;
+ },
+
+ removeFrom: function (map) {
+ var pos = this.getPosition(),
+ corner = map._controlCorners[pos];
+
+ corner.removeChild(this._container);
+ this._map = null;
+
+ if (this.onRemove) {
+ this.onRemove(map);
+ }
+
+ return this;
+ }
+});
+
+L.control = function (options) {
+ return new L.Control(options);
+};
+
+
+/*
+ * Adds control-related methods to L.Map.
+ */
+
+L.Map.include({
+ addControl: function (control) {
+ control.addTo(this);
+ return this;
+ },
+
+ removeControl: function (control) {
+ control.removeFrom(this);
+ return this;
+ },
+
+ _initControlPos: function () {
+ var corners = this._controlCorners = {},
+ l = 'leaflet-',
+ container = this._controlContainer =
+ L.DomUtil.create('div', l + 'control-container', this._container);
+
+ function createCorner(vSide, hSide) {
+ var className = l + vSide + ' ' + l + hSide;
+
+ corners[vSide + hSide] = L.DomUtil.create('div', className, container);
+ }
+
+ createCorner('top', 'left');
+ createCorner('top', 'right');
+ createCorner('bottom', 'left');
+ createCorner('bottom', 'right');
+ }
+});
+
+
+/*
+ * L.Control.Zoom is used for the default zoom buttons on the map.
+ */
+
+L.Control.Zoom = L.Control.extend({
+ options: {
+ position: 'topleft'
+ },
+
+ onAdd: function (map) {
+ var zoomName = 'leaflet-control-zoom',
+ barName = 'leaflet-bar',
+ partName = barName + '-part',
+ container = L.DomUtil.create('div', zoomName + ' ' + barName);
+
+ this._map = map;
+
+ this._zoomInButton = this._createButton('+', 'Zoom in',
+ zoomName + '-in ' +
+ partName + ' ' +
+ partName + '-top',
+ container, this._zoomIn, this);
+
+ this._zoomOutButton = this._createButton('-', 'Zoom out',
+ zoomName + '-out ' +
+ partName + ' ' +
+ partName + '-bottom',
+ container, this._zoomOut, this);
+
+ map.on('zoomend', this._updateDisabled, this);
+
+ return container;
+ },
+
+ onRemove: function (map) {
+ map.off('zoomend', this._updateDisabled, this);
+ },
+
+ _zoomIn: function (e) {
+ this._map.zoomIn(e.shiftKey ? 3 : 1);
+ },
+
+ _zoomOut: function (e) {
+ this._map.zoomOut(e.shiftKey ? 3 : 1);
+ },
+
+ _createButton: function (html, title, className, container, fn, context) {
+ var link = L.DomUtil.create('a', className, container);
+ link.innerHTML = html;
+ link.href = '#';
+ link.title = title;
+
+ var stop = L.DomEvent.stopPropagation;
+
+ L.DomEvent
+ .on(link, 'click', stop)
+ .on(link, 'mousedown', stop)
+ .on(link, 'dblclick', stop)
+ .on(link, 'click', L.DomEvent.preventDefault)
+ .on(link, 'click', fn, context);
+
+ return link;
+ },
+
+ _updateDisabled: function () {
+ var map = this._map,
+ className = 'leaflet-control-zoom-disabled';
+
+ L.DomUtil.removeClass(this._zoomInButton, className);
+ L.DomUtil.removeClass(this._zoomOutButton, className);
+
+ if (map._zoom === map.getMinZoom()) {
+ L.DomUtil.addClass(this._zoomOutButton, className);
+ }
+ if (map._zoom === map.getMaxZoom()) {
+ L.DomUtil.addClass(this._zoomInButton, className);
+ }
+ }
+});
+
+L.Map.mergeOptions({
+ zoomControl: true
+});
+
+L.Map.addInitHook(function () {
+ if (this.options.zoomControl) {
+ this.zoomControl = new L.Control.Zoom();
+ this.addControl(this.zoomControl);
+ }
+});
+
+L.control.zoom = function (options) {
+ return new L.Control.Zoom(options);
+};
+
+
+
+/*
+ * L.Control.Attribution is used for displaying attribution on the map (added by default).
+ */
+
+L.Control.Attribution = L.Control.extend({
+ options: {
+ position: 'bottomright',
+ prefix: 'Powered by <a href="http://leafletjs.com">Leaflet</a>'
+ },
+
+ initialize: function (options) {
+ L.setOptions(this, options);
+
+ this._attributions = {};
+ },
+
+ onAdd: function (map) {
+ this._container = L.DomUtil.create('div', 'leaflet-control-attribution');
+ L.DomEvent.disableClickPropagation(this._container);
+
+ map
+ .on('layeradd', this._onLayerAdd, this)
+ .on('layerremove', this._onLayerRemove, this);
+
+ this._update();
+
+ return this._container;
+ },
+
+ onRemove: function (map) {
+ map
+ .off('layeradd', this._onLayerAdd)
+ .off('layerremove', this._onLayerRemove);
+
+ },
+
+ setPrefix: function (prefix) {
+ this.options.prefix = prefix;
+ this._update();
+ return this;
+ },
+
+ addAttribution: function (text) {
+ if (!text) { return; }
+
+ if (!this._attributions[text]) {
+ this._attributions[text] = 0;
+ }
+ this._attributions[text]++;
+
+ this._update();
+
+ return this;
+ },
+
+ removeAttribution: function (text) {
+ if (!text) { return; }
+
+ this._attributions[text]--;
+ this._update();
+
+ return this;
+ },
+
+ _update: function () {
+ if (!this._map) { return; }
+
+ var attribs = [];
+
+ for (var i in this._attributions) {
+ if (this._attributions.hasOwnProperty(i) && this._attributions[i]) {
+ attribs.push(i);
+ }
+ }
+
+ var prefixAndAttribs = [];
+
+ if (this.options.prefix) {
+ prefixAndAttribs.push(this.options.prefix);
+ }
+ if (attribs.length) {
+ prefixAndAttribs.push(attribs.join(', '));
+ }
+
+ this._container.innerHTML = prefixAndAttribs.join(' — ');
+ },
+
+ _onLayerAdd: function (e) {
+ if (e.layer.getAttribution) {
+ this.addAttribution(e.layer.getAttribution());
+ }
+ },
+
+ _onLayerRemove: function (e) {
+ if (e.layer.getAttribution) {
+ this.removeAttribution(e.layer.getAttribution());
+ }
+ }
+});
+
+L.Map.mergeOptions({
+ attributionControl: true
+});
+
+L.Map.addInitHook(function () {
+ if (this.options.attributionControl) {
+ this.attributionControl = (new L.Control.Attribution()).addTo(this);
+ }
+});
+
+L.control.attribution = function (options) {
+ return new L.Control.Attribution(options);
+};
+
+
+/*
+ * L.Control.Scale is used for displaying metric/imperial scale on the map.
+ */
+
+L.Control.Scale = L.Control.extend({
+ options: {
+ position: 'bottomleft',
+ maxWidth: 100,
+ metric: true,
+ imperial: true,
+ updateWhenIdle: false
+ },
+
+ onAdd: function (map) {
+ this._map = map;
+
+ var className = 'leaflet-control-scale',
+ container = L.DomUtil.create('div', className),
+ options = this.options;
+
+ this._addScales(options, className, container);
+
+ map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
+ map.whenReady(this._update, this);
+
+ return container;
+ },
+
+ onRemove: function (map) {
+ map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
+ },
+
+ _addScales: function (options, className, container) {
+ if (options.metric) {
+ this._mScale = L.DomUtil.create('div', className + '-line', container);
+ }
+ if (options.imperial) {
+ this._iScale = L.DomUtil.create('div', className + '-line', container);
+ }
+ },
+
+ _update: function () {
+ var bounds = this._map.getBounds(),
+ centerLat = bounds.getCenter().lat,
+ halfWorldMeters = 6378137 * Math.PI * Math.cos(centerLat * Math.PI / 180),
+ dist = halfWorldMeters * (bounds.getNorthEast().lng - bounds.getSouthWest().lng) / 180,
+
+ size = this._map.getSize(),
+ options = this.options,
+ maxMeters = 0;
+
+ if (size.x > 0) {
+ maxMeters = dist * (options.maxWidth / size.x);
+ }
+
+ this._updateScales(options, maxMeters);
+ },
+
+ _updateScales: function (options, maxMeters) {
+ if (options.metric && maxMeters) {
+ this._updateMetric(maxMeters);
+ }
+
+ if (options.imperial && maxMeters) {
+ this._updateImperial(maxMeters);
+ }
+ },
+
+ _updateMetric: function (maxMeters) {
+ var meters = this._getRoundNum(maxMeters);
+
+ this._mScale.style.width = this._getScaleWidth(meters / maxMeters) + 'px';
+ this._mScale.innerHTML = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
+ },
+
+ _updateImperial: function (maxMeters) {
+ var maxFeet = maxMeters * 3.2808399,
+ scale = this._iScale,
+ maxMiles, miles, feet;
+
+ if (maxFeet > 5280) {
+ maxMiles = maxFeet / 5280;
+ miles = this._getRoundNum(maxMiles);
+
+ scale.style.width = this._getScaleWidth(miles / maxMiles) + 'px';
+ scale.innerHTML = miles + ' mi';
+
+ } else {
+ feet = this._getRoundNum(maxFeet);
+
+ scale.style.width = this._getScaleWidth(feet / maxFeet) + 'px';
+ scale.innerHTML = feet + ' ft';
+ }
+ },
+
+ _getScaleWidth: function (ratio) {
+ return Math.round(this.options.maxWidth * ratio) - 10;
+ },
+
+ _getRoundNum: function (num) {
+ var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1),
+ d = num / pow10;
+
+ d = d >= 10 ? 10 : d >= 5 ? 5 : d >= 3 ? 3 : d >= 2 ? 2 : 1;
+
+ return pow10 * d;
+ }
+});
+
+L.control.scale = function (options) {
+ return new L.Control.Scale(options);
+};
+
+
+/*
+ * L.Control.Layers is a control to allow users to switch between different layers on the map.
+ */
+
+L.Control.Layers = L.Control.extend({
+ options: {
+ collapsed: true,
+ position: 'topright',
+ autoZIndex: true
+ },
+
+ initialize: function (baseLayers, overlays, options) {
+ L.setOptions(this, options);
+
+ this._layers = {};
+ this._lastZIndex = 0;
+ this._handlingClick = false;
+
+ for (var i in baseLayers) {
+ if (baseLayers.hasOwnProperty(i)) {
+ this._addLayer(baseLayers[i], i);
+ }
+ }
+
+ for (i in overlays) {
+ if (overlays.hasOwnProperty(i)) {
+ this._addLayer(overlays[i], i, true);
+ }
+ }
+ },
+
+ onAdd: function (map) {
+ this._initLayout();
+ this._update();
+
+ map
+ .on('layeradd', this._onLayerChange, this)
+ .on('layerremove', this._onLayerChange, this);
+
+ return this._container;
+ },
+
+ onRemove: function (map) {
+ map
+ .off('layeradd', this._onLayerChange)
+ .off('layerremove', this._onLayerChange);
+ },
+
+ addBaseLayer: function (layer, name) {
+ this._addLayer(layer, name);
+ this._update();
+ return this;
+ },
+
+ addOverlay: function (layer, name) {
+ this._addLayer(layer, name, true);
+ this._update();
+ return this;
+ },
+
+ removeLayer: function (layer) {
+ var id = L.stamp(layer);
+ delete this._layers[id];
+ this._update();
+ return this;
+ },
+
+ _initLayout: function () {
+ var className = 'leaflet-control-layers',
+ container = this._container = L.DomUtil.create('div', className);
+
+ if (!L.Browser.touch) {
+ L.DomEvent.disableClickPropagation(container);
+ L.DomEvent.on(container, 'mousewheel', L.DomEvent.stopPropagation);
+ } else {
+ L.DomEvent.on(container, 'click', L.DomEvent.stopPropagation);
+ }
+
+ var form = this._form = L.DomUtil.create('form', className + '-list');
+
+ if (this.options.collapsed) {
+ L.DomEvent
+ .on(container, 'mouseover', this._expand, this)
+ .on(container, 'mouseout', this._collapse, this);
+
+ var link = this._layersLink = L.DomUtil.create('a', className + '-toggle', container);
+ link.href = '#';
+ link.title = 'Layers';
+
+ if (L.Browser.touch) {
+ L.DomEvent
+ .on(link, 'click', L.DomEvent.stopPropagation)
+ .on(link, 'click', L.DomEvent.preventDefault)
+ .on(link, 'click', this._expand, this);
+ }
+ else {
+ L.DomEvent.on(link, 'focus', this._expand, this);
+ }
+
+ this._map.on('movestart', this._collapse, this);
+ // TODO keyboard accessibility
+ } else {
+ this._expand();
+ }
+
+ this._baseLayersList = L.DomUtil.create('div', className + '-base', form);
+ this._separator = L.DomUtil.create('div', className + '-separator', form);
+ this._overlaysList = L.DomUtil.create('div', className + '-overlays', form);
+
+ container.appendChild(form);
+ },
+
+ _addLayer: function (layer, name, overlay) {
+ var id = L.stamp(layer);
+
+ this._layers[id] = {
+ layer: layer,
+ name: name,
+ overlay: overlay
+ };
+
+ if (this.options.autoZIndex && layer.setZIndex) {
+ this._lastZIndex++;
+ layer.setZIndex(this._lastZIndex);
+ }
+ },
+
+ _update: function () {
+ if (!this._container) {
+ return;
+ }
+
+ this._baseLayersList.innerHTML = '';
+ this._overlaysList.innerHTML = '';
+
+ var baseLayersPresent = false,
+ overlaysPresent = false;
+
+ for (var i in this._layers) {
+ if (this._layers.hasOwnProperty(i)) {
+ var obj = this._layers[i];
+ this._addItem(obj);
+ overlaysPresent = overlaysPresent || obj.overlay;
+ baseLayersPresent = baseLayersPresent || !obj.overlay;
+ }
+ }
+
+ this._separator.style.display = (overlaysPresent && baseLayersPresent ? '' : 'none');
+ },
+
+ _onLayerChange: function (e) {
+ var id = L.stamp(e.layer);
+
+ if (this._layers[id] && !this._handlingClick) {
+ this._update();
+ }
+ },
+
+ // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe)
+ _createRadioElement: function (name, checked) {
+
+ var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' + name + '"';
+ if (checked) {
+ radioHtml += ' checked="checked"';
+ }
+ radioHtml += '/>';
+
+ var radioFragment = document.createElement('div');
+ radioFragment.innerHTML = radioHtml;
+
+ return radioFragment.firstChild;
+ },
+
+ _addItem: function (obj) {
+ var label = document.createElement('label'),
+ input,
+ checked = this._map.hasLayer(obj.layer);
+
+ if (obj.overlay) {
+ input = document.createElement('input');
+ input.type = 'checkbox';
+ input.className = 'leaflet-control-layers-selector';
+ input.defaultChecked = checked;
+ } else {
+ input = this._createRadioElement('leaflet-base-layers', checked);
+ }
+
+ input.layerId = L.stamp(obj.layer);
+
+ L.DomEvent.on(input, 'click', this._onInputClick, this);
+
+ var name = document.createElement('span');
+ name.innerHTML = ' ' + obj.name;
+
+ label.appendChild(input);
+ label.appendChild(name);
+
+ var container = obj.overlay ? this._overlaysList : this._baseLayersList;
+ container.appendChild(label);
+
+ return label;
+ },
+
+ _onInputClick: function () {
+ var i, input, obj,
+ inputs = this._form.getElementsByTagName('input'),
+ inputsLen = inputs.length,
+ baseLayer;
+
+ this._handlingClick = true;
+
+ for (i = 0; i < inputsLen; i++) {
+ input = inputs[i];
+ obj = this._layers[input.layerId];
+
+ if (input.checked && !this._map.hasLayer(obj.layer)) {
+ this._map.addLayer(obj.layer);
+ if (!obj.overlay) {
+ baseLayer = obj.layer;
+ }
+ } else if (!input.checked && this._map.hasLayer(obj.layer)) {
+ this._map.removeLayer(obj.layer);
+ }
+ }
+
+ if (baseLayer) {
+ this._map.setZoom(this._map.getZoom());
+ this._map.fire('baselayerchange', {layer: baseLayer});
+ }
+
+ this._handlingClick = false;
+ },
+
+ _expand: function () {
+ L.DomUtil.addClass(this._container, 'leaflet-control-layers-expanded');
+ },
+
+ _collapse: function () {
+ this._container.className = this._container.className.replace(' leaflet-control-layers-expanded', '');
+ }
+});
+
+L.control.layers = function (baseLayers, overlays, options) {
+ return new L.Control.Layers(baseLayers, overlays, options);
+};
+
+
+/*
+ * L.PosAnimation is used by Leaflet internally for pan animations.
+ */
+
+L.PosAnimation = L.Class.extend({
+ includes: L.Mixin.Events,
+
+ run: function (el, newPos, duration, easeLinearity) { // (HTMLElement, Point[, Number, Number])
+ this.stop();
+
+ this._el = el;
+ this._inProgress = true;
+
+ this.fire('start');
+
+ el.style[L.DomUtil.TRANSITION] = 'all ' + (duration || 0.25) +
+ 's cubic-bezier(0,0,' + (easeLinearity || 0.5) + ',1)';
+
+ L.DomEvent.on(el, L.DomUtil.TRANSITION_END, this._onTransitionEnd, this);
+ L.DomUtil.setPosition(el, newPos);
+
+ // toggle reflow, Chrome flickers for some reason if you don't do this
+ L.Util.falseFn(el.offsetWidth);
+
+ // there's no native way to track value updates of transitioned properties, so we imitate this
+ this._stepTimer = setInterval(L.bind(this.fire, this, 'step'), 50);
+ },
+
+ stop: function () {
+ if (!this._inProgress) { return; }
+
+ // if we just removed the transition property, the element would jump to its final position,
+ // so we need to make it stay at the current position
+
+ L.DomUtil.setPosition(this._el, this._getPos());
+ this._onTransitionEnd();
+ L.Util.falseFn(this._el.offsetWidth); // force reflow in case we are about to start a new animation
+ },
+
+ // you can't easily get intermediate values of properties animated with CSS3 Transitions,
+ // we need to parse computed style (in case of transform it returns matrix string)
+
+ _transformRe: /(-?[\d\.]+), (-?[\d\.]+)\)/,
+
+ _getPos: function () {
+ var left, top, matches,
+ el = this._el,
+ style = window.getComputedStyle(el);
+
+ if (L.Browser.any3d) {
+ matches = style[L.DomUtil.TRANSFORM].match(this._transformRe);
+ left = parseFloat(matches[1]);
+ top = parseFloat(matches[2]);
+ } else {
+ left = parseFloat(style.left);
+ top = parseFloat(style.top);
+ }
+
+ return new L.Point(left, top, true);
+ },
+
+ _onTransitionEnd: function () {
+ L.DomEvent.off(this._el, L.DomUtil.TRANSITION_END, this._onTransitionEnd, this);
+
+ if (!this._inProgress) { return; }
+ this._inProgress = false;
+
+ this._el.style[L.DomUtil.TRANSITION] = '';
+
+ clearInterval(this._stepTimer);
+
+ this.fire('step').fire('end');
+ }
+
+});
+
+
+/*
+ * Extends L.Map to handle panning animations.
+ */
+
+L.Map.include({
+
+ setView: function (center, zoom, forceReset) {
+ zoom = this._limitZoom(zoom);
+
+ var zoomChanged = (this._zoom !== zoom);
+
+ if (this._loaded && !forceReset && this._layers) {
+
+ if (this._panAnim) {
+ this._panAnim.stop();
+ }
+
+ var done = (zoomChanged ?
+ this._zoomToIfClose && this._zoomToIfClose(center, zoom) :
+ this._panByIfClose(center));
+
+ // exit if animated pan or zoom started
+ if (done) {
+ clearTimeout(this._sizeTimer);
+ return this;
+ }
+ }
+
+ // reset the map view
+ this._resetView(center, zoom);
+
+ return this;
+ },
+
+ panBy: function (offset, duration, easeLinearity) {
+ offset = L.point(offset);
+
+ if (!(offset.x || offset.y)) {
+ return this;
+ }
+
+ if (!this._panAnim) {
+ this._panAnim = new L.PosAnimation();
+
+ this._panAnim.on({
+ 'step': this._onPanTransitionStep,
+ 'end': this._onPanTransitionEnd
+ }, this);
+ }
+
+ this.fire('movestart');
+
+ L.DomUtil.addClass(this._mapPane, 'leaflet-pan-anim');
+
+ var newPos = L.DomUtil.getPosition(this._mapPane).subtract(offset)._round();
+ this._panAnim.run(this._mapPane, newPos, duration || 0.25, easeLinearity);
+
+ return this;
+ },
+
+ _onPanTransitionStep: function () {
+ this.fire('move');
+ },
+
+ _onPanTransitionEnd: function () {
+ L.DomUtil.removeClass(this._mapPane, 'leaflet-pan-anim');
+ this.fire('moveend');
+ },
+
+ _panByIfClose: function (center) {
+ // difference between the new and current centers in pixels
+ var offset = this._getCenterOffset(center)._floor();
+
+ if (this._offsetIsWithinView(offset)) {
+ this.panBy(offset);
+ return true;
+ }
+ return false;
+ },
+
+ _offsetIsWithinView: function (offset, multiplyFactor) {
+ var m = multiplyFactor || 1,
+ size = this.getSize();
+
+ return (Math.abs(offset.x) <= size.x * m) &&
+ (Math.abs(offset.y) <= size.y * m);
+ }
+});
+
+
+/*
+ * L.PosAnimation fallback implementation that powers Leaflet pan animations
+ * in browsers that don't support CSS3 Transitions.
+ */
+
+L.PosAnimation = L.DomUtil.TRANSITION ? L.PosAnimation : L.PosAnimation.extend({
+
+ run: function (el, newPos, duration, easeLinearity) { // (HTMLElement, Point[, Number, Number])
+ this.stop();
+
+ this._el = el;
+ this._inProgress = true;
+ this._duration = duration || 0.25;
+ this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2);
+
+ this._startPos = L.DomUtil.getPosition(el);
+ this._offset = newPos.subtract(this._startPos);
+ this._startTime = +new Date();
+
+ this.fire('start');
+
+ this._animate();
+ },
+
+ stop: function () {
+ if (!this._inProgress) { return; }
+
+ this._step();
+ this._complete();
+ },
+
+ _animate: function () {
+ // animation loop
+ this._animId = L.Util.requestAnimFrame(this._animate, this);
+ this._step();
+ },
+
+ _step: function () {
+ var elapsed = (+new Date()) - this._startTime,
+ duration = this._duration * 1000;
+
+ if (elapsed < duration) {
+ this._runFrame(this._easeOut(elapsed / duration));
+ } else {
+ this._runFrame(1);
+ this._complete();
+ }
+ },
+
+ _runFrame: function (progress) {
+ var pos = this._startPos.add(this._offset.multiplyBy(progress));
+ L.DomUtil.setPosition(this._el, pos);
+
+ this.fire('step');
+ },
+
+ _complete: function () {
+ L.Util.cancelAnimFrame(this._animId);
+
+ this._inProgress = false;
+ this.fire('end');
+ },
+
+ _easeOut: function (t) {
+ return 1 - Math.pow(1 - t, this._easeOutPower);
+ }
+});
+
+
+/*
+ * Extends L.Map to handle zoom animations.
+ */
+
+L.Map.mergeOptions({
+ zoomAnimation: L.DomUtil.TRANSITION && !L.Browser.android23 && !L.Browser.mobileOpera
+});
+
+if (L.DomUtil.TRANSITION) {
+ L.Map.addInitHook(function () {
+ L.DomEvent.on(this._mapPane, L.DomUtil.TRANSITION_END, this._catchTransitionEnd, this);
+ });
+}
+
+L.Map.include(!L.DomUtil.TRANSITION ? {} : {
+
+ _zoomToIfClose: function (center, zoom) {
+
+ if (this._animatingZoom) { return true; }
+
+ if (!this.options.zoomAnimation) { return false; }
+
+ var scale = this.getZoomScale(zoom),
+ offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
+
+ // if offset does not exceed half of the view
+ if (!this._offsetIsWithinView(offset, 1)) { return false; }
+
+ L.DomUtil.addClass(this._mapPane, 'leaflet-zoom-anim');
+
+ this
+ .fire('movestart')
+ .fire('zoomstart');
+
+ this.fire('zoomanim', {
+ center: center,
+ zoom: zoom
+ });
+
+ var origin = this._getCenterLayerPoint().add(offset);
+
+ this._prepareTileBg();
+ this._runAnimation(center, zoom, scale, origin);
+
+ return true;
+ },
+
+ _catchTransitionEnd: function () {
+ if (this._animatingZoom) {
+ this._onZoomTransitionEnd();
+ }
+ },
+
+ _runAnimation: function (center, zoom, scale, origin, backwardsTransform) {
+ this._animateToCenter = center;
+ this._animateToZoom = zoom;
+ this._animatingZoom = true;
+
+ if (L.Draggable) {
+ L.Draggable._disabled = true;
+ }
+
+ var transform = L.DomUtil.TRANSFORM,
+ tileBg = this._tileBg;
+
+ clearTimeout(this._clearTileBgTimer);
+
+ L.Util.falseFn(tileBg.offsetWidth); //hack to make sure transform is updated before running animation
+
+ var scaleStr = L.DomUtil.getScaleString(scale, origin),
+ oldTransform = tileBg.style[transform];
+
+ tileBg.style[transform] = backwardsTransform ?
+ oldTransform + ' ' + scaleStr :
+ scaleStr + ' ' + oldTransform;
+ },
+
+ _prepareTileBg: function () {
+ var tilePane = this._tilePane,
+ tileBg = this._tileBg;
+
+ // If foreground layer doesn't have many tiles but bg layer does, keep the existing bg layer and just zoom it some more
+ if (tileBg && this._getLoadedTilesPercentage(tileBg) > 0.5 &&
+ this._getLoadedTilesPercentage(tilePane) < 0.5) {
+
+ tilePane.style.visibility = 'hidden';
+ tilePane.empty = true;
+ this._stopLoadingImages(tilePane);
+ return;
+ }
+
+ if (!tileBg) {
+ tileBg = this._tileBg = this._createPane('leaflet-tile-pane', this._mapPane);
+ tileBg.style.zIndex = 1;
+ }
+
+ // prepare the background pane to become the main tile pane
+ tileBg.style[L.DomUtil.TRANSFORM] = '';
+ tileBg.style.visibility = 'hidden';
+
+ // tells tile layers to reinitialize their containers
+ tileBg.empty = true; //new FG
+ tilePane.empty = false; //new BG
+
+ //Switch out the current layer to be the new bg layer (And vice-versa)
+ this._tilePane = this._panes.tilePane = tileBg;
+ var newTileBg = this._tileBg = tilePane;
+
+ L.DomUtil.addClass(newTileBg, 'leaflet-zoom-animated');
+
+ this._stopLoadingImages(newTileBg);
+ },
+
+ _getLoadedTilesPercentage: function (container) {
+ var tiles = container.getElementsByTagName('img'),
+ i, len, count = 0;
+
+ for (i = 0, len = tiles.length; i < len; i++) {
+ if (tiles[i].complete) {
+ count++;
+ }
+ }
+ return count / len;
+ },
+
+ // stops loading all tiles in the background layer
+ _stopLoadingImages: function (container) {
+ var tiles = Array.prototype.slice.call(container.getElementsByTagName('img')),
+ i, len, tile;
+
+ for (i = 0, len = tiles.length; i < len; i++) {
+ tile = tiles[i];
+
+ if (!tile.complete) {
+ tile.onload = L.Util.falseFn;
+ tile.onerror = L.Util.falseFn;
+ tile.src = L.Util.emptyImageUrl;
+
+ tile.parentNode.removeChild(tile);
+ }
+ }
+ },
+
+ _onZoomTransitionEnd: function () {
+ this._restoreTileFront();
+
+ L.DomUtil.removeClass(this._mapPane, 'leaflet-zoom-anim');
+ L.Util.falseFn(this._tileBg.offsetWidth); // force reflow
+ this._animatingZoom = false;
+ this._resetView(this._animateToCenter, this._animateToZoom, true, true);
+
+ if (L.Draggable) {
+ L.Draggable._disabled = false;
+ }
+ },
+
+ _restoreTileFront: function () {
+ this._tilePane.innerHTML = '';
+ this._tilePane.style.visibility = '';
+ this._tilePane.style.zIndex = 2;
+ this._tileBg.style.zIndex = 1;
+ },
+
+ _clearTileBg: function () {
+ if (!this._animatingZoom && !this.touchZoom._zooming) {
+ this._tileBg.innerHTML = '';
+ }
+ }
+});
+
+
+/*
+ * Provides L.Map with convenient shortcuts for using browser geolocation features.
+ */
+
+L.Map.include({
+ _defaultLocateOptions: {
+ watch: false,
+ setView: false,
+ maxZoom: Infinity,
+ timeout: 10000,
+ maximumAge: 0,
+ enableHighAccuracy: false
+ },
+
+ locate: function (/*Object*/ options) {
+
+ options = this._locationOptions = L.extend(this._defaultLocateOptions, options);
+
+ if (!navigator.geolocation) {
+ this._handleGeolocationError({
+ code: 0,
+ message: "Geolocation not supported."
+ });
+ return this;
+ }
+
+ var onResponse = L.bind(this._handleGeolocationResponse, this),
+ onError = L.bind(this._handleGeolocationError, this);
+
+ if (options.watch) {
+ this._locationWatchId =
+ navigator.geolocation.watchPosition(onResponse, onError, options);
+ } else {
+ navigator.geolocation.getCurrentPosition(onResponse, onError, options);
+ }
+ return this;
+ },
+
+ stopLocate: function () {
+ if (navigator.geolocation) {
+ navigator.geolocation.clearWatch(this._locationWatchId);
+ }
+ return this;
+ },
+
+ _handleGeolocationError: function (error) {
+ var c = error.code,
+ message = error.message ||
+ (c === 1 ? "permission denied" :
+ (c === 2 ? "position unavailable" : "timeout"));
+
+ if (this._locationOptions.setView && !this._loaded) {
+ this.fitWorld();
+ }
+
+ this.fire('locationerror', {
+ code: c,
+ message: "Geolocation error: " + message + "."
+ });
+ },
+
+ _handleGeolocationResponse: function (pos) {
+ var latAccuracy = 180 * pos.coords.accuracy / 4e7,
+ lngAccuracy = latAccuracy * 2,
+
+ lat = pos.coords.latitude,
+ lng = pos.coords.longitude,
+ latlng = new L.LatLng(lat, lng),
+
+ sw = new L.LatLng(lat - latAccuracy, lng - lngAccuracy),
+ ne = new L.LatLng(lat + latAccuracy, lng + lngAccuracy),
+ bounds = new L.LatLngBounds(sw, ne),
+
+ options = this._locationOptions;
+
+ if (options.setView) {
+ var zoom = Math.min(this.getBoundsZoom(bounds), options.maxZoom);
+ this.setView(latlng, zoom);
+ }
+
+ this.fire('locationfound', {
+ latlng: latlng,
+ bounds: bounds,
+ accuracy: pos.coords.accuracy
+ });
+ }
+});
+
+
+}(this, document));
\ No newline at end of file
Added: sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/leaflet.css
===================================================================
--- sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/leaflet.css (rev 0)
+++ sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/leaflet.css 2013-06-19 11:24:48 UTC (rev 97)
@@ -0,0 +1,457 @@
+/* %%Ignore-License required styles */
+
+.leaflet-map-pane,
+.leaflet-tile,
+.leaflet-marker-icon,
+.leaflet-marker-shadow,
+.leaflet-tile-pane,
+.leaflet-overlay-pane,
+.leaflet-shadow-pane,
+.leaflet-marker-pane,
+.leaflet-popup-pane,
+.leaflet-overlay-pane svg,
+.leaflet-zoom-box,
+.leaflet-image-layer,
+.leaflet-layer {
+ position: absolute;
+ left: 0;
+ top: 0;
+ }
+.leaflet-container {
+ overflow: hidden;
+ -ms-touch-action: none;
+ }
+.leaflet-tile,
+.leaflet-marker-icon,
+.leaflet-marker-shadow {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none;
+ }
+.leaflet-marker-icon,
+.leaflet-marker-shadow {
+ display: block;
+ }
+/* map is broken in FF if you have max-width: 100% on tiles */
+.leaflet-container img {
+ max-width: none !important;
+ }
+/* stupid Android 2 doesn't understand "max-width: none" properly */
+.leaflet-container img.leaflet-image-layer {
+ max-width: 15000px !important;
+ }
+.leaflet-tile {
+ filter: inherit;
+ visibility: hidden;
+ }
+.leaflet-tile-loaded {
+ visibility: inherit;
+ }
+.leaflet-zoom-box {
+ width: 0;
+ height: 0;
+ }
+
+.leaflet-tile-pane { z-index: 2; }
+.leaflet-objects-pane { z-index: 3; }
+.leaflet-overlay-pane { z-index: 4; }
+.leaflet-shadow-pane { z-index: 5; }
+.leaflet-marker-pane { z-index: 6; }
+.leaflet-popup-pane { z-index: 7; }
+
+
+/* control positioning */
+
+.leaflet-control {
+ position: relative;
+ z-index: 7;
+ pointer-events: auto;
+ }
+.leaflet-top,
+.leaflet-bottom {
+ position: absolute;
+ z-index: 1000;
+ pointer-events: none;
+ }
+.leaflet-top {
+ top: 0;
+ }
+.leaflet-right {
+ right: 0;
+ }
+.leaflet-bottom {
+ bottom: 0;
+ }
+.leaflet-left {
+ left: 0;
+ }
+.leaflet-control {
+ float: left;
+ clear: both;
+ }
+.leaflet-right .leaflet-control {
+ float: right;
+ }
+.leaflet-top .leaflet-control {
+ margin-top: 10px;
+ }
+.leaflet-bottom .leaflet-control {
+ margin-bottom: 10px;
+ }
+.leaflet-left .leaflet-control {
+ margin-left: 10px;
+ }
+.leaflet-right .leaflet-control {
+ margin-right: 10px;
+ }
+
+
+/* zoom and fade animations */
+
+.leaflet-fade-anim .leaflet-tile,
+.leaflet-fade-anim .leaflet-popup {
+ opacity: 0;
+ -webkit-transition: opacity 0.2s linear;
+ -moz-transition: opacity 0.2s linear;
+ -o-transition: opacity 0.2s linear;
+ transition: opacity 0.2s linear;
+ }
+.leaflet-fade-anim .leaflet-tile-loaded,
+.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
+ opacity: 1;
+ }
+
+.leaflet-zoom-anim .leaflet-zoom-animated {
+ -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
+ -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
+ -o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1);
+ transition: transform 0.25s cubic-bezier(0,0,0.25,1);
+ }
+.leaflet-zoom-anim .leaflet-tile,
+.leaflet-pan-anim .leaflet-tile,
+.leaflet-touching .leaflet-zoom-animated {
+ -webkit-transition: none;
+ -moz-transition: none;
+ -o-transition: none;
+ transition: none;
+ }
+
+.leaflet-zoom-anim .leaflet-zoom-hide {
+ visibility: hidden;
+ }
+
+
+/* cursors */
+
+.leaflet-clickable {
+ cursor: pointer;
+ }
+.leaflet-container {
+ cursor: -webkit-grab;
+ cursor: -moz-grab;
+ }
+.leaflet-popup-pane,
+.leaflet-control {
+ cursor: auto;
+ }
+.leaflet-dragging,
+.leaflet-dragging .leaflet-clickable,
+.leaflet-dragging .leaflet-container {
+ cursor: move;
+ cursor: -webkit-grabbing;
+ cursor: -moz-grabbing;
+ }
+
+
+/* visual tweaks */
+
+.leaflet-container {
+ background: #ddd;
+ outline: 0;
+ }
+.leaflet-container a {
+ color: #0078A8;
+ }
+.leaflet-container a.leaflet-active {
+ outline: 2px solid orange;
+ }
+.leaflet-zoom-box {
+ border: 2px dotted #05f;
+ background: white;
+ opacity: 0.5;
+ }
+
+
+/* general typography */
+.leaflet-container {
+ font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif;
+ }
+
+
+/* general toolbar styles */
+
+.leaflet-bar {
+ box-shadow: 0 0 8px rgba(0,0,0,0.4);
+ border: 1px solid #888;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+ }
+.leaflet-bar-part {
+ background-color: rgba(255, 255, 255, 0.8);
+ border-bottom: 1px solid #aaa;
+ }
+.leaflet-bar-part-top {
+ -webkit-border-radius: 4px 4px 0 0;
+ border-radius: 4px 4px 0 0;
+ }
+.leaflet-bar-part-bottom {
+ -webkit-border-radius: 0 0 4px 4px;
+ border-radius: 0 0 4px 4px;
+ border-bottom: none;
+ }
+
+.leaflet-touch .leaflet-bar {
+ -webkit-border-radius: 10px;
+ border-radius: 10px;
+ }
+.leaflet-touch .leaflet-bar-part {
+ border-bottom: 4px solid rgba(0,0,0,0.3);
+ }
+.leaflet-touch .leaflet-bar-part-top {
+ -webkit-border-radius: 7px 7px 0 0;
+ border-radius: 7px 7px 0 0;
+ }
+.leaflet-touch .leaflet-bar-part-bottom {
+ -webkit-border-radius: 0 0 7px 7px;
+ border-radius: 0 0 7px 7px;
+ border-bottom: none;
+ }
+
+
+/* zoom control */
+
+.leaflet-container .leaflet-control-zoom {
+ margin-left: 13px;
+ margin-top: 12px;
+ }
+.leaflet-control-zoom a {
+ width: 22px;
+ height: 22px;
+ text-align: center;
+ text-decoration: none;
+ color: black;
+ }
+.leaflet-control-zoom a,
+.leaflet-control-layers-toggle {
+ background-position: 50% 50%;
+ background-repeat: no-repeat;
+ display: block;
+ }
+.leaflet-control-zoom a:hover {
+ background-color: #fff;
+ color: #777;
+ }
+.leaflet-control-zoom-in {
+ font: bold 18px/24px Arial, Helvetica, sans-serif;
+ }
+.leaflet-control-zoom-out {
+ font: bold 23px/20px Tahoma, Verdana, sans-serif;
+ }
+.leaflet-control-zoom a.leaflet-control-zoom-disabled {
+ cursor: default;
+ background-color: rgba(255, 255, 255, 0.8);
+ color: #bbb;
+ }
+
+.leaflet-touch .leaflet-control-zoom a {
+ width: 30px;
+ height: 30px;
+ }
+.leaflet-touch .leaflet-control-zoom-in {
+ font-size: 24px;
+ line-height: 29px;
+ }
+.leaflet-touch .leaflet-control-zoom-out {
+ font-size: 28px;
+ line-height: 24px;
+ }
+
+/* layers control */
+
+.leaflet-control-layers {
+ box-shadow: 0 1px 7px rgba(0,0,0,0.4);
+ background: #f8f8f9;
+ -webkit-border-radius: 8px;
+ border-radius: 8px;
+ }
+.leaflet-control-layers-toggle {
+ background-image: url(images/layers.png);
+ width: 36px;
+ height: 36px;
+ }
+.leaflet-touch .leaflet-control-layers-toggle {
+ width: 44px;
+ height: 44px;
+ }
+.leaflet-control-layers .leaflet-control-layers-list,
+.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
+ display: none;
+ }
+.leaflet-control-layers-expanded .leaflet-control-layers-list {
+ display: block;
+ position: relative;
+ }
+.leaflet-control-layers-expanded {
+ padding: 6px 10px 6px 6px;
+ color: #333;
+ background: #fff;
+ }
+.leaflet-control-layers-selector {
+ margin-top: 2px;
+ position: relative;
+ top: 1px;
+ }
+.leaflet-control-layers label {
+ display: block;
+ }
+.leaflet-control-layers-separator {
+ height: 0;
+ border-top: 1px solid #ddd;
+ margin: 5px -10px 5px -6px;
+ }
+
+
+/* attribution and scale controls */
+
+.leaflet-container .leaflet-control-attribution {
+ background-color: rgba(255, 255, 255, 0.7);
+ box-shadow: 0 0 5px #bbb;
+ margin: 0;
+ }
+.leaflet-control-attribution,
+.leaflet-control-scale-line {
+ padding: 0 5px;
+ color: #333;
+ }
+.leaflet-container .leaflet-control-attribution,
+.leaflet-container .leaflet-control-scale {
+ font-size: 11px;
+ }
+.leaflet-left .leaflet-control-scale {
+ margin-left: 5px;
+ }
+.leaflet-bottom .leaflet-control-scale {
+ margin-bottom: 5px;
+ }
+.leaflet-control-scale-line {
+ border: 2px solid #777;
+ border-top: none;
+ color: black;
+ line-height: 1.1;
+ padding: 2px 5px 1px;
+ font-size: 11px;
+ text-shadow: 1px 1px 1px #fff;
+ background-color: rgba(255, 255, 255, 0.5);
+ box-shadow: 0 -1px 5px rgba(0, 0, 0, 0.2);
+ white-space: nowrap;
+ overflow: hidden;
+ }
+.leaflet-control-scale-line:not(:first-child) {
+ border-top: 2px solid #777;
+ border-bottom: none;
+ margin-top: -2px;
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
+ }
+.leaflet-control-scale-line:not(:first-child):not(:last-child) {
+ border-bottom: 2px solid #777;
+ }
+
+.leaflet-touch .leaflet-control-attribution,
+.leaflet-touch .leaflet-control-layers,
+.leaflet-touch .leaflet-control-zoom {
+ box-shadow: none;
+ }
+.leaflet-touch .leaflet-control-layers,
+.leaflet-touch .leaflet-control-zoom {
+ border: 4px solid rgba(0,0,0,0.3);
+ }
+
+
+/* popup */
+
+.leaflet-popup {
+ position: absolute;
+ text-align: center;
+ }
+.leaflet-popup-content-wrapper {
+ padding: 1px;
+ text-align: left;
+ -webkit-border-radius: 20px;
+ border-radius: 20px;
+ }
+.leaflet-popup-content {
+ margin: 14px 20px;
+ line-height: 1.4;
+ }
+.leaflet-popup-content p {
+ margin: 18px 0;
+ }
+.leaflet-popup-tip-container {
+ margin: 0 auto;
+ width: 40px;
+ height: 20px;
+ position: relative;
+ overflow: hidden;
+ }
+.leaflet-popup-tip {
+ width: 15px;
+ height: 15px;
+ padding: 1px;
+
+ margin: -8px auto 0;
+
+ -webkit-transform: rotate(45deg);
+ -moz-transform: rotate(45deg);
+ -ms-transform: rotate(45deg);
+ -o-transform: rotate(45deg);
+ transform: rotate(45deg);
+ }
+.leaflet-popup-content-wrapper, .leaflet-popup-tip {
+ background: white;
+
+ box-shadow: 0 3px 14px rgba(0,0,0,0.4);
+ }
+.leaflet-container a.leaflet-popup-close-button {
+ position: absolute;
+ top: 0;
+ right: 0;
+ padding: 4px 5px 0 0;
+ text-align: center;
+ width: 18px;
+ height: 14px;
+ font: 16px/14px Tahoma, Verdana, sans-serif;
+ color: #c3c3c3;
+ text-decoration: none;
+ font-weight: bold;
+ background: transparent;
+ }
+.leaflet-container a.leaflet-popup-close-button:hover {
+ color: #999;
+ }
+.leaflet-popup-scrolled {
+ overflow: auto;
+ border-bottom: 1px solid #ddd;
+ border-top: 1px solid #ddd;
+ }
+
+
+/* div icon */
+
+.leaflet-div-icon {
+ background: #fff;
+ border: 1px solid #666;
+ }
+.leaflet-editing-icon {
+ -webkit-border-radius: 2px;
+ border-radius: 2px;
+ }
Added: sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/leaflet.ie.css
===================================================================
--- sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/leaflet.ie.css (rev 0)
+++ sandbox/nuiton-js-leaflet/src/main/resources/nuiton-js-leaflet/leaflet.ie.css 2013-06-19 11:24:48 UTC (rev 97)
@@ -0,0 +1,58 @@
+/* %%Ignore-License */
+.leaflet-vml-shape {
+ width: 1px;
+ height: 1px;
+ }
+.lvml {
+ behavior: url(#default#VML);
+ display: inline-block;
+ position: absolute;
+ }
+
+.leaflet-control {
+ display: inline;
+ }
+
+.leaflet-popup-tip {
+ width: 21px;
+ _width: 27px;
+ margin: 0 auto;
+ _margin-top: -3px;
+
+ filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
+ -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
+ }
+.leaflet-popup-tip-container {
+ margin-top: -1px;
+ }
+.leaflet-popup-content-wrapper, .leaflet-popup-tip {
+ border: 1px solid #999;
+ }
+.leaflet-popup-content-wrapper {
+ zoom: 1;
+ }
+
+.leaflet-control-zoom,
+.leaflet-control-layers {
+ border: 3px solid #999;
+ }
+.leaflet-control-zoom a {
+ background-color: #eee;
+ }
+.leaflet-control-zoom a:hover {
+ background-color: #fff;
+ }
+.leaflet-control-layers-toggle {
+ }
+.leaflet-control-attribution,
+.leaflet-control-layers,
+.leaflet-control-scale-line {
+ background: white;
+ }
+.leaflet-zoom-box {
+ filter: alpha(opacity=50);
+ }
+.leaflet-control-attribution {
+ border-top: 1px solid #bbb;
+ border-left: 1px solid #bbb;
+ }
1
0
Author: echatellier
Date: 2013-06-19 13:24:18 +0200 (Wed, 19 Jun 2013)
New Revision: 96
Url: http://nuiton.org/projects/nuiton-js/repository/revisions/96
Log:
Initial import.
Added:
sandbox/nuiton-js-leaflet/
1
0
Author: echatellier
Date: 2013-06-19 11:44:36 +0200 (Wed, 19 Jun 2013)
New Revision: 95
Url: http://nuiton.org/projects/nuiton-js/repository/revisions/95
Log:
fixes #2720 : Update wro4j to 1.7.0
Modified:
trunk/pom.xml
Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml 2013-06-18 09:09:04 UTC (rev 94)
+++ trunk/pom.xml 2013-06-19 09:44:36 UTC (rev 95)
@@ -107,7 +107,7 @@
<properties>
- <wroVersion>1.6.3</wroVersion>
+ <wroVersion>1.7.0</wroVersion>
<slf4jVersion>1.7.3</slf4jVersion>
<servletApiVersion>2.5</servletApiVersion>
1
0
18 Jun '13
Author: kmorin
Date: 2013-06-18 11:09:04 +0200 (Tue, 18 Jun 2013)
New Revision: 94
Url: http://nuiton.org/projects/nuiton-js/repository/revisions/94
Log:
add moment.js 2.0.0
Added:
tags/nuiton-js-moment-2.0.0-1/
tags/nuiton-js-moment-2.0.0-1/LICENSE.txt
tags/nuiton-js-moment-2.0.0-1/README.txt
tags/nuiton-js-moment-2.0.0-1/changelog.txt
tags/nuiton-js-moment-2.0.0-1/pom.xml
tags/nuiton-js-moment-2.0.0-1/pom.xml~
tags/nuiton-js-moment-2.0.0-1/src/
tags/nuiton-js-moment-2.0.0-1/src/main/
tags/nuiton-js-moment-2.0.0-1/src/main/resources/
tags/nuiton-js-moment-2.0.0-1/src/main/resources/META-INF/
tags/nuiton-js-moment-2.0.0-1/src/main/resources/META-INF/nuiton-js/
tags/nuiton-js-moment-2.0.0-1/src/main/resources/META-INF/nuiton-js/wro-moment.xml
tags/nuiton-js-moment-2.0.0-1/src/main/resources/nuiton-js-moment/
tags/nuiton-js-moment-2.0.0-1/src/main/resources/nuiton-js-moment/moment.js
tags/nuiton-js-moment-2.0.0-1/src/site/
tags/nuiton-js-moment-2.0.0-1/src/site/rst/
tags/nuiton-js-moment-2.0.0-1/src/site/rst/index.rst
tags/nuiton-js-moment-2.0.0-1/src/site/site_fr.xml
Added: tags/nuiton-js-moment-2.0.0-1/LICENSE.txt
===================================================================
--- tags/nuiton-js-moment-2.0.0-1/LICENSE.txt (rev 0)
+++ tags/nuiton-js-moment-2.0.0-1/LICENSE.txt 2013-06-18 09:09:04 UTC (rev 94)
@@ -0,0 +1,166 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
+
Added: tags/nuiton-js-moment-2.0.0-1/README.txt
===================================================================
--- tags/nuiton-js-moment-2.0.0-1/README.txt (rev 0)
+++ tags/nuiton-js-moment-2.0.0-1/README.txt 2013-06-18 09:09:04 UTC (rev 94)
@@ -0,0 +1 @@
+
Added: tags/nuiton-js-moment-2.0.0-1/changelog.txt
===================================================================
--- tags/nuiton-js-moment-2.0.0-1/changelog.txt (rev 0)
+++ tags/nuiton-js-moment-2.0.0-1/changelog.txt 2013-06-18 09:09:04 UTC (rev 94)
@@ -0,0 +1,2 @@
+Please refer to the release note :
+ http://maven-site.nuiton.org/nuiton-js/changes-report.html
Added: tags/nuiton-js-moment-2.0.0-1/pom.xml
===================================================================
--- tags/nuiton-js-moment-2.0.0-1/pom.xml (rev 0)
+++ tags/nuiton-js-moment-2.0.0-1/pom.xml 2013-06-18 09:09:04 UTC (rev 94)
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <!-- ************************************************************* -->
+ <!-- *** POM Relationships *************************************** -->
+ <!-- ************************************************************* -->
+
+ <parent>
+ <groupId>org.nuiton.js</groupId>
+ <artifactId>nuiton-js-lib</artifactId>
+ <version>1.0</version>
+ </parent>
+
+ <artifactId>nuiton-js-moment</artifactId>
+ <version>2.0.0-1</version>
+
+ <name>Nuiton JS :: Moment</name>
+ <description>Moment.js packaging</description>
+
+ <url>http://momentjs.com/</url>
+
+ <scm>
+ <connection>scm:svn:http://svn.nuiton.org/svn/nuiton-js/tags/nuiton-js-moment-2.0.0-1</connection>
+ <developerConnection>scm:svn:http://svn.nuiton.org/svn/nuiton-js/tags/nuiton-js-moment-2.0.0-1</developerConnection>
+ <url>http://www.nuiton.org/repositories/browse/nuiton-js/tags/nuiton-js-moment-2…</url>
+ </scm>
+
+ <licenses>
+ <license>
+ <name>MIT License</name>
+ <url>http://opensource.org/licenses/mit-license.php</url>
+ </license>
+ </licenses>
+
+</project>
Added: tags/nuiton-js-moment-2.0.0-1/pom.xml~
===================================================================
--- tags/nuiton-js-moment-2.0.0-1/pom.xml~ (rev 0)
+++ tags/nuiton-js-moment-2.0.0-1/pom.xml~ 2013-06-18 09:09:04 UTC (rev 94)
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <!-- ************************************************************* -->
+ <!-- *** POM Relationships *************************************** -->
+ <!-- ************************************************************* -->
+
+ <parent>
+ <groupId>org.nuiton.js</groupId>
+ <artifactId>nuiton-js-lib</artifactId>
+ <version>1.0</version>
+ </parent>
+
+ <artifactId>nuiton-js-moment</artifactId>
+ <version>2.0.0-1</version>
+
+ <name>Nuiton JS :: Moment</name>
+ <description>Moment.js packaging</description>
+
+ <url>http://twitter.github.com/bootstrap/</url>
+
+ <scm>
+ <connection>scm:svn:http://svn.nuiton.org/svn/nuiton-js/tags/nuiton-js-moment-2.0.0-1</connection>
+ <developerConnection>scm:svn:http://svn.nuiton.org/svn/nuiton-js/tags/nuiton-js-moment-2.0.0-1</developerConnection>
+ <url>http://www.nuiton.org/repositories/browse/nuiton-js/tags/nuiton-js-moment-2…</url>
+ </scm>
+
+ <licenses>
+ <license>
+ <name>MIT License</name>
+ <url>http://opensource.org/licenses/mit-license.php</url>
+ </license>
+ </licenses>
+
+</project>
Added: tags/nuiton-js-moment-2.0.0-1/src/main/resources/META-INF/nuiton-js/wro-moment.xml
===================================================================
--- tags/nuiton-js-moment-2.0.0-1/src/main/resources/META-INF/nuiton-js/wro-moment.xml (rev 0)
+++ tags/nuiton-js-moment-2.0.0-1/src/main/resources/META-INF/nuiton-js/wro-moment.xml 2013-06-18 09:09:04 UTC (rev 94)
@@ -0,0 +1,7 @@
+<groups xmlns="http://www.isdc.ro/wro">
+
+ <group name='moment'>
+ <js>classpath:nuiton-js-moment/moment.js</js>
+ </group>
+
+</groups>
Added: tags/nuiton-js-moment-2.0.0-1/src/main/resources/nuiton-js-moment/moment.js
===================================================================
--- tags/nuiton-js-moment-2.0.0-1/src/main/resources/nuiton-js-moment/moment.js (rev 0)
+++ tags/nuiton-js-moment-2.0.0-1/src/main/resources/nuiton-js-moment/moment.js 2013-06-18 09:09:04 UTC (rev 94)
@@ -0,0 +1,1400 @@
+// moment.js
+// version : 2.0.0
+// author : Tim Wood
+// license : MIT
+// momentjs.com
+
+(function (undefined) {
+
+ /************************************
+ Constants
+ ************************************/
+
+ var moment,
+ VERSION = "2.0.0",
+ round = Math.round, i,
+ // internal storage for language config files
+ languages = {},
+
+ // check for nodeJS
+ hasModule = (typeof module !== 'undefined' && module.exports),
+
+ // ASP.NET json date format regex
+ aspNetJsonRegex = /^\/?Date\((\-?\d+)/i,
+
+ // format tokens
+ formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|YYYYY|YYYY|YY|a|A|hh?|HH?|mm?|ss?|SS?S?|X|zz?|ZZ?|.)/g,
+ localFormattingTokens = /(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g,
+
+ // parsing tokens
+ parseMultipleFormatChunker = /([0-9a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)/gi,
+
+ // parsing token regexes
+ parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99
+ parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999
+ parseTokenThreeDigits = /\d{3}/, // 000 - 999
+ parseTokenFourDigits = /\d{1,4}/, // 0 - 9999
+ parseTokenSixDigits = /[+\-]?\d{1,6}/, // -999,999 - 999,999
+ parseTokenWord = /[0-9]*[a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF]+\s*?[\u0600-\u06FF]+/i, // any word (or two) characters or numbers including two word month in arabic.
+ parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/i, // +00:00 -00:00 +0000 -0000 or Z
+ parseTokenT = /T/i, // T (ISO seperator)
+ parseTokenTimestampMs = /[\+\-]?\d+(\.\d{1,3})?/, // 123456789 123456789.123
+
+ // preliminary iso regex
+ // 0000-00-00 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000
+ isoRegex = /^\s*\d{4}-\d\d-\d\d((T| )(\d\d(:\d\d(:\d\d(\.\d\d?\d?)?)?)?)?([\+\-]\d\d:?\d\d)?)?/,
+ isoFormat = 'YYYY-MM-DDTHH:mm:ssZ',
+
+ // iso time formats and regexes
+ isoTimes = [
+ ['HH:mm:ss.S', /(T| )\d\d:\d\d:\d\d\.\d{1,3}/],
+ ['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/],
+ ['HH:mm', /(T| )\d\d:\d\d/],
+ ['HH', /(T| )\d\d/]
+ ],
+
+ // timezone chunker "+10:00" > ["10", "00"] or "-1530" > ["-15", "30"]
+ parseTimezoneChunker = /([\+\-]|\d\d)/gi,
+
+ // getter and setter names
+ proxyGettersAndSetters = 'Month|Date|Hours|Minutes|Seconds|Milliseconds'.split('|'),
+ unitMillisecondFactors = {
+ 'Milliseconds' : 1,
+ 'Seconds' : 1e3,
+ 'Minutes' : 6e4,
+ 'Hours' : 36e5,
+ 'Days' : 864e5,
+ 'Months' : 2592e6,
+ 'Years' : 31536e6
+ },
+
+ // format function strings
+ formatFunctions = {},
+
+ // tokens to ordinalize and pad
+ ordinalizeTokens = 'DDD w W M D d'.split(' '),
+ paddedTokens = 'M D H h m s w W'.split(' '),
+
+ formatTokenFunctions = {
+ M : function () {
+ return this.month() + 1;
+ },
+ MMM : function (format) {
+ return this.lang().monthsShort(this, format);
+ },
+ MMMM : function (format) {
+ return this.lang().months(this, format);
+ },
+ D : function () {
+ return this.date();
+ },
+ DDD : function () {
+ return this.dayOfYear();
+ },
+ d : function () {
+ return this.day();
+ },
+ dd : function (format) {
+ return this.lang().weekdaysMin(this, format);
+ },
+ ddd : function (format) {
+ return this.lang().weekdaysShort(this, format);
+ },
+ dddd : function (format) {
+ return this.lang().weekdays(this, format);
+ },
+ w : function () {
+ return this.week();
+ },
+ W : function () {
+ return this.isoWeek();
+ },
+ YY : function () {
+ return leftZeroFill(this.year() % 100, 2);
+ },
+ YYYY : function () {
+ return leftZeroFill(this.year(), 4);
+ },
+ YYYYY : function () {
+ return leftZeroFill(this.year(), 5);
+ },
+ a : function () {
+ return this.lang().meridiem(this.hours(), this.minutes(), true);
+ },
+ A : function () {
+ return this.lang().meridiem(this.hours(), this.minutes(), false);
+ },
+ H : function () {
+ return this.hours();
+ },
+ h : function () {
+ return this.hours() % 12 || 12;
+ },
+ m : function () {
+ return this.minutes();
+ },
+ s : function () {
+ return this.seconds();
+ },
+ S : function () {
+ return ~~(this.milliseconds() / 100);
+ },
+ SS : function () {
+ return leftZeroFill(~~(this.milliseconds() / 10), 2);
+ },
+ SSS : function () {
+ return leftZeroFill(this.milliseconds(), 3);
+ },
+ Z : function () {
+ var a = -this.zone(),
+ b = "+";
+ if (a < 0) {
+ a = -a;
+ b = "-";
+ }
+ return b + leftZeroFill(~~(a / 60), 2) + ":" + leftZeroFill(~~a % 60, 2);
+ },
+ ZZ : function () {
+ var a = -this.zone(),
+ b = "+";
+ if (a < 0) {
+ a = -a;
+ b = "-";
+ }
+ return b + leftZeroFill(~~(10 * a / 6), 4);
+ },
+ X : function () {
+ return this.unix();
+ }
+ };
+
+ function padToken(func, count) {
+ return function (a) {
+ return leftZeroFill(func.call(this, a), count);
+ };
+ }
+ function ordinalizeToken(func) {
+ return function (a) {
+ return this.lang().ordinal(func.call(this, a));
+ };
+ }
+
+ while (ordinalizeTokens.length) {
+ i = ordinalizeTokens.pop();
+ formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i]);
+ }
+ while (paddedTokens.length) {
+ i = paddedTokens.pop();
+ formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2);
+ }
+ formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3);
+
+
+ /************************************
+ Constructors
+ ************************************/
+
+ function Language() {
+
+ }
+
+ // Moment prototype object
+ function Moment(config) {
+ extend(this, config);
+ }
+
+ // Duration Constructor
+ function Duration(duration) {
+ var data = this._data = {},
+ years = duration.years || duration.year || duration.y || 0,
+ months = duration.months || duration.month || duration.M || 0,
+ weeks = duration.weeks || duration.week || duration.w || 0,
+ days = duration.days || duration.day || duration.d || 0,
+ hours = duration.hours || duration.hour || duration.h || 0,
+ minutes = duration.minutes || duration.minute || duration.m || 0,
+ seconds = duration.seconds || duration.second || duration.s || 0,
+ milliseconds = duration.milliseconds || duration.millisecond || duration.ms || 0;
+
+ // representation for dateAddRemove
+ this._milliseconds = milliseconds +
+ seconds * 1e3 + // 1000
+ minutes * 6e4 + // 1000 * 60
+ hours * 36e5; // 1000 * 60 * 60
+ // Because of dateAddRemove treats 24 hours as different from a
+ // day when working around DST, we need to store them separately
+ this._days = days +
+ weeks * 7;
+ // It is impossible translate months into days without knowing
+ // which months you are are talking about, so we have to store
+ // it separately.
+ this._months = months +
+ years * 12;
+
+ // The following code bubbles up values, see the tests for
+ // examples of what that means.
+ data.milliseconds = milliseconds % 1000;
+ seconds += absRound(milliseconds / 1000);
+
+ data.seconds = seconds % 60;
+ minutes += absRound(seconds / 60);
+
+ data.minutes = minutes % 60;
+ hours += absRound(minutes / 60);
+
+ data.hours = hours % 24;
+ days += absRound(hours / 24);
+
+ days += weeks * 7;
+ data.days = days % 30;
+
+ months += absRound(days / 30);
+
+ data.months = months % 12;
+ years += absRound(months / 12);
+
+ data.years = years;
+ }
+
+
+ /************************************
+ Helpers
+ ************************************/
+
+
+ function extend(a, b) {
+ for (var i in b) {
+ if (b.hasOwnProperty(i)) {
+ a[i] = b[i];
+ }
+ }
+ return a;
+ }
+
+ function absRound(number) {
+ if (number < 0) {
+ return Math.ceil(number);
+ } else {
+ return Math.floor(number);
+ }
+ }
+
+ // left zero fill a number
+ // see http://jsperf.com/left-zero-filling for performance comparison
+ function leftZeroFill(number, targetLength) {
+ var output = number + '';
+ while (output.length < targetLength) {
+ output = '0' + output;
+ }
+ return output;
+ }
+
+ // helper function for _.addTime and _.subtractTime
+ function addOrSubtractDurationFromMoment(mom, duration, isAdding) {
+ var ms = duration._milliseconds,
+ d = duration._days,
+ M = duration._months,
+ currentDate;
+
+ if (ms) {
+ mom._d.setTime(+mom + ms * isAdding);
+ }
+ if (d) {
+ mom.date(mom.date() + d * isAdding);
+ }
+ if (M) {
+ currentDate = mom.date();
+ mom.date(1)
+ .month(mom.month() + M * isAdding)
+ .date(Math.min(currentDate, mom.daysInMonth()));
+ }
+ }
+
+ // check if is an array
+ function isArray(input) {
+ return Object.prototype.toString.call(input) === '[object Array]';
+ }
+
+ // compare two arrays, return the number of differences
+ function compareArrays(array1, array2) {
+ var len = Math.min(array1.length, array2.length),
+ lengthDiff = Math.abs(array1.length - array2.length),
+ diffs = 0,
+ i;
+ for (i = 0; i < len; i++) {
+ if (~~array1[i] !== ~~array2[i]) {
+ diffs++;
+ }
+ }
+ return diffs + lengthDiff;
+ }
+
+
+ /************************************
+ Languages
+ ************************************/
+
+
+ Language.prototype = {
+ set : function (config) {
+ var prop, i;
+ for (i in config) {
+ prop = config[i];
+ if (typeof prop === 'function') {
+ this[i] = prop;
+ } else {
+ this['_' + i] = prop;
+ }
+ }
+ },
+
+ _months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"),
+ months : function (m) {
+ return this._months[m.month()];
+ },
+
+ _monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),
+ monthsShort : function (m) {
+ return this._monthsShort[m.month()];
+ },
+
+ monthsParse : function (monthName) {
+ var i, mom, regex, output;
+
+ if (!this._monthsParse) {
+ this._monthsParse = [];
+ }
+
+ for (i = 0; i < 12; i++) {
+ // make the regex if we don't have it already
+ if (!this._monthsParse[i]) {
+ mom = moment([2000, i]);
+ regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
+ this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
+ }
+ // test the regex
+ if (this._monthsParse[i].test(monthName)) {
+ return i;
+ }
+ }
+ },
+
+ _weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),
+ weekdays : function (m) {
+ return this._weekdays[m.day()];
+ },
+
+ _weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),
+ weekdaysShort : function (m) {
+ return this._weekdaysShort[m.day()];
+ },
+
+ _weekdaysMin : "Su_Mo_Tu_We_Th_Fr_Sa".split("_"),
+ weekdaysMin : function (m) {
+ return this._weekdaysMin[m.day()];
+ },
+
+ _longDateFormat : {
+ LT : "h:mm A",
+ L : "MM/DD/YYYY",
+ LL : "MMMM D YYYY",
+ LLL : "MMMM D YYYY LT",
+ LLLL : "dddd, MMMM D YYYY LT"
+ },
+ longDateFormat : function (key) {
+ var output = this._longDateFormat[key];
+ if (!output && this._longDateFormat[key.toUpperCase()]) {
+ output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) {
+ return val.slice(1);
+ });
+ this._longDateFormat[key] = output;
+ }
+ return output;
+ },
+
+ meridiem : function (hours, minutes, isLower) {
+ if (hours > 11) {
+ return isLower ? 'pm' : 'PM';
+ } else {
+ return isLower ? 'am' : 'AM';
+ }
+ },
+
+ _calendar : {
+ sameDay : '[Today at] LT',
+ nextDay : '[Tomorrow at] LT',
+ nextWeek : 'dddd [at] LT',
+ lastDay : '[Yesterday at] LT',
+ lastWeek : '[last] dddd [at] LT',
+ sameElse : 'L'
+ },
+ calendar : function (key, mom) {
+ var output = this._calendar[key];
+ return typeof output === 'function' ? output.apply(mom) : output;
+ },
+
+ _relativeTime : {
+ future : "in %s",
+ past : "%s ago",
+ s : "a few seconds",
+ m : "a minute",
+ mm : "%d minutes",
+ h : "an hour",
+ hh : "%d hours",
+ d : "a day",
+ dd : "%d days",
+ M : "a month",
+ MM : "%d months",
+ y : "a year",
+ yy : "%d years"
+ },
+ relativeTime : function (number, withoutSuffix, string, isFuture) {
+ var output = this._relativeTime[string];
+ return (typeof output === 'function') ?
+ output(number, withoutSuffix, string, isFuture) :
+ output.replace(/%d/i, number);
+ },
+ pastFuture : function (diff, output) {
+ var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
+ return typeof format === 'function' ? format(output) : format.replace(/%s/i, output);
+ },
+
+ ordinal : function (number) {
+ return this._ordinal.replace("%d", number);
+ },
+ _ordinal : "%d",
+
+ preparse : function (string) {
+ return string;
+ },
+
+ postformat : function (string) {
+ return string;
+ },
+
+ week : function (mom) {
+ return weekOfYear(mom, this._week.dow, this._week.doy);
+ },
+ _week : {
+ dow : 0, // Sunday is the first day of the week.
+ doy : 6 // The week that contains Jan 1st is the first week of the year.
+ }
+ };
+
+ // Loads a language definition into the `languages` cache. The function
+ // takes a key and optionally values. If not in the browser and no values
+ // are provided, it will load the language file module. As a convenience,
+ // this function also returns the language values.
+ function loadLang(key, values) {
+ values.abbr = key;
+ if (!languages[key]) {
+ languages[key] = new Language();
+ }
+ languages[key].set(values);
+ return languages[key];
+ }
+
+ // Determines which language definition to use and returns it.
+ //
+ // With no parameters, it will return the global language. If you
+ // pass in a language key, such as 'en', it will return the
+ // definition for 'en', so long as 'en' has already been loaded using
+ // moment.lang.
+ function getLangDefinition(key) {
+ if (!key) {
+ return moment.fn._lang;
+ }
+ if (!languages[key] && hasModule) {
+ require('./lang/' + key);
+ }
+ return languages[key];
+ }
+
+
+ /************************************
+ Formatting
+ ************************************/
+
+
+ function removeFormattingTokens(input) {
+ if (input.match(/\[.*\]/)) {
+ return input.replace(/^\[|\]$/g, "");
+ }
+ return input.replace(/\\/g, "");
+ }
+
+ function makeFormatFunction(format) {
+ var array = format.match(formattingTokens), i, length;
+
+ for (i = 0, length = array.length; i < length; i++) {
+ if (formatTokenFunctions[array[i]]) {
+ array[i] = formatTokenFunctions[array[i]];
+ } else {
+ array[i] = removeFormattingTokens(array[i]);
+ }
+ }
+
+ return function (mom) {
+ var output = "";
+ for (i = 0; i < length; i++) {
+ output += typeof array[i].call === 'function' ? array[i].call(mom, format) : array[i];
+ }
+ return output;
+ };
+ }
+
+ // format date using native date object
+ function formatMoment(m, format) {
+ var i = 5;
+
+ function replaceLongDateFormatTokens(input) {
+ return m.lang().longDateFormat(input) || input;
+ }
+
+ while (i-- && localFormattingTokens.test(format)) {
+ format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);
+ }
+
+ if (!formatFunctions[format]) {
+ formatFunctions[format] = makeFormatFunction(format);
+ }
+
+ return formatFunctions[format](m);
+ }
+
+
+ /************************************
+ Parsing
+ ************************************/
+
+
+ // get the regex to find the next token
+ function getParseRegexForToken(token) {
+ switch (token) {
+ case 'DDDD':
+ return parseTokenThreeDigits;
+ case 'YYYY':
+ return parseTokenFourDigits;
+ case 'YYYYY':
+ return parseTokenSixDigits;
+ case 'S':
+ case 'SS':
+ case 'SSS':
+ case 'DDD':
+ return parseTokenOneToThreeDigits;
+ case 'MMM':
+ case 'MMMM':
+ case 'dd':
+ case 'ddd':
+ case 'dddd':
+ case 'a':
+ case 'A':
+ return parseTokenWord;
+ case 'X':
+ return parseTokenTimestampMs;
+ case 'Z':
+ case 'ZZ':
+ return parseTokenTimezone;
+ case 'T':
+ return parseTokenT;
+ case 'MM':
+ case 'DD':
+ case 'YY':
+ case 'HH':
+ case 'hh':
+ case 'mm':
+ case 'ss':
+ case 'M':
+ case 'D':
+ case 'd':
+ case 'H':
+ case 'h':
+ case 'm':
+ case 's':
+ return parseTokenOneOrTwoDigits;
+ default :
+ return new RegExp(token.replace('\\', ''));
+ }
+ }
+
+ // function to convert string input to date
+ function addTimeToArrayFromToken(token, input, config) {
+ var a, b,
+ datePartArray = config._a;
+
+ switch (token) {
+ // MONTH
+ case 'M' : // fall through to MM
+ case 'MM' :
+ datePartArray[1] = (input == null) ? 0 : ~~input - 1;
+ break;
+ case 'MMM' : // fall through to MMMM
+ case 'MMMM' :
+ a = getLangDefinition(config._l).monthsParse(input);
+ // if we didn't find a month name, mark the date as invalid.
+ if (a != null) {
+ datePartArray[1] = a;
+ } else {
+ config._isValid = false;
+ }
+ break;
+ // DAY OF MONTH
+ case 'D' : // fall through to DDDD
+ case 'DD' : // fall through to DDDD
+ case 'DDD' : // fall through to DDDD
+ case 'DDDD' :
+ if (input != null) {
+ datePartArray[2] = ~~input;
+ }
+ break;
+ // YEAR
+ case 'YY' :
+ datePartArray[0] = ~~input + (~~input > 68 ? 1900 : 2000);
+ break;
+ case 'YYYY' :
+ case 'YYYYY' :
+ datePartArray[0] = ~~input;
+ break;
+ // AM / PM
+ case 'a' : // fall through to A
+ case 'A' :
+ config._isPm = ((input + '').toLowerCase() === 'pm');
+ break;
+ // 24 HOUR
+ case 'H' : // fall through to hh
+ case 'HH' : // fall through to hh
+ case 'h' : // fall through to hh
+ case 'hh' :
+ datePartArray[3] = ~~input;
+ break;
+ // MINUTE
+ case 'm' : // fall through to mm
+ case 'mm' :
+ datePartArray[4] = ~~input;
+ break;
+ // SECOND
+ case 's' : // fall through to ss
+ case 'ss' :
+ datePartArray[5] = ~~input;
+ break;
+ // MILLISECOND
+ case 'S' :
+ case 'SS' :
+ case 'SSS' :
+ datePartArray[6] = ~~ (('0.' + input) * 1000);
+ break;
+ // UNIX TIMESTAMP WITH MS
+ case 'X':
+ config._d = new Date(parseFloat(input) * 1000);
+ break;
+ // TIMEZONE
+ case 'Z' : // fall through to ZZ
+ case 'ZZ' :
+ config._useUTC = true;
+ a = (input + '').match(parseTimezoneChunker);
+ if (a && a[1]) {
+ config._tzh = ~~a[1];
+ }
+ if (a && a[2]) {
+ config._tzm = ~~a[2];
+ }
+ // reverse offsets
+ if (a && a[0] === '+') {
+ config._tzh = -config._tzh;
+ config._tzm = -config._tzm;
+ }
+ break;
+ }
+
+ // if the input is null, the date is not valid
+ if (input == null) {
+ config._isValid = false;
+ }
+ }
+
+ // convert an array to a date.
+ // the array should mirror the parameters below
+ // note: all values past the year are optional and will default to the lowest possible value.
+ // [year, month, day , hour, minute, second, millisecond]
+ function dateFromArray(config) {
+ var i, date, input = [];
+
+ if (config._d) {
+ return;
+ }
+
+ for (i = 0; i < 7; i++) {
+ config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i];
+ }
+
+ // add the offsets to the time to be parsed so that we can have a clean array for checking isValid
+ input[3] += config._tzh || 0;
+ input[4] += config._tzm || 0;
+
+ date = new Date(0);
+
+ if (config._useUTC) {
+ date.setUTCFullYear(input[0], input[1], input[2]);
+ date.setUTCHours(input[3], input[4], input[5], input[6]);
+ } else {
+ date.setFullYear(input[0], input[1], input[2]);
+ date.setHours(input[3], input[4], input[5], input[6]);
+ }
+
+ config._d = date;
+ }
+
+ // date from string and format string
+ function makeDateFromStringAndFormat(config) {
+ // This array is used to make a Date, either with `new Date` or `Date.UTC`
+ var tokens = config._f.match(formattingTokens),
+ string = config._i,
+ i, parsedInput;
+
+ config._a = [];
+
+ for (i = 0; i < tokens.length; i++) {
+ parsedInput = (getParseRegexForToken(tokens[i]).exec(string) || [])[0];
+ if (parsedInput) {
+ string = string.slice(string.indexOf(parsedInput) + parsedInput.length);
+ }
+ // don't parse if its not a known token
+ if (formatTokenFunctions[tokens[i]]) {
+ addTimeToArrayFromToken(tokens[i], parsedInput, config);
+ }
+ }
+ // handle am pm
+ if (config._isPm && config._a[3] < 12) {
+ config._a[3] += 12;
+ }
+ // if is 12 am, change hours to 0
+ if (config._isPm === false && config._a[3] === 12) {
+ config._a[3] = 0;
+ }
+ // return
+ dateFromArray(config);
+ }
+
+ // date from string and array of format strings
+ function makeDateFromStringAndArray(config) {
+ var tempConfig,
+ tempMoment,
+ bestMoment,
+
+ scoreToBeat = 99,
+ i,
+ currentDate,
+ currentScore;
+
+ while (config._f.length) {
+ tempConfig = extend({}, config);
+ tempConfig._f = config._f.pop();
+ makeDateFromStringAndFormat(tempConfig);
+ tempMoment = new Moment(tempConfig);
+
+ if (tempMoment.isValid()) {
+ bestMoment = tempMoment;
+ break;
+ }
+
+ currentScore = compareArrays(tempConfig._a, tempMoment.toArray());
+
+ if (currentScore < scoreToBeat) {
+ scoreToBeat = currentScore;
+ bestMoment = tempMoment;
+ }
+ }
+
+ extend(config, bestMoment);
+ }
+
+ // date from iso format
+ function makeDateFromString(config) {
+ var i,
+ string = config._i;
+ if (isoRegex.exec(string)) {
+ config._f = 'YYYY-MM-DDT';
+ for (i = 0; i < 4; i++) {
+ if (isoTimes[i][1].exec(string)) {
+ config._f += isoTimes[i][0];
+ break;
+ }
+ }
+ if (parseTokenTimezone.exec(string)) {
+ config._f += " Z";
+ }
+ makeDateFromStringAndFormat(config);
+ } else {
+ config._d = new Date(string);
+ }
+ }
+
+ function makeDateFromInput(config) {
+ var input = config._i,
+ matched = aspNetJsonRegex.exec(input);
+
+ if (input === undefined) {
+ config._d = new Date();
+ } else if (matched) {
+ config._d = new Date(+matched[1]);
+ } else if (typeof input === 'string') {
+ makeDateFromString(config);
+ } else if (isArray(input)) {
+ config._a = input.slice(0);
+ dateFromArray(config);
+ } else {
+ config._d = input instanceof Date ? new Date(+input) : new Date(input);
+ }
+ }
+
+
+ /************************************
+ Relative Time
+ ************************************/
+
+
+ // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
+ function substituteTimeAgo(string, number, withoutSuffix, isFuture, lang) {
+ return lang.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
+ }
+
+ function relativeTime(milliseconds, withoutSuffix, lang) {
+ var seconds = round(Math.abs(milliseconds) / 1000),
+ minutes = round(seconds / 60),
+ hours = round(minutes / 60),
+ days = round(hours / 24),
+ years = round(days / 365),
+ args = seconds < 45 && ['s', seconds] ||
+ minutes === 1 && ['m'] ||
+ minutes < 45 && ['mm', minutes] ||
+ hours === 1 && ['h'] ||
+ hours < 22 && ['hh', hours] ||
+ days === 1 && ['d'] ||
+ days <= 25 && ['dd', days] ||
+ days <= 45 && ['M'] ||
+ days < 345 && ['MM', round(days / 30)] ||
+ years === 1 && ['y'] || ['yy', years];
+ args[2] = withoutSuffix;
+ args[3] = milliseconds > 0;
+ args[4] = lang;
+ return substituteTimeAgo.apply({}, args);
+ }
+
+
+ /************************************
+ Week of Year
+ ************************************/
+
+
+ // firstDayOfWeek 0 = sun, 6 = sat
+ // the day of the week that starts the week
+ // (usually sunday or monday)
+ // firstDayOfWeekOfYear 0 = sun, 6 = sat
+ // the first week is the week that contains the first
+ // of this day of the week
+ // (eg. ISO weeks use thursday (4))
+ function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) {
+ var end = firstDayOfWeekOfYear - firstDayOfWeek,
+ daysToDayOfWeek = firstDayOfWeekOfYear - mom.day();
+
+
+ if (daysToDayOfWeek > end) {
+ daysToDayOfWeek -= 7;
+ }
+
+ if (daysToDayOfWeek < end - 7) {
+ daysToDayOfWeek += 7;
+ }
+
+ return Math.ceil(moment(mom).add('d', daysToDayOfWeek).dayOfYear() / 7);
+ }
+
+
+ /************************************
+ Top Level Functions
+ ************************************/
+
+ function makeMoment(config) {
+ var input = config._i,
+ format = config._f;
+
+ if (input === null || input === '') {
+ return null;
+ }
+
+ if (typeof input === 'string') {
+ config._i = input = getLangDefinition().preparse(input);
+ }
+
+ if (moment.isMoment(input)) {
+ config = extend({}, input);
+ config._d = new Date(+input._d);
+ } else if (format) {
+ if (isArray(format)) {
+ makeDateFromStringAndArray(config);
+ } else {
+ makeDateFromStringAndFormat(config);
+ }
+ } else {
+ makeDateFromInput(config);
+ }
+
+ return new Moment(config);
+ }
+
+ moment = function (input, format, lang) {
+ return makeMoment({
+ _i : input,
+ _f : format,
+ _l : lang,
+ _isUTC : false
+ });
+ };
+
+ // creating with utc
+ moment.utc = function (input, format, lang) {
+ return makeMoment({
+ _useUTC : true,
+ _isUTC : true,
+ _l : lang,
+ _i : input,
+ _f : format
+ });
+ };
+
+ // creating with unix timestamp (in seconds)
+ moment.unix = function (input) {
+ return moment(input * 1000);
+ };
+
+ // duration
+ moment.duration = function (input, key) {
+ var isDuration = moment.isDuration(input),
+ isNumber = (typeof input === 'number'),
+ duration = (isDuration ? input._data : (isNumber ? {} : input)),
+ ret;
+
+ if (isNumber) {
+ if (key) {
+ duration[key] = input;
+ } else {
+ duration.milliseconds = input;
+ }
+ }
+
+ ret = new Duration(duration);
+
+ if (isDuration && input.hasOwnProperty('_lang')) {
+ ret._lang = input._lang;
+ }
+
+ return ret;
+ };
+
+ // version number
+ moment.version = VERSION;
+
+ // default format
+ moment.defaultFormat = isoFormat;
+
+ // This function will load languages and then set the global language. If
+ // no arguments are passed in, it will simply return the current global
+ // language key.
+ moment.lang = function (key, values) {
+ var i;
+
+ if (!key) {
+ return moment.fn._lang._abbr;
+ }
+ if (values) {
+ loadLang(key, values);
+ } else if (!languages[key]) {
+ getLangDefinition(key);
+ }
+ moment.duration.fn._lang = moment.fn._lang = getLangDefinition(key);
+ };
+
+ // returns language data
+ moment.langData = function (key) {
+ if (key && key._lang && key._lang._abbr) {
+ key = key._lang._abbr;
+ }
+ return getLangDefinition(key);
+ };
+
+ // compare moment object
+ moment.isMoment = function (obj) {
+ return obj instanceof Moment;
+ };
+
+ // for typechecking Duration objects
+ moment.isDuration = function (obj) {
+ return obj instanceof Duration;
+ };
+
+
+ /************************************
+ Moment Prototype
+ ************************************/
+
+
+ moment.fn = Moment.prototype = {
+
+ clone : function () {
+ return moment(this);
+ },
+
+ valueOf : function () {
+ return +this._d;
+ },
+
+ unix : function () {
+ return Math.floor(+this._d / 1000);
+ },
+
+ toString : function () {
+ return this.format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ");
+ },
+
+ toDate : function () {
+ return this._d;
+ },
+
+ toJSON : function () {
+ return moment.utc(this).format('YYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
+ },
+
+ toArray : function () {
+ var m = this;
+ return [
+ m.year(),
+ m.month(),
+ m.date(),
+ m.hours(),
+ m.minutes(),
+ m.seconds(),
+ m.milliseconds()
+ ];
+ },
+
+ isValid : function () {
+ if (this._isValid == null) {
+ if (this._a) {
+ this._isValid = !compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray());
+ } else {
+ this._isValid = !isNaN(this._d.getTime());
+ }
+ }
+ return !!this._isValid;
+ },
+
+ utc : function () {
+ this._isUTC = true;
+ return this;
+ },
+
+ local : function () {
+ this._isUTC = false;
+ return this;
+ },
+
+ format : function (inputString) {
+ var output = formatMoment(this, inputString || moment.defaultFormat);
+ return this.lang().postformat(output);
+ },
+
+ add : function (input, val) {
+ var dur;
+ // switch args to support add('s', 1) and add(1, 's')
+ if (typeof input === 'string') {
+ dur = moment.duration(+val, input);
+ } else {
+ dur = moment.duration(input, val);
+ }
+ addOrSubtractDurationFromMoment(this, dur, 1);
+ return this;
+ },
+
+ subtract : function (input, val) {
+ var dur;
+ // switch args to support subtract('s', 1) and subtract(1, 's')
+ if (typeof input === 'string') {
+ dur = moment.duration(+val, input);
+ } else {
+ dur = moment.duration(input, val);
+ }
+ addOrSubtractDurationFromMoment(this, dur, -1);
+ return this;
+ },
+
+ diff : function (input, units, asFloat) {
+ var that = this._isUTC ? moment(input).utc() : moment(input).local(),
+ zoneDiff = (this.zone() - that.zone()) * 6e4,
+ diff, output;
+
+ if (units) {
+ // standardize on singular form
+ units = units.replace(/s$/, '');
+ }
+
+ if (units === 'year' || units === 'month') {
+ diff = (this.daysInMonth() + that.daysInMonth()) * 432e5; // 24 * 60 * 60 * 1000 / 2
+ output = ((this.year() - that.year()) * 12) + (this.month() - that.month());
+ output += ((this - moment(this).startOf('month')) - (that - moment(that).startOf('month'))) / diff;
+ if (units === 'year') {
+ output = output / 12;
+ }
+ } else {
+ diff = (this - that) - zoneDiff;
+ output = units === 'second' ? diff / 1e3 : // 1000
+ units === 'minute' ? diff / 6e4 : // 1000 * 60
+ units === 'hour' ? diff / 36e5 : // 1000 * 60 * 60
+ units === 'day' ? diff / 864e5 : // 1000 * 60 * 60 * 24
+ units === 'week' ? diff / 6048e5 : // 1000 * 60 * 60 * 24 * 7
+ diff;
+ }
+ return asFloat ? output : absRound(output);
+ },
+
+ from : function (time, withoutSuffix) {
+ return moment.duration(this.diff(time)).lang(this.lang()._abbr).humanize(!withoutSuffix);
+ },
+
+ fromNow : function (withoutSuffix) {
+ return this.from(moment(), withoutSuffix);
+ },
+
+ calendar : function () {
+ var diff = this.diff(moment().startOf('day'), 'days', true),
+ format = diff < -6 ? 'sameElse' :
+ diff < -1 ? 'lastWeek' :
+ diff < 0 ? 'lastDay' :
+ diff < 1 ? 'sameDay' :
+ diff < 2 ? 'nextDay' :
+ diff < 7 ? 'nextWeek' : 'sameElse';
+ return this.format(this.lang().calendar(format, this));
+ },
+
+ isLeapYear : function () {
+ var year = this.year();
+ return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
+ },
+
+ isDST : function () {
+ return (this.zone() < moment([this.year()]).zone() ||
+ this.zone() < moment([this.year(), 5]).zone());
+ },
+
+ day : function (input) {
+ var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
+ return input == null ? day :
+ this.add({ d : input - day });
+ },
+
+ startOf: function (units) {
+ units = units.replace(/s$/, '');
+ // the following switch intentionally omits break keywords
+ // to utilize falling through the cases.
+ switch (units) {
+ case 'year':
+ this.month(0);
+ /* falls through */
+ case 'month':
+ this.date(1);
+ /* falls through */
+ case 'week':
+ case 'day':
+ this.hours(0);
+ /* falls through */
+ case 'hour':
+ this.minutes(0);
+ /* falls through */
+ case 'minute':
+ this.seconds(0);
+ /* falls through */
+ case 'second':
+ this.milliseconds(0);
+ /* falls through */
+ }
+
+ // weeks are a special case
+ if (units === 'week') {
+ this.day(0);
+ }
+
+ return this;
+ },
+
+ endOf: function (units) {
+ return this.startOf(units).add(units.replace(/s?$/, 's'), 1).subtract('ms', 1);
+ },
+
+ isAfter: function (input, units) {
+ units = typeof units !== 'undefined' ? units : 'millisecond';
+ return +this.clone().startOf(units) > +moment(input).startOf(units);
+ },
+
+ isBefore: function (input, units) {
+ units = typeof units !== 'undefined' ? units : 'millisecond';
+ return +this.clone().startOf(units) < +moment(input).startOf(units);
+ },
+
+ isSame: function (input, units) {
+ units = typeof units !== 'undefined' ? units : 'millisecond';
+ return +this.clone().startOf(units) === +moment(input).startOf(units);
+ },
+
+ zone : function () {
+ return this._isUTC ? 0 : this._d.getTimezoneOffset();
+ },
+
+ daysInMonth : function () {
+ return moment.utc([this.year(), this.month() + 1, 0]).date();
+ },
+
+ dayOfYear : function (input) {
+ var dayOfYear = round((moment(this).startOf('day') - moment(this).startOf('year')) / 864e5) + 1;
+ return input == null ? dayOfYear : this.add("d", (input - dayOfYear));
+ },
+
+ isoWeek : function (input) {
+ var week = weekOfYear(this, 1, 4);
+ return input == null ? week : this.add("d", (input - week) * 7);
+ },
+
+ week : function (input) {
+ var week = this.lang().week(this);
+ return input == null ? week : this.add("d", (input - week) * 7);
+ },
+
+ // If passed a language key, it will set the language for this
+ // instance. Otherwise, it will return the language configuration
+ // variables for this instance.
+ lang : function (key) {
+ if (key === undefined) {
+ return this._lang;
+ } else {
+ this._lang = getLangDefinition(key);
+ return this;
+ }
+ }
+ };
+
+ // helper for adding shortcuts
+ function makeGetterAndSetter(name, key) {
+ moment.fn[name] = moment.fn[name + 's'] = function (input) {
+ var utc = this._isUTC ? 'UTC' : '';
+ if (input != null) {
+ this._d['set' + utc + key](input);
+ return this;
+ } else {
+ return this._d['get' + utc + key]();
+ }
+ };
+ }
+
+ // loop through and add shortcuts (Month, Date, Hours, Minutes, Seconds, Milliseconds)
+ for (i = 0; i < proxyGettersAndSetters.length; i ++) {
+ makeGetterAndSetter(proxyGettersAndSetters[i].toLowerCase().replace(/s$/, ''), proxyGettersAndSetters[i]);
+ }
+
+ // add shortcut for year (uses different syntax than the getter/setter 'year' == 'FullYear')
+ makeGetterAndSetter('year', 'FullYear');
+
+ // add plural methods
+ moment.fn.days = moment.fn.day;
+ moment.fn.weeks = moment.fn.week;
+ moment.fn.isoWeeks = moment.fn.isoWeek;
+
+ /************************************
+ Duration Prototype
+ ************************************/
+
+
+ moment.duration.fn = Duration.prototype = {
+ weeks : function () {
+ return absRound(this.days() / 7);
+ },
+
+ valueOf : function () {
+ return this._milliseconds +
+ this._days * 864e5 +
+ this._months * 2592e6;
+ },
+
+ humanize : function (withSuffix) {
+ var difference = +this,
+ output = relativeTime(difference, !withSuffix, this.lang());
+
+ if (withSuffix) {
+ output = this.lang().pastFuture(difference, output);
+ }
+
+ return this.lang().postformat(output);
+ },
+
+ lang : moment.fn.lang
+ };
+
+ function makeDurationGetter(name) {
+ moment.duration.fn[name] = function () {
+ return this._data[name];
+ };
+ }
+
+ function makeDurationAsGetter(name, factor) {
+ moment.duration.fn['as' + name] = function () {
+ return +this / factor;
+ };
+ }
+
+ for (i in unitMillisecondFactors) {
+ if (unitMillisecondFactors.hasOwnProperty(i)) {
+ makeDurationAsGetter(i, unitMillisecondFactors[i]);
+ makeDurationGetter(i.toLowerCase());
+ }
+ }
+
+ makeDurationAsGetter('Weeks', 6048e5);
+
+
+ /************************************
+ Default Lang
+ ************************************/
+
+
+ // Set default language, other languages will inherit from English.
+ moment.lang('en', {
+ ordinal : function (number) {
+ var b = number % 10,
+ output = (~~ (number % 100 / 10) === 1) ? 'th' :
+ (b === 1) ? 'st' :
+ (b === 2) ? 'nd' :
+ (b === 3) ? 'rd' : 'th';
+ return number + output;
+ }
+ });
+
+
+ /************************************
+ Exposing Moment
+ ************************************/
+
+
+ // CommonJS module is defined
+ if (hasModule) {
+ module.exports = moment;
+ }
+ /*global ender:false */
+ if (typeof ender === 'undefined') {
+ // here, `this` means `window` in the browser, or `global` on the server
+ // add `moment` as a global object via a string identifier,
+ // for Closure Compiler "advanced" mode
+ this['moment'] = moment;
+ }
+ /*global define:false */
+ if (typeof define === "function" && define.amd) {
+ define("moment", [], function () {
+ return moment;
+ });
+ }
+}).call(this);
Added: tags/nuiton-js-moment-2.0.0-1/src/site/rst/index.rst
===================================================================
--- tags/nuiton-js-moment-2.0.0-1/src/site/rst/index.rst (rev 0)
+++ tags/nuiton-js-moment-2.0.0-1/src/site/rst/index.rst 2013-06-18 09:09:04 UTC (rev 94)
@@ -0,0 +1,14 @@
+
+Utilisation
+===========
+
+::
+
+ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+
+ <script language="javascript" type="text/javascript" src="<c:url value="/nuiton-js/moment.js"/>"></script>
+
+JS disponible
+-------------
+
+- moment.js
Added: tags/nuiton-js-moment-2.0.0-1/src/site/site_fr.xml
===================================================================
--- tags/nuiton-js-moment-2.0.0-1/src/site/site_fr.xml (rev 0)
+++ tags/nuiton-js-moment-2.0.0-1/src/site/site_fr.xml 2013-06-18 09:09:04 UTC (rev 94)
@@ -0,0 +1,49 @@
+<project name="${project.name}">
+
+ <bannerLeft>
+ <src alt="NuitonJS">logo.jpg</src>
+ <name>${project.name}</name>
+ <href>index.html</href>
+ </bannerLeft>
+
+ <body>
+
+ <head>
+ <script type="text/javascript"
+ src="http://maven-site.nuiton.org/public/js/mavenpom-site.js">
+ </script>
+
+ <link rel="stylesheet" type="text/css"
+ href="http://maven-site.nuiton.org/public/css/mavenpom-site.css"/>
+ </head>
+
+ <breadcrumbs>
+ <item name="${project.name}" href="${project.url}"/>
+ </breadcrumbs>
+
+ <menu ref="parent"/>
+
+ <menu name="Utilisateurs">
+ <item name="Accueil" href="index.html"/>
+ </menu>
+
+ <menu ref="reports"/>
+
+ <footer>
+
+ <div id='projectMetas'
+ projectversion='${project.version}'
+ platform='${project.platform}'
+ projectid='${project.projectId}'
+ scm='${project.scm.connection}'
+ scmwebeditorenabled='${project.scmwebeditorEnabled}'
+ scmwebeditorurl='${project.scmwebeditorUrl}'
+ siteSourcesType='${project.siteSourcesType}'
+ piwikEnabled='${project.piwikEnabled}'
+ piwikId='${project.piwikId}'>
+ </div>
+ </footer>
+
+ </body>
+
+</project>
1
0
Author: kmorin
Date: 2013-06-18 10:10:49 +0200 (Tue, 18 Jun 2013)
New Revision: 93
Url: http://nuiton.org/projects/nuiton-js/repository/revisions/93
Log:
add jquery.i18n.properties 1.0.9
Added:
tags/nuiton-js-jquery-i18n-properties-1.0.9-1/
tags/nuiton-js-jquery-i18n-properties-1.0.9-1/LICENSE.txt
tags/nuiton-js-jquery-i18n-properties-1.0.9-1/README.txt
tags/nuiton-js-jquery-i18n-properties-1.0.9-1/changelog.txt
tags/nuiton-js-jquery-i18n-properties-1.0.9-1/pom.xml
tags/nuiton-js-jquery-i18n-properties-1.0.9-1/src/
tags/nuiton-js-jquery-i18n-properties-1.0.9-1/src/main/
tags/nuiton-js-jquery-i18n-properties-1.0.9-1/src/main/resources/
tags/nuiton-js-jquery-i18n-properties-1.0.9-1/src/main/resources/META-INF/
tags/nuiton-js-jquery-i18n-properties-1.0.9-1/src/main/resources/META-INF/nuiton-js/
tags/nuiton-js-jquery-i18n-properties-1.0.9-1/src/main/resources/META-INF/nuiton-js/wro-jquery-i18n-properties.xml
tags/nuiton-js-jquery-i18n-properties-1.0.9-1/src/main/resources/nuiton-js-jquery-i18n-properties/
tags/nuiton-js-jquery-i18n-properties-1.0.9-1/src/main/resources/nuiton-js-jquery-i18n-properties/jquery.i18n.properties-1.0.9.js
tags/nuiton-js-jquery-i18n-properties-1.0.9-1/src/site/
tags/nuiton-js-jquery-i18n-properties-1.0.9-1/src/site/rst/
tags/nuiton-js-jquery-i18n-properties-1.0.9-1/src/site/rst/index.rst
tags/nuiton-js-jquery-i18n-properties-1.0.9-1/src/site/site_fr.xml
Added: tags/nuiton-js-jquery-i18n-properties-1.0.9-1/LICENSE.txt
===================================================================
--- tags/nuiton-js-jquery-i18n-properties-1.0.9-1/LICENSE.txt (rev 0)
+++ tags/nuiton-js-jquery-i18n-properties-1.0.9-1/LICENSE.txt 2013-06-18 08:10:49 UTC (rev 93)
@@ -0,0 +1,166 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
+
Added: tags/nuiton-js-jquery-i18n-properties-1.0.9-1/README.txt
===================================================================
--- tags/nuiton-js-jquery-i18n-properties-1.0.9-1/README.txt (rev 0)
+++ tags/nuiton-js-jquery-i18n-properties-1.0.9-1/README.txt 2013-06-18 08:10:49 UTC (rev 93)
@@ -0,0 +1 @@
+
Added: tags/nuiton-js-jquery-i18n-properties-1.0.9-1/changelog.txt
===================================================================
--- tags/nuiton-js-jquery-i18n-properties-1.0.9-1/changelog.txt (rev 0)
+++ tags/nuiton-js-jquery-i18n-properties-1.0.9-1/changelog.txt 2013-06-18 08:10:49 UTC (rev 93)
@@ -0,0 +1,2 @@
+Please refer to the release note :
+ http://maven-site.nuiton.org/nuiton-js/changes-report.html
Added: tags/nuiton-js-jquery-i18n-properties-1.0.9-1/pom.xml
===================================================================
--- tags/nuiton-js-jquery-i18n-properties-1.0.9-1/pom.xml (rev 0)
+++ tags/nuiton-js-jquery-i18n-properties-1.0.9-1/pom.xml 2013-06-18 08:10:49 UTC (rev 93)
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <!-- ************************************************************* -->
+ <!-- *** POM Relationships *************************************** -->
+ <!-- ************************************************************* -->
+
+ <parent>
+ <groupId>org.nuiton.js</groupId>
+ <artifactId>nuiton-js-lib</artifactId>
+ <version>1.0</version>
+ </parent>
+
+ <artifactId>nuiton-js-jquery-i18n-properties</artifactId>
+ <version>1.0.9-1</version>
+
+ <name>Nuiton JS :: jQuery i18n properties</name>
+ <description>jQuery i18n properties jar packaging</description>
+
+ <url>http://code.google.com/p/jquery-i18n-properties</url>
+
+ <licenses>
+ <license>
+ <name>MIT License</name>
+ <url>http://opensource.org/licenses/mit-license.php</url>
+ </license>
+ </licenses>
+
+ <scm>
+ <connection>scm:svn:http://svn.nuiton.org/svn/nuiton-js/tags/nuiton-js-jquery-i18n-prop…</connection>
+ <developerConnection>scm:svn:http://svn.nuiton.org/svn/nuiton-js/tags/nuiton-js-jquery-i18n-prop…</developerConnection>
+ <url>http://www.nuiton.org/repositories/browse/nuiton-js/tags/nuiton-js-jquery-i…</url>
+ </scm>
+
+</project>
Added: tags/nuiton-js-jquery-i18n-properties-1.0.9-1/src/main/resources/META-INF/nuiton-js/wro-jquery-i18n-properties.xml
===================================================================
--- tags/nuiton-js-jquery-i18n-properties-1.0.9-1/src/main/resources/META-INF/nuiton-js/wro-jquery-i18n-properties.xml (rev 0)
+++ tags/nuiton-js-jquery-i18n-properties-1.0.9-1/src/main/resources/META-INF/nuiton-js/wro-jquery-i18n-properties.xml 2013-06-18 08:10:49 UTC (rev 93)
@@ -0,0 +1,5 @@
+<groups xmlns="http://www.isdc.ro/wro">
+ <group name='jquery.i18n.properties'>
+ <js>classpath:nuiton-js-jquery-i18n-properties/jquery.i18n.properties-1.0.9.js</js>
+ </group>
+</groups>
Added: tags/nuiton-js-jquery-i18n-properties-1.0.9-1/src/main/resources/nuiton-js-jquery-i18n-properties/jquery.i18n.properties-1.0.9.js
===================================================================
--- tags/nuiton-js-jquery-i18n-properties-1.0.9-1/src/main/resources/nuiton-js-jquery-i18n-properties/jquery.i18n.properties-1.0.9.js (rev 0)
+++ tags/nuiton-js-jquery-i18n-properties-1.0.9-1/src/main/resources/nuiton-js-jquery-i18n-properties/jquery.i18n.properties-1.0.9.js 2013-06-18 08:10:49 UTC (rev 93)
@@ -0,0 +1,474 @@
+/******************************************************************************
+ * jquery.i18n.properties
+ *
+ * Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and
+ * MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses.
+ *
+ * @version 1.0.x
+ * @author Nuno Fernandes
+ * @url www.codingwithcoffee.com
+ * @inspiration Localisation assistance for jQuery (http://keith-wood.name/localisation.html)
+ * by Keith Wood (kbwood{at}iinet.com.au) June 2007
+ *
+ *****************************************************************************/
+
+(function($) {
+$.i18n = {};
+
+/** Map holding bundle keys (if mode: 'map') */
+$.i18n.map = {};
+
+/**
+ * Load and parse message bundle files (.properties),
+ * making bundles keys available as javascript variables.
+ *
+ * i18n files are named <name>.js, or <name>_<language>.js or <name>_<language>_<country>.js
+ * Where:
+ * The <language> argument is a valid ISO Language Code. These codes are the lower-case,
+ * two-letter codes as defined by ISO-639. You can find a full list of these codes at a
+ * number of sites, such as: http://www.loc.gov/standards/iso639-2/englangn.html
+ * The <country> argument is a valid ISO Country Code. These codes are the upper-case,
+ * two-letter codes as defined by ISO-3166. You can find a full list of these codes at a
+ * number of sites, such as: http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/lis…
+ *
+ * Sample usage for a bundles/Messages.properties bundle:
+ * $.i18n.properties({
+ * name: 'Messages',
+ * language: 'en_US',
+ * path: 'bundles'
+ * });
+ * @param name (string/string[], optional) names of file to load (eg, 'Messages' or ['Msg1','Msg2']). Defaults to "Messages"
+ * @param language (string, optional) language/country code (eg, 'en', 'en_US', 'pt_PT'). if not specified, language reported by the browser will be used instead.
+ * @param path (string, optional) path of directory that contains file to load
+ * @param mode (string, optional) whether bundles keys are available as JavaScript variables/functions or as a map (eg, 'vars' or 'map')
+ * @param cache (boolean, optional) whether bundles should be cached by the browser, or forcibly reloaded on each page load. Defaults to false (i.e. forcibly reloaded)
+ * @param encoding (string, optional) the encoding to request for bundles. Property file resource bundles are specified to be in ISO-8859-1 format. Defaults to UTF-8 for backward compatibility.
+ * @param callback (function, optional) callback function to be called after script is terminated
+ */
+$.i18n.properties = function(settings) {
+ // set up settings
+ var defaults = {
+ name: 'Messages',
+ language: '',
+ path: '',
+ mode: 'vars',
+ cache: false,
+ encoding: 'UTF-8',
+ callback: null
+ };
+ settings = $.extend(defaults, settings);
+ if(settings.language === null || settings.language == '') {
+ settings.language = $.i18n.browserLang();
+ }
+ if(settings.language === null) {settings.language='';}
+
+ // load and parse bundle files
+ var files = getFiles(settings.name);
+ for(i=0; i<files.length; i++) {
+ // 1. load base (eg, Messages.properties)
+ loadAndParseFile(settings.path + files[i] + '.properties', settings);
+ // 2. with language code (eg, Messages_pt.properties)
+ if(settings.language.length >= 2) {
+ loadAndParseFile(settings.path + files[i] + '_' + settings.language.substring(0, 2) +'.properties', settings);
+ }
+ // 3. with language code and country code (eg, Messages_pt_PT.properties)
+ if(settings.language.length >= 5) {
+ loadAndParseFile(settings.path + files[i] + '_' + settings.language.substring(0, 5) +'.properties', settings);
+ }
+ }
+
+ // call callback
+ if(settings.callback){ settings.callback(); }
+};
+
+
+/**
+ * When configured with mode: 'map', allows access to bundle values by specifying its key.
+ * Eg, jQuery.i18n.prop('com.company.bundles.menu_add')
+ */
+$.i18n.prop = function(key /* Add parameters as function arguments as necessary */) {
+ var value = $.i18n.map[key];
+ if (value == null)
+ return '[' + key + ']';
+
+// if(arguments.length < 2) // No arguments.
+// //if(key == 'spv.lbl.modified') {alert(value);}
+// return value;
+
+// if (!$.isArray(placeHolderValues)) {
+// // If placeHolderValues is not an array, make it into one.
+// placeHolderValues = [placeHolderValues];
+// for (var i=2; i<arguments.length; i++)
+// placeHolderValues.push(arguments[i]);
+// }
+
+ // Place holder replacement
+ /**
+ * Tested with:
+ * test.t1=asdf ''{0}''
+ * test.t2=asdf '{0}' '{1}'{1}'zxcv
+ * test.t3=This is \"a quote" 'a''{0}''s'd{fgh{ij'
+ * test.t4="'''{'0}''" {0}{a}
+ * test.t5="'''{0}'''" {1}
+ * test.t6=a {1} b {0} c
+ * test.t7=a 'quoted \\ s\ttringy' \t\t x
+ *
+ * Produces:
+ * test.t1, p1 ==> asdf 'p1'
+ * test.t2, p1 ==> asdf {0} {1}{1}zxcv
+ * test.t3, p1 ==> This is "a quote" a'{0}'sd{fgh{ij
+ * test.t4, p1 ==> "'{0}'" p1{a}
+ * test.t5, p1 ==> "'{0}'" {1}
+ * test.t6, p1 ==> a {1} b p1 c
+ * test.t6, p1, p2 ==> a p2 b p1 c
+ * test.t6, p1, p2, p3 ==> a p2 b p1 c
+ * test.t7 ==> a quoted \ s tringy x
+ */
+
+ var i;
+ if (typeof(value) == 'string') {
+ // Handle escape characters. Done separately from the tokenizing loop below because escape characters are
+ // active in quoted strings.
+ i = 0;
+ while ((i = value.indexOf('\\', i)) != -1) {
+ if (value[i+1] == 't')
+ value = value.substring(0, i) + '\t' + value.substring((i++) + 2); // tab
+ else if (value[i+1] == 'r')
+ value = value.substring(0, i) + '\r' + value.substring((i++) + 2); // return
+ else if (value[i+1] == 'n')
+ value = value.substring(0, i) + '\n' + value.substring((i++) + 2); // line feed
+ else if (value[i+1] == 'f')
+ value = value.substring(0, i) + '\f' + value.substring((i++) + 2); // form feed
+ else if (value[i+1] == '\\')
+ value = value.substring(0, i) + '\\' + value.substring((i++) + 2); // \
+ else
+ value = value.substring(0, i) + value.substring(i+1); // Quietly drop the character
+ }
+
+ // Lazily convert the string to a list of tokens.
+ var arr = [], j, index;
+ i = 0;
+ while (i < value.length) {
+ if (value[i] == '\'') {
+ // Handle quotes
+ if (i == value.length-1)
+ value = value.substring(0, i); // Silently drop the trailing quote
+ else if (value[i+1] == '\'')
+ value = value.substring(0, i) + value.substring(++i); // Escaped quote
+ else {
+ // Quoted string
+ j = i + 2;
+ while ((j = value.indexOf('\'', j)) != -1) {
+ if (j == value.length-1 || value[j+1] != '\'') {
+ // Found start and end quotes. Remove them
+ value = value.substring(0,i) + value.substring(i+1, j) + value.substring(j+1);
+ i = j - 1;
+ break;
+ }
+ else {
+ // Found a double quote, reduce to a single quote.
+ value = value.substring(0,j) + value.substring(++j);
+ }
+ }
+
+ if (j == -1) {
+ // There is no end quote. Drop the start quote
+ value = value.substring(0,i) + value.substring(i+1);
+ }
+ }
+ }
+ else if (value[i] == '{') {
+ // Beginning of an unquoted place holder.
+ j = value.indexOf('}', i+1);
+ if (j == -1)
+ i++; // No end. Process the rest of the line. Java would throw an exception
+ else {
+ // Add 1 to the index so that it aligns with the function arguments.
+ index = parseInt(value.substring(i+1, j));
+ if (!isNaN(index) && index >= 0) {
+ // Put the line thus far (if it isn't empty) into the array
+ var s = value.substring(0, i);
+ if (s != "")
+ arr.push(s);
+ // Put the parameter reference into the array
+ arr.push(index);
+ // Start the processing over again starting from the rest of the line.
+ i = 0;
+ value = value.substring(j+1);
+ }
+ else
+ i = j + 1; // Invalid parameter. Leave as is.
+ }
+ }
+ else
+ i++;
+ }
+
+ // Put the remainder of the no-empty line into the array.
+ if (value != "")
+ arr.push(value);
+ value = arr;
+
+ // Make the array the value for the entry.
+ $.i18n.map[key] = arr;
+ }
+
+ if (value.length == 0)
+ return "";
+ if (value.lengh == 1 && typeof(value[0]) == "string")
+ return value[0];
+
+ var s = "";
+ for (i=0; i<value.length; i++) {
+ if (typeof(value[i]) == "string")
+ s += value[i];
+ // Must be a number
+ else if (value[i] + 1 < arguments.length)
+ s += arguments[value[i] + 1];
+ else
+ s += "{"+ value[i] +"}";
+ }
+
+ return s;
+};
+
+/** Language reported by browser, normalized code */
+$.i18n.browserLang = function() {
+ return normaliseLanguageCode(navigator.language /* Mozilla */ || navigator.userLanguage /* IE */);
+}
+
+
+/** Load and parse .properties files */
+function loadAndParseFile(filename, settings) {
+ $.ajax({
+ url: filename,
+ async: false,
+ cache: settings.cache,
+ contentType:'text/plain;charset='+ settings.encoding,
+ dataType: 'text',
+ success: function(data, status) {
+ parseData(data, settings.mode);
+ }
+ });
+}
+
+/** Parse .properties files */
+function parseData(data, mode) {
+ var parsed = '';
+ var parameters = data.split( /\n/ );
+ var regPlaceHolder = /(\{\d+\})/g;
+ var regRepPlaceHolder = /\{(\d+)\}/g;
+ var unicodeRE = /(\\u.{4})/ig;
+ for(var i=0; i<parameters.length; i++ ) {
+ parameters[i] = parameters[i].replace( /^\s\s*/, '' ).replace( /\s\s*$/, '' ); // trim
+ if(parameters[i].length > 0 && parameters[i].match("^#")!="#") { // skip comments
+ var pair = parameters[i].split('=');
+ if(pair.length > 0) {
+ /** Process key & value */
+ var name = unescape(pair[0]).replace( /^\s\s*/, '' ).replace( /\s\s*$/, '' ); // trim
+ var value = pair.length == 1 ? "" : pair[1];
+ // process multi-line values
+ while(value.match(/\\$/)=="\\") {
+ value = value.substring(0, value.length - 1);
+ value += parameters[++i].replace( /\s\s*$/, '' ); // right trim
+ }
+ // Put values with embedded '='s back together
+ for(var s=2;s<pair.length;s++){ value +='=' + pair[s]; }
+ value = value.replace( /^\s\s*/, '' ).replace( /\s\s*$/, '' ); // trim
+
+ /** Mode: bundle keys in a map */
+ if(mode == 'map' || mode == 'both') {
+ // handle unicode chars possibly left out
+ var unicodeMatches = value.match(unicodeRE);
+ if(unicodeMatches) {
+ for(var u=0; u<unicodeMatches.length; u++) {
+ value = value.replace( unicodeMatches[u], unescapeUnicode(unicodeMatches[u]));
+ }
+ }
+ // add to map
+ $.i18n.map[name] = value;
+ }
+
+ /** Mode: bundle keys as vars/functions */
+ if(mode == 'vars' || mode == 'both') {
+ value = value.replace( /"/g, '\\"' ); // escape quotation mark (")
+
+ // make sure namespaced key exists (eg, 'some.key')
+ checkKeyNamespace(name);
+
+ // value with variable substitutions
+ if(regPlaceHolder.test(value)) {
+ var parts = value.split(regPlaceHolder);
+ // process function args
+ var first = true;
+ var fnArgs = '';
+ var usedArgs = [];
+ for(var p=0; p<parts.length; p++) {
+ if(regPlaceHolder.test(parts[p]) && (usedArgs.length == 0 || usedArgs.indexOf(parts[p]) == -1)) {
+ if(!first) {fnArgs += ',';}
+ fnArgs += parts[p].replace(regRepPlaceHolder, 'v$1');
+ usedArgs.push(parts[p]);
+ first = false;
+ }
+ }
+ parsed += name + '=function(' + fnArgs + '){';
+ // process function body
+ var fnExpr = '"' + value.replace(regRepPlaceHolder, '"+v$1+"') + '"';
+ parsed += 'return ' + fnExpr + ';' + '};';
+
+ // simple value
+ }else{
+ parsed += name+'="'+value+'";';
+ }
+ } // END: Mode: bundle keys as vars/functions
+ } // END: if(pair.length > 0)
+ } // END: skip comments
+ }
+ eval(parsed);
+}
+
+/** Make sure namespace exists (for keys with dots in name) */
+// TODO key parts that start with numbers quietly fail. i.e. month.short.1=Jan
+function checkKeyNamespace(key) {
+ var regDot = /\./;
+ if(regDot.test(key)) {
+ var fullname = '';
+ var names = key.split( /\./ );
+ for(var i=0; i<names.length; i++) {
+ if(i>0) {fullname += '.';}
+ fullname += names[i];
+ if(eval('typeof '+fullname+' == "undefined"')) {
+ eval(fullname + '={};');
+ }
+ }
+ }
+}
+
+/** Make sure filename is an array */
+function getFiles(names) {
+ return (names && names.constructor == Array) ? names : [names];
+}
+
+/** Ensure language code is in the format aa_AA. */
+function normaliseLanguageCode(lang) {
+ lang = lang.toLowerCase();
+ if(lang.length > 3) {
+ lang = lang.substring(0, 3) + lang.substring(3).toUpperCase();
+ }
+ return lang;
+}
+
+/** Unescape unicode chars ('\u00e3') */
+function unescapeUnicode(str) {
+ // unescape unicode codes
+ var codes = [];
+ var code = parseInt(str.substr(2), 16);
+ if (code >= 0 && code < Math.pow(2, 16)) {
+ codes.push(code);
+ }
+ // convert codes to text
+ var unescaped = '';
+ for (var i = 0; i < codes.length; ++i) {
+ unescaped += String.fromCharCode(codes[i]);
+ }
+ return unescaped;
+}
+
+/* Cross-Browser Split 1.0.1
+(c) Steven Levithan <stevenlevithan.com>; MIT License
+An ECMA-compliant, uniform cross-browser split method */
+var cbSplit;
+// avoid running twice, which would break `cbSplit._nativeSplit`'s reference to the native `split`
+if (!cbSplit) {
+ cbSplit = function(str, separator, limit) {
+ // if `separator` is not a regex, use the native `split`
+ if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
+ if(typeof cbSplit._nativeSplit == "undefined")
+ return str.split(separator, limit);
+ else
+ return cbSplit._nativeSplit.call(str, separator, limit);
+ }
+
+ var output = [],
+ lastLastIndex = 0,
+ flags = (separator.ignoreCase ? "i" : "") +
+ (separator.multiline ? "m" : "") +
+ (separator.sticky ? "y" : ""),
+ separator = RegExp(separator.source, flags + "g"), // make `global` and avoid `lastIndex` issues by working with a copy
+ separator2, match, lastIndex, lastLength;
+
+ str = str + ""; // type conversion
+ if (!cbSplit._compliantExecNpcg) {
+ separator2 = RegExp("^" + separator.source + "$(?!\\s)", flags); // doesn't need /g or /y, but they don't hurt
+ }
+
+ /* behavior for `limit`: if it's...
+ - `undefined`: no limit.
+ - `NaN` or zero: return an empty array.
+ - a positive number: use `Math.floor(limit)`.
+ - a negative number: no limit.
+ - other: type-convert, then use the above rules. */
+ if (limit === undefined || +limit < 0) {
+ limit = Infinity;
+ } else {
+ limit = Math.floor(+limit);
+ if (!limit) {
+ return [];
+ }
+ }
+
+ while (match = separator.exec(str)) {
+ lastIndex = match.index + match[0].length; // `separator.lastIndex` is not reliable cross-browser
+
+ if (lastIndex > lastLastIndex) {
+ output.push(str.slice(lastLastIndex, match.index));
+
+ // fix browsers whose `exec` methods don't consistently return `undefined` for nonparticipating capturing groups
+ if (!cbSplit._compliantExecNpcg && match.length > 1) {
+ match[0].replace(separator2, function () {
+ for (var i = 1; i < arguments.length - 2; i++) {
+ if (arguments[i] === undefined) {
+ match[i] = undefined;
+ }
+ }
+ });
+ }
+
+ if (match.length > 1 && match.index < str.length) {
+ Array.prototype.push.apply(output, match.slice(1));
+ }
+
+ lastLength = match[0].length;
+ lastLastIndex = lastIndex;
+
+ if (output.length >= limit) {
+ break;
+ }
+ }
+
+ if (separator.lastIndex === match.index) {
+ separator.lastIndex++; // avoid an infinite loop
+ }
+ }
+
+ if (lastLastIndex === str.length) {
+ if (lastLength || !separator.test("")) {
+ output.push("");
+ }
+ } else {
+ output.push(str.slice(lastLastIndex));
+ }
+
+ return output.length > limit ? output.slice(0, limit) : output;
+ };
+
+ cbSplit._compliantExecNpcg = /()??/.exec("")[1] === undefined; // NPCG: nonparticipating capturing group
+ cbSplit._nativeSplit = String.prototype.split;
+
+} // end `if (!cbSplit)`
+String.prototype.split = function (separator, limit) {
+ return cbSplit(this, separator, limit);
+};
+
+})(jQuery);
+
\ No newline at end of file
Added: tags/nuiton-js-jquery-i18n-properties-1.0.9-1/src/site/rst/index.rst
===================================================================
--- tags/nuiton-js-jquery-i18n-properties-1.0.9-1/src/site/rst/index.rst (rev 0)
+++ tags/nuiton-js-jquery-i18n-properties-1.0.9-1/src/site/rst/index.rst 2013-06-18 08:10:49 UTC (rev 93)
@@ -0,0 +1,18 @@
+
+Utilisation
+===========
+
+::
+
+ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+ <script language="javascript" type="text/javascript" src="<c:url value="/nuiton-js/jquery.i18n.properties-1.0.9.js"/>"></script>
+
+JS disponible
+-------------
+
+- jquery.i18n.properties.js
+
+CSS disponible
+--------------
+
+aucun
Added: tags/nuiton-js-jquery-i18n-properties-1.0.9-1/src/site/site_fr.xml
===================================================================
--- tags/nuiton-js-jquery-i18n-properties-1.0.9-1/src/site/site_fr.xml (rev 0)
+++ tags/nuiton-js-jquery-i18n-properties-1.0.9-1/src/site/site_fr.xml 2013-06-18 08:10:49 UTC (rev 93)
@@ -0,0 +1,49 @@
+<project name="${project.name}">
+
+ <bannerLeft>
+ <src alt="NuitonJS">logo.jpg</src>
+ <name>${project.name}</name>
+ <href>index.html</href>
+ </bannerLeft>
+
+ <body>
+
+ <head>
+ <script type="text/javascript"
+ src="http://maven-site.nuiton.org/public/js/mavenpom-site.js">
+ </script>
+
+ <link rel="stylesheet" type="text/css"
+ href="http://maven-site.nuiton.org/public/css/mavenpom-site.css"/>
+ </head>
+
+ <breadcrumbs>
+ <item name="${project.name}" href="${project.url}"/>
+ </breadcrumbs>
+
+ <menu ref="parent"/>
+
+ <menu name="Utilisateurs">
+ <item name="Accueil" href="index.html"/>
+ </menu>
+
+ <menu ref="reports"/>
+
+ <footer>
+
+ <div id='projectMetas'
+ projectversion='${project.version}'
+ platform='${project.platform}'
+ projectid='${project.projectId}'
+ scm='${project.scm.connection}'
+ scmwebeditorenabled='${project.scmwebeditorEnabled}'
+ scmwebeditorurl='${project.scmwebeditorUrl}'
+ siteSourcesType='${project.siteSourcesType}'
+ piwikEnabled='${project.piwikEnabled}'
+ piwikId='${project.piwikId}'>
+ </div>
+ </footer>
+
+ </body>
+
+</project>
1
0
17 Jun '13
Author: kmorin
Date: 2013-06-17 17:45:26 +0200 (Mon, 17 Jun 2013)
New Revision: 92
Url: http://nuiton.org/projects/nuiton-js/repository/revisions/92
Log:
add canjs 1.1.6
Added:
tags/nuiton-js-canjs-1.1.6-1/
tags/nuiton-js-canjs-1.1.6-1/LICENSE.txt
tags/nuiton-js-canjs-1.1.6-1/README.txt
tags/nuiton-js-canjs-1.1.6-1/changelog.txt
tags/nuiton-js-canjs-1.1.6-1/pom.xml
tags/nuiton-js-canjs-1.1.6-1/src/
tags/nuiton-js-canjs-1.1.6-1/src/main/
tags/nuiton-js-canjs-1.1.6-1/src/main/resources/
tags/nuiton-js-canjs-1.1.6-1/src/main/resources/META-INF/
tags/nuiton-js-canjs-1.1.6-1/src/main/resources/META-INF/nuiton-js/
tags/nuiton-js-canjs-1.1.6-1/src/main/resources/META-INF/nuiton-js/wro-canjs.xml
tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/
tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.construct.super.js
tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.control.plugin.js
tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.dojo.js
tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.fixture.js
tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.jquery.js
tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.model.queue.js
tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.mootools.js
tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.object.js
tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.observe.attributes.js
tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.observe.backup.js
tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.observe.delegate.js
tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.observe.setter.js
tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.observe.validations.js
tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.util.string.js
tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.view.modifiers.js
tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.view.mustache.js
tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.yui.js
tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.zepto.js
tags/nuiton-js-canjs-1.1.6-1/src/site/
tags/nuiton-js-canjs-1.1.6-1/src/site/rst/
tags/nuiton-js-canjs-1.1.6-1/src/site/rst/index.rst
tags/nuiton-js-canjs-1.1.6-1/src/site/site_fr.xml
Added: tags/nuiton-js-canjs-1.1.6-1/LICENSE.txt
===================================================================
--- tags/nuiton-js-canjs-1.1.6-1/LICENSE.txt (rev 0)
+++ tags/nuiton-js-canjs-1.1.6-1/LICENSE.txt 2013-06-17 15:45:26 UTC (rev 92)
@@ -0,0 +1,166 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
+
Added: tags/nuiton-js-canjs-1.1.6-1/README.txt
===================================================================
--- tags/nuiton-js-canjs-1.1.6-1/README.txt (rev 0)
+++ tags/nuiton-js-canjs-1.1.6-1/README.txt 2013-06-17 15:45:26 UTC (rev 92)
@@ -0,0 +1 @@
+
Added: tags/nuiton-js-canjs-1.1.6-1/changelog.txt
===================================================================
--- tags/nuiton-js-canjs-1.1.6-1/changelog.txt (rev 0)
+++ tags/nuiton-js-canjs-1.1.6-1/changelog.txt 2013-06-17 15:45:26 UTC (rev 92)
@@ -0,0 +1,2 @@
+Please refer to the release note :
+ http://maven-site.nuiton.org/nuiton-js/changes-report.html
Added: tags/nuiton-js-canjs-1.1.6-1/pom.xml
===================================================================
--- tags/nuiton-js-canjs-1.1.6-1/pom.xml (rev 0)
+++ tags/nuiton-js-canjs-1.1.6-1/pom.xml 2013-06-17 15:45:26 UTC (rev 92)
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <!-- ************************************************************* -->
+ <!-- *** POM Relationships *************************************** -->
+ <!-- ************************************************************* -->
+
+ <parent>
+ <groupId>org.nuiton.js</groupId>
+ <artifactId>nuiton-js-lib</artifactId>
+ <version>1.0</version>
+ </parent>
+
+ <artifactId>nuiton-js-canjs</artifactId>
+ <version>1.1.6-1</version>
+
+ <name>Nuiton JS :: CanJS</name>
+ <description>CanJS jar packaging</description>
+
+ <url>http://canjs.com/</url>
+
+ <licenses>
+ <license>
+ <name>MIT License</name>
+ <url>https://github.com/bitovi/canjs/blob/master/license.md</url>
+ </license>
+ </licenses>
+
+ <scm>
+ <connection>scm:svn:http://svn.nuiton.org/svn/nuiton-js/tags/nuiton-js-canjs-1.1.6-1</connection>
+ <developerConnection>scm:svn:http://svn.nuiton.org/svn/nuiton-js/tags/nuiton-js-canjs-1.1.6-1</developerConnection>
+ <url>http://www.nuiton.org/repositories/browse/nuiton-js/tags/nuiton-js-canjs-1.…</url>
+ </scm>
+
+</project>
Added: tags/nuiton-js-canjs-1.1.6-1/src/main/resources/META-INF/nuiton-js/wro-canjs.xml
===================================================================
--- tags/nuiton-js-canjs-1.1.6-1/src/main/resources/META-INF/nuiton-js/wro-canjs.xml (rev 0)
+++ tags/nuiton-js-canjs-1.1.6-1/src/main/resources/META-INF/nuiton-js/wro-canjs.xml 2013-06-17 15:45:26 UTC (rev 92)
@@ -0,0 +1,97 @@
+<!--
+ #%L
+ Nuiton JS :: JQuery
+ $Id:$
+ $HeadURL:$
+ %%
+ Copyright (C) 2012 - 2013 CodeLutin, Chatellier Eric
+ %%
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Lesser Public License for more details.
+
+ You should have received a copy of the GNU General Lesser Public
+ License along with this program. If not, see
+ <http://www.gnu.org/licenses/lgpl-3.0.html>.
+ #L%
+ -->
+<groups xmlns="http://www.isdc.ro/wro">
+ <group name='can.jquery'>
+ <js>classpath:nuiton-js-canjs/can.jquery.js</js>
+ </group>
+
+ <group name='can.dojo'>
+ <js>classpath:nuiton-js-canjs/can.dojo.js</js>
+ </group>
+
+ <group name='can.mootools'>
+ <js>classpath:nuiton-js-canjs/can.mootools.js</js>
+ </group>
+
+ <group name='can.yui'>
+ <js>classpath:nuiton-js-canjs/can.yui.js</js>
+ </group>
+
+ <group name='can.zepto'>
+ <js>classpath:nuiton-js-canjs/can.zepto.js</js>
+ </group>
+
+ <group name='can.construct.super'>
+ <js>classpath:nuiton-js-canjs/can.construct.super.js</js>
+ </group>
+
+ <group name='can.control.plugin'>
+ <js>classpath:nuiton-js-canjs/can.control.plugin.js</js>
+ </group>
+
+ <group name='can.fixture'>
+ <js>classpath:nuiton-js-canjs/can.fixture.js</js>
+ </group>
+
+ <group name='can.model.queue'>
+ <js>classpath:nuiton-js-canjs/can.model.queue.js</js>
+ </group>
+
+ <group name='can.object'>
+ <js>classpath:nuiton-js-canjs/can.object.js</js>
+ </group>
+
+ <group name='can.observe.attributes'>
+ <js>classpath:nuiton-js-canjs/can.observe.attributes.js</js>
+ </group>
+
+ <group name='can.observe.backup'>
+ <js>classpath:nuiton-js-canjs/can.observe.backup.js</js>
+ </group>
+
+ <group name='can.observe.delegate'>
+ <js>classpath:nuiton-js-canjs/can.observe.delegate.js</js>
+ </group>
+
+ <group name='can.observe.setter'>
+ <js>classpath:nuiton-js-canjs/can.observe.setter.js</js>
+ </group>
+
+ <group name='can.observe.validations'>
+ <js>classpath:nuiton-js-canjs/can.observe.validations.js</js>
+ </group>
+
+ <group name='can.util.string'>
+ <js>classpath:nuiton-js-canjs/can.util.string.js</js>
+ </group>
+
+ <group name='can.view.modifiers'>
+ <js>classpath:nuiton-js-canjs/can.view.modifiers.js</js>
+ </group>
+
+ <group name='can.view.mustache'>
+ <js>classpath:nuiton-js-canjs/can.view.mustache.js</js>
+ </group>
+
+</groups>
Added: tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.construct.super.js
===================================================================
--- tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.construct.super.js (rev 0)
+++ tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.construct.super.js 2013-06-17 15:45:26 UTC (rev 92)
@@ -0,0 +1,54 @@
+/*!
+ * CanJS - 1.1.6
+ * http://canjs.us/
+ * Copyright (c) 2013 Bitovi
+ * Wed, 29 May 2013 18:59:28 GMT
+ * Licensed MIT
+ * Includes: can/construct/super
+ * Download from: http://canjs.com
+ */
+(function(can, Construct) {
+
+ // tests if we can get super in .toString()
+ var isFunction = can.isFunction,
+
+ fnTest = /xyz/.test(function() {
+ xyz;
+ }) ? /\b_super\b/ : /.*/;
+
+ // overwrites a single property so it can still call super
+ can.Construct._overwrite = function(addTo, base, name, val) {
+ // Check if we're overwriting an existing function
+ addTo[name] = isFunction(val) &&
+ isFunction(base[name]) &&
+ fnTest.test(val) ? (function(name, fn) {
+ return function() {
+ var tmp = this._super,
+ ret;
+
+ // Add a new ._super() method that is the same method
+ // but on the super-class
+ this._super = base[name];
+
+ // The method only need to be bound temporarily, so we
+ // remove it when we're done executing
+ ret = fn.apply(this, arguments);
+ this._super = tmp;
+ return ret;
+ };
+ })(name, val) : val;
+ }
+
+ // overwrites an object with methods, sets up _super
+ // newProps - new properties
+ // oldProps - where the old properties might be
+ // addTo - what we are adding to
+ can.Construct._inherit = function(newProps, oldProps, addTo) {
+ addTo = addTo || newProps
+ for (var name in newProps) {
+ can.Construct._overwrite(addTo, oldProps, name, newProps[name]);
+ }
+ }
+
+ return can;
+ })(can);
\ No newline at end of file
Added: tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.control.plugin.js
===================================================================
--- tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.control.plugin.js (rev 0)
+++ tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.control.plugin.js 2013-06-17 15:45:26 UTC (rev 92)
@@ -0,0 +1,109 @@
+/*!
+ * CanJS - 1.1.6
+ * http://canjs.us/
+ * Copyright (c) 2013 Bitovi
+ * Wed, 29 May 2013 18:59:29 GMT
+ * Licensed MIT
+ * Includes: can/control/plugin
+ * Download from: http://canjs.com
+ */
+(function($, can) {
+ //used to determine if a control instance is one of controllers
+ //controllers can be strings or classes
+ var i,
+ isAControllerOf = function(instance, controllers) {
+ for (i = 0; i < controllers.length; i++) {
+ if (typeof controllers[i] == 'string' ? instance.constructor._shortName == controllers[i] : instance instanceof controllers[i]) {
+ return true;
+ }
+ }
+ return false;
+ },
+ makeArray = can.makeArray,
+ old = can.Control.setup;
+
+ can.Control.setup = function() {
+ // if you didn't provide a name, or are control, don't do anything
+ if (this !== can.Control) {
+
+
+ var pluginName = this.pluginName || this._fullName;
+
+ // create jQuery plugin
+ if (pluginName !== 'can_control') {
+ this.plugin(pluginName);
+ }
+
+ old.apply(this, arguments);
+ }
+ };
+
+ $.fn.extend({
+
+
+ controls: function() {
+ var controllerNames = makeArray(arguments),
+ instances = [],
+ controls, c, cname;
+ //check if arguments
+ this.each(function() {
+
+ controls = can.$(this).data("controls");
+ if (!controls) {
+ return;
+ }
+ for (var i = 0; i < controls.length; i++) {
+ c = controls[i];
+ if (!controllerNames.length || isAControllerOf(c, controllerNames)) {
+ instances.push(c);
+ }
+ }
+ });
+ return instances;
+ },
+
+
+ control: function(control) {
+ return this.controls.apply(this, arguments)[0];
+ }
+ });
+
+ can.Control.plugin = function(pluginname) {
+ var control = this;
+
+ if (!$.fn[pluginname]) {
+ $.fn[pluginname] = function(options) {
+
+ var args = makeArray(arguments), //if the arg is a method on this control
+ isMethod = typeof options == "string" && $.isFunction(control.prototype[options]),
+ meth = args[0],
+ returns;
+ this.each(function() {
+ //check if created
+ var plugin = can.$(this).control(control);
+
+ if (plugin) {
+ if (isMethod) {
+ // call a method on the control with the remaining args
+ returns = plugin[meth].apply(plugin, args.slice(1));
+ } else {
+ // call the plugin's update method
+ plugin.update.apply(plugin, args);
+ }
+ } else {
+ //create a new control instance
+ control.newInstance.apply(control, [this].concat(args));
+ }
+ });
+ return returns !== undefined ? returns : this;
+ };
+ }
+ }
+
+ can.Control.prototype.update = function(options) {
+ can.extend(this.options, options);
+ this.on();
+ };
+
+ return can;
+ })(jQuery, can);
\ No newline at end of file
Added: tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.dojo.js
===================================================================
--- tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.dojo.js (rev 0)
+++ tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.dojo.js 2013-06-17 15:45:26 UTC (rev 92)
@@ -0,0 +1,5170 @@
+/*!
+ * CanJS - 1.1.6
+ * http://canjs.us/
+ * Copyright (c) 2013 Bitovi
+ * Wed, 29 May 2013 18:59:27 GMT
+ * Licensed MIT
+ * Includes: can/construct,can/observe,can/observe/compute,can/model,can/view,can/view/ejs,can/control,can/route,can/control/route
+ * Download from: http://canjs.com
+ */
+(function(undefined) {
+
+ // ## util/can.js
+ var __m4 = (function() {
+ var can = window.can || {};
+ if (typeof GLOBALCAN === 'undefined' || GLOBALCAN !== false) {
+ window.can = can;
+ }
+
+ can.isDeferred = function(obj) {
+ var isFunction = this.isFunction;
+ // Returns `true` if something looks like a deferred.
+ return obj && isFunction(obj.then) && isFunction(obj.pipe);
+ };
+
+ var cid = 0;
+ can.cid = function(object, name) {
+ if (object._cid) {
+ return object._cid
+ } else {
+ return object._cid = (name || "") + (++cid)
+ }
+ }
+ can.VERSION = '@EDGE';
+ return can;
+ })();
+
+ // ## util/event.js
+ var __m6 = (function(can) {
+
+ // event.js
+ // ---------
+ // _Basic event wrapper._
+ can.addEvent = function(event, fn) {
+ if (!this.__bindEvents) {
+ this.__bindEvents = {};
+ }
+ var eventName = event.split(".")[0];
+
+ if (!this.__bindEvents[eventName]) {
+ this.__bindEvents[eventName] = [];
+ }
+ this.__bindEvents[eventName].push({
+ handler: fn,
+ name: event
+ });
+ return this;
+ };
+ can.removeEvent = function(event, fn) {
+ if (!this.__bindEvents) {
+ return;
+ }
+ var i = 0,
+ events = this.__bindEvents[event.split(".")[0]],
+ ev;
+ while (i < events.length) {
+ ev = events[i]
+ if ((fn && ev.handler === fn) || (!fn && ev.name === event)) {
+ events.splice(i, 1);
+ } else {
+ i++;
+ }
+ }
+ return this;
+ };
+ can.dispatch = function(event) {
+ if (!this.__bindEvents) {
+ return;
+ }
+
+ var eventName = event.type.split(".")[0],
+ handlers = (this.__bindEvents[eventName] || []).slice(0),
+ self = this,
+ args = [event].concat(event.data || []);
+
+ can.each(handlers, function(ev) {
+ event.data = args.slice(1);
+ ev.handler.apply(self, args);
+ });
+ }
+
+ return can;
+
+ })(__m4);
+
+ // ## util/fragment.js
+ var __m7 = (function(can) {
+
+ // fragment.js
+ // ---------
+ // _DOM Fragment support._
+ var fragmentRE = /^\s*<(\w+)[^>]*>/,
+ fragment = function(html, name) {
+ if (name === undefined) {
+ name = fragmentRE.test(html) && RegExp.$1;
+ }
+
+ if (html && can.isFunction(html.replace)) {
+ // Fix "XHTML"-style tags in all browsers
+ html = html.replace(/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, "<$1></$2>");
+ }
+
+ var container = document.createElement('div'),
+ temp = document.createElement('div')
+
+ // IE's parser will strip any `<tr><td>` tags when `innerHTML`
+ // is called on a `tbody`. To get around this, we construct a
+ // valid table with a `tbody` that has the `innerHTML` we want.
+ // Then the container is the `firstChild` of the `tbody`.
+ // [source](http://www.ericvasilik.com/2006/07/code-karma.html).
+ if (name === "tbody" || name === "tfoot" || name === "thead") {
+ temp.innerHTML = "<table>" + html + "</table>";
+ container = temp.firstChild.nodeType === 3 ? temp.lastChild : temp.firstChild;
+ } else if (name === "tr") {
+ temp.innerHTML = "<table><tbody>" + html + "</tbody></table>";
+ container = temp.firstChild.nodeType === 3 ? temp.lastChild : temp.firstChild.firstChild;
+ } else if (name === "td" || name === "th") {
+ temp.innerHTML = "<table><tbody><tr>" + html + "</tr></tbody></table>";
+ container = temp.firstChild.nodeType === 3 ? temp.lastChild : temp.firstChild.firstChild.firstChild;
+ } else if (name === 'option') {
+ temp.innerHTML = "<select>" + html + "</select>";
+ container = temp.firstChild.nodeType === 3 ? temp.lastChild : temp.firstChild;
+ } else {
+ container.innerHTML = '' + html;
+ }
+
+ // IE8 barfs if you pass slice a `childNodes` object, so make a copy.
+ var tmp = {},
+ children = container.childNodes;
+ tmp.length = children.length;
+ for (var i = 0; i < children.length; i++) {
+ tmp[i] = children[i];
+ }
+ return [].slice.call(tmp);
+ }
+
+ can.buildFragment = function(html, nodes) {
+ var parts = fragment(html),
+ frag = document.createDocumentFragment();
+
+ can.each(parts, function(part) {
+ frag.appendChild(part);
+ })
+ return frag;
+ };
+
+ return can;
+ })(__m4);
+
+ // ## util/array/each.js
+ var __m8 = (function(can) {
+ can.each = function(elements, callback, context) {
+ var i = 0,
+ key;
+ if (elements) {
+ if (typeof elements.length === 'number' && elements.pop) {
+ if (elements.attr) {
+ elements.attr('length');
+ }
+ for (key = elements.length; i < key; i++) {
+ if (callback.call(context || elements[i], elements[i], i, elements) === false) {
+ break;
+ }
+ }
+ } else if (elements.hasOwnProperty) {
+ for (key in elements) {
+ if (elements.hasOwnProperty(key)) {
+ if (callback.call(context || elements[key], elements[key], key, elements) === false) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ return elements;
+ };
+
+ return can;
+ })(__m4);
+
+ // ## util/object/isplain/isplain.js
+ var __m9 = (function(can) {
+ var core_hasOwn = Object.prototype.hasOwnProperty,
+ isWindow = function(obj) {
+ return obj != null && obj == obj.window;
+ },
+ isPlainObject = function(obj) {
+ // Must be an Object.
+ // Because of IE, we also have to check the presence of the constructor property.
+ // Make sure that DOM nodes and window objects don't pass through, as well
+ if (!obj || (typeof obj !== "object") || obj.nodeType || isWindow(obj)) {
+ return false;
+ }
+
+ try {
+ // Not own constructor property must be Object
+ if (obj.constructor && !core_hasOwn.call(obj, "constructor") && !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) {
+ return false;
+ }
+ } catch (e) {
+ // IE8,9 Will throw exceptions on certain host objects #9897
+ return false;
+ }
+
+ // Own properties are enumerated firstly, so to speed up,
+ // if last one is own, then all properties are own.
+
+ var key;
+ for (key in obj) {}
+
+ return key === undefined || core_hasOwn.call(obj, key);
+ }
+
+ can.isPlainObject = isPlainObject;
+ return can;
+ })(__m4);
+
+ // ## util/deferred.js
+ var __m10 = (function(can) {
+
+ // deferred.js
+ // ---------
+ // _Lightweight, jQuery style deferreds._
+ // extend is usually provided by the wrapper but to avoid steal.then calls
+ // we define a simple extend here as well
+ var extend = function(target, src) {
+ for (var key in src) {
+ if (src.hasOwnProperty(key)) {
+ target[key] = src[key];
+ }
+ }
+ },
+ Deferred = function(func) {
+ if (!(this instanceof Deferred))
+ return new Deferred();
+
+ this._doneFuncs = [];
+ this._failFuncs = [];
+ this._resultArgs = null;
+ this._status = "";
+
+ // Check for option `function` -- call it with this as context and as first
+ // parameter, as specified in jQuery API.
+ func && func.call(this, this);
+ };
+
+ can.Deferred = Deferred;
+ can.when = Deferred.when = function() {
+ var args = can.makeArray(arguments);
+ if (args.length < 2) {
+ var obj = args[0];
+ if (obj && (can.isFunction(obj.isResolved) && can.isFunction(obj.isRejected))) {
+ return obj;
+ } else {
+ return Deferred().resolve(obj);
+ }
+ } else {
+
+ var df = Deferred(),
+ done = 0,
+ // Resolve params -- params of each resolve, we need to track them down
+ // to be able to pass them in the correct order if the master
+ // needs to be resolved.
+ rp = [];
+
+ can.each(args, function(arg, j) {
+ arg.done(function() {
+ rp[j] = (arguments.length < 2) ? arguments[0] : arguments;
+ if (++done == args.length) {
+ df.resolve.apply(df, rp);
+ }
+ }).fail(function() {
+ df.reject((arguments.length === 1) ? arguments[0] : arguments);
+ });
+ });
+
+ return df;
+
+ }
+ }
+
+ var resolveFunc = function(type, _status) {
+ return function(context) {
+ var args = this._resultArgs = (arguments.length > 1) ? arguments[1] : [];
+ return this.exec(context, this[type], args, _status);
+ }
+ },
+ doneFunc = function(type, _status) {
+ return function() {
+ var self = this;
+ // In Safari, the properties of the `arguments` object are not enumerable,
+ // so we have to convert arguments to an `Array` that allows `can.each` to loop over them.
+ can.each(Array.prototype.slice.call(arguments), function(v, i, args) {
+ if (!v)
+ return;
+ if (v.constructor === Array) {
+ args.callee.apply(self, v)
+ } else {
+ // Immediately call the `function` if the deferred has been resolved.
+ if (self._status === _status)
+ v.apply(self, self._resultArgs || []);
+
+ self[type].push(v);
+ }
+ });
+ return this;
+ }
+ };
+
+ extend(Deferred.prototype, {
+ pipe: function(done, fail) {
+ var d = can.Deferred();
+ this.done(function() {
+ d.resolve(done.apply(this, arguments));
+ });
+
+ this.fail(function() {
+ if (fail) {
+ d.reject(fail.apply(this, arguments));
+ } else {
+ d.reject.apply(d, arguments);
+ }
+ });
+ return d;
+ },
+ resolveWith: resolveFunc("_doneFuncs", "rs"),
+ rejectWith: resolveFunc("_failFuncs", "rj"),
+ done: doneFunc("_doneFuncs", "rs"),
+ fail: doneFunc("_failFuncs", "rj"),
+ always: function() {
+ var args = can.makeArray(arguments);
+ if (args.length && args[0])
+ this.done(args[0]).fail(args[0]);
+
+ return this;
+ },
+
+ then: function() {
+ var args = can.makeArray(arguments);
+ // Fail `function`(s)
+ if (args.length > 1 && args[1])
+ this.fail(args[1]);
+
+ // Done `function`(s)
+ if (args.length && args[0])
+ this.done(args[0]);
+
+ return this;
+ },
+
+ state: function() {
+ switch (this._status) {
+ case 'rs':
+ return 'resolved';
+ case 'rj':
+ return 'rejected';
+ default:
+ return 'pending';
+ }
+ },
+
+ isResolved: function() {
+ return this._status === "rs";
+ },
+
+ isRejected: function() {
+ return this._status === "rj";
+ },
+
+ reject: function() {
+ return this.rejectWith(this, arguments);
+ },
+
+ resolve: function() {
+ return this.resolveWith(this, arguments);
+ },
+
+ exec: function(context, dst, args, st) {
+ if (this._status !== "")
+ return this;
+
+ this._status = st;
+
+ can.each(dst, function(d) {
+ d.apply(context, args);
+ });
+
+ return this;
+ }
+ });
+
+ return can;
+ })(__m4);
+
+ // ## util/hashchange.js
+ var __m11 = (function() {
+ // This is a workaround for libraries that don't natively listen to the window hashchange event
+ ! function() {
+ var addEvent = function(el, ev, fn) {
+ if (el.addEventListener) {
+ el.addEventListener(ev, fn, false);
+ } else if (el.attachEvent) {
+ el.attachEvent('on' + ev, fn);
+ } else {
+ el['on' + ev] = fn;
+ }
+ },
+ onHashchange = function() {
+ can.trigger(window, 'hashchange');
+ };
+
+ addEvent(window, 'hashchange', onHashchange);
+ }();
+ })();
+
+ // ## util/dojo/dojo.js
+ var __m3 = (function(can) {
+ define("plugd/trigger", ["dojo"], function(dojo) {
+
+ var d = dojo,
+ isfn = d.isFunction,
+ leaveRe = /mouse(enter|leave)/,
+ _fix = function(_, p) {
+ return "mouse" + (p == "enter" ? "over" : "out");
+ },
+ mix = d._mixin,
+
+ // the guts of the node triggering logic:
+ // the function accepts node (not string|node), "on"-less event name,
+ // and an object of args to mix into the event.
+ realTrigger = d.doc.createEvent ? function(n, e, a) {
+ // the sane branch
+ var ev = d.doc.createEvent("HTMLEvents");
+ e = e.replace(leaveRe, _fix);
+ // destroyed events should not bubble
+ ev.initEvent(e, e === "destroyed" ? false : true, true);
+ a && mix(ev, a);
+ n.dispatchEvent(ev);
+ } : function(n, e, a) {
+ // the janktastic branch
+ var ev = "on" + e,
+ stop = false,
+ lc = e.toLowerCase(),
+ node = n;
+ try {
+ // FIXME: is this worth it? for mixed-case native event support:? Opera ends up in the
+ // createEvent path above, and also fails on _some_ native-named events.
+ // if(lc !== e && d.indexOf(d.NodeList.events, lc) >= 0){
+ // // if the event is one of those listed in our NodeList list
+ // // in lowercase form but is mixed case, throw to avoid
+ // // fireEvent. /me sighs. http://gist.github.com/315318
+ // throw("janktastic");
+ // }
+ n.fireEvent(ev);
+ } catch (er) {
+ // a lame duck to work with. we're probably a 'custom event'
+ var evdata = mix({
+ type: e,
+ target: n,
+ faux: true,
+ // HACK: [needs] added support for customStopper to _base/event.js
+ // some tests will fail until del._stopPropagation has support.
+ _stopper: function() {
+ stop = this.cancelBubble;
+ }
+ }, a);
+
+ isfn(n[ev]) && n[ev](evdata);
+
+ // handle bubbling of custom events, unless the event was stopped.
+ while (!stop && n !== d.doc && n.parentNode) {
+ n = n.parentNode;
+ isfn(n[ev]) && n[ev](evdata);
+ }
+ }
+ };
+
+ d._trigger = function(node, event, extraArgs) {
+ // summary:
+ // Helper for `dojo.trigger`, which handles the DOM cases. We should never
+ // be here without a domNode reference and a string eventname.
+ var n = d.byId(node),
+ ev = event && event.slice(0, 2) == "on" ? event.slice(2) : event;
+ realTrigger(n, ev, extraArgs);
+ };
+
+ d.trigger = function(obj, event, extraArgs) {
+ // summary:
+ // Trigger some event. It can be either a Dom Event, Custom Event,
+ // or direct function call.
+ // description:
+ // Trigger some event. It can be either a Dom Event, Custom Event,
+ // or direct function call. NOTE: This function does not trigger
+ // default behavior, only triggers bound event listeneres. eg:
+ // one cannot trigger("anchorNode", "onclick") and expect the browser
+ // to follow the href="" attribute naturally.
+ // obj: String|DomNode|Object|Function
+ // An ID, or DomNode reference, from which to trigger the event.
+ // If an Object, fire the `event` in the scope of this object,
+ // similar to calling dojo.hitch(obj, event)(). The return value
+ // in this case is returned from `dojo.trigger`
+ // event: String|Function
+ // The name of the event to trigger. can be any DOM level 2 event
+ // and can be in either form: "onclick" or "click" for instance.
+ // In the object-firing case, this method can be a function or
+ // a string version of a member function, just like `dojo.hitch`.
+ // extraArgs: Object?
+ // An object to mix into the `event` object passed to any bound
+ // listeners. Be careful not to override important members, like
+ // `type`, or `preventDefault`. It will likely error.
+ // Additionally, extraArgs is moot in the object-triggering case,
+ // as all arguments beyond the `event` are curried onto the triggered
+ // function.
+ // example:
+ // | dojo.connect(node, "onclick", function(e){ });
+ // | // later:
+ // | dojo.trigger(node, "onclick");
+ // example:
+ // | // or from within dojo.query: (requires dojo.NodeList)
+ // | dojo.query("a").onclick(function(){}).trigger("onclick");
+ // example:
+ // | // fire obj.method() in scope of obj
+ // | dojo.trigger(obj, "method");
+ // example:
+ // | // fire an anonymous function:
+ // | dojo.trigger(d.global, function(){ });
+ // example:
+ // | // fire and anonymous function in the scope of obj
+ // | dojo.trigger(obj, function(){ this == obj; });
+ // example:
+ // | // with a connected function like:
+ // | dojo.connect(dojo.doc, "onclick", function(e){
+ // | if(e && e.manuallydone){
+ // | console.log("this was a triggered onclick, not natural");
+ // | }
+ // | });
+ // | // fire onclick, passing in a custom bit of info
+ // | dojo.trigger("someId", "onclick", { manuallydone:true });
+ // returns: Anything
+ // Will not return anything in the Dom event case, but will return whatever
+ // return value is received from the triggered event.
+ return (isfn(obj) || isfn(event) || isfn(obj[event])) ? d.hitch.apply(d, arguments)() : d._trigger.apply(d, arguments);
+ };
+ d.NodeList.prototype.trigger = d.NodeList._adaptAsForEach(d._trigger);
+
+ // if the node.js module is available, extend trigger into that.
+ if (d._Node && !d._Node.prototype.trigger) {
+ d.extend(d._Node, {
+ trigger: function(ev, data) {
+ // summary:
+ // Fire some some event originating from this node.
+ // Only available if both the `dojo.trigger` and `dojo.node` plugin
+ // are enabled. Allows chaining as all `dojo._Node` methods do.
+ // ev: String
+ // Some string event name to fire. eg: "onclick", "submit"
+ // data: Object
+ // Just like `extraArgs` for `dojo.trigger`, additional data
+ // to mix into the event object.
+ // example:
+ // | // fire onlick orginiating from a node with id="someAnchorId"
+ // | dojo.node("someAnchorId").trigger("click");
+ d._trigger(this, ev, data);
+ return this; // dojo._Node
+ }
+ });
+ }
+
+ return d.trigger;
+
+ });
+
+ // dojo.js
+ // ---------
+ // _dojo node list._
+ // These are pre-loaded by `steal` -> no callback.
+ require(["dojo", "dojo/query", "plugd/trigger", "dojo/NodeList-dom"]);
+
+ // Map string helpers.
+ can.trim = function(s) {
+ return s && dojo.trim(s);
+ }
+
+ // Map array helpers.
+ can.makeArray = function(arr) {
+ var array = [];
+ dojo.forEach(arr, function(item) {
+ array.push(item)
+ });
+ return array;
+ };
+ can.isArray = dojo.isArray;
+ can.inArray = function(item, arr) {
+ return dojo.indexOf(arr, item);
+ };
+ can.map = function(arr, fn) {
+ return dojo.map(can.makeArray(arr || []), fn);
+ };
+ // Map object helpers.
+ can.extend = function(first) {
+ if (first === true) {
+ var args = can.makeArray(arguments);
+ args.shift();
+ return dojo.mixin.apply(dojo, args)
+ }
+ return dojo.mixin.apply(dojo, arguments)
+ }
+ can.isEmptyObject = function(object) {
+ var prop;
+ for (prop in object) {
+ break;
+ }
+ return prop === undefined
+ }
+
+ // Use a version of param similar to jQuery's param that
+ // handles nested data instead of dojo.objectToQuery which doesn't
+ can.param = function(object) {
+ var pairs = [],
+ add = function(key, value) {
+ pairs.push(encodeURIComponent(key) + "=" + encodeURIComponent(value))
+ };
+
+ for (var name in object) {
+ can.buildParam(name, object[name], add);
+ }
+ return pairs.join("&").replace(/%20/g, "+");
+ }
+ can.buildParam = function(prefix, obj, add) {
+ if (can.isArray(obj)) {
+ for (var i = 0, l = obj.length; i < l; ++i) {
+ add(prefix + "[]", obj[i])
+ }
+ } else if (dojo.isObject(obj)) {
+ for (var name in obj) {
+ can.buildParam(prefix + "[" + name + "]", obj[name], add);
+ }
+ } else {
+ add(prefix, obj);
+ }
+ }
+
+ // Map function helpers.
+ can.proxy = function(func, context) {
+ return dojo.hitch(context, func)
+ }
+ can.isFunction = function(f) {
+ return dojo.isFunction(f);
+ }
+
+
+ // The id of the `function` to be bound, used as an expando on the `function`
+ // so we can lookup it's `remove` object.
+ var dojoId = 0,
+ // Takes a node list, goes through each node
+ // and adds events data that has a map of events to
+ // callbackId to `remove` object. It looks like
+ // `{click: {5: {remove: fn}}}`.
+ dojoAddBinding = function(nodelist, ev, cb) {
+ nodelist.forEach(function(node) {
+ // Converting a raw select node to a node list
+ // returns a node list of its options due to a
+ // bug in Dojo 1.7.1, this is sovled by wrapping
+ // it in an array.
+ node = new dojo.NodeList(node.nodeName === "SELECT" ? [node] : node)
+ var events = can.data(node, "events");
+ if (!events) {
+ can.data(node, "events", events = {})
+ }
+ if (!events[ev]) {
+ events[ev] = {};
+ }
+ if (cb.__bindingsIds === undefined) {
+ cb.__bindingsIds = dojoId++;
+ }
+ events[ev][cb.__bindingsIds] = node.on(ev, cb)[0]
+ });
+ },
+ // Removes a binding on a `nodelist` by finding
+ // the remove object within the object's data.
+ dojoRemoveBinding = function(nodelist, ev, cb) {
+ nodelist.forEach(function(node) {
+ var node = new dojo.NodeList(node),
+ events = can.data(node, "events"),
+ handlers = events[ev],
+ handler = handlers[cb.__bindingsIds];
+
+ dojo.disconnect(handler);
+ delete handlers[cb.__bindingsIds];
+
+ if (can.isEmptyObject(handlers)) {
+ delete events[ev]
+ }
+ });
+ }
+
+ can.bind = function(ev, cb) {
+ // If we can bind to it...
+ if (this.bind && this.bind !== can.bind) {
+ this.bind(ev, cb)
+
+ // Otherwise it's an element or `nodeList`.
+ } else if (this.on || this.nodeType) {
+ // Converting a raw select node to a node list
+ // returns a node list of its options due to a
+ // bug in Dojo 1.7.1, this is sovled by wrapping
+ // it in an array.
+ dojoAddBinding(new dojo.NodeList(this.nodeName === "SELECT" ? [this] : this), ev, cb)
+ } else if (this.addEvent) {
+ this.addEvent(ev, cb)
+ } else {
+ // Make it bind-able...
+ can.addEvent.call(this, ev, cb)
+ }
+ return this;
+ }
+ can.unbind = function(ev, cb) {
+ // If we can bind to it...
+ if (this.unbind && this.unbind !== can.unbind) {
+ this.unbind(ev, cb)
+ } else if (this.on || this.nodeType) {
+ dojoRemoveBinding(new dojo.NodeList(this), ev, cb);
+ } else {
+ // Make it bind-able...
+ can.removeEvent.call(this, ev, cb)
+ }
+ return this;
+ }
+
+ can.trigger = function(item, event, args, bubble) {
+ if ((!(item instanceof dojo.NodeList)) && (item.nodeName || item === window)) {
+ item = can.$(item);
+ }
+
+ if (item.trigger) {
+ if (bubble === false) {
+ if (!item[0] || item[0].nodeType === 3) {
+ return;
+ }
+ // Force stop propagation by
+ // listening to `on` and then immediately disconnecting.
+ var connect = item.on(event, function(ev) {
+
+ ev.stopPropagation && ev.stopPropagation();
+ ev.cancelBubble = true;
+ ev._stopper && ev._stopper();
+
+ dojo.disconnect(connect);
+ })
+ item.trigger(event, args)
+ } else {
+ item.trigger(event, args)
+ }
+
+ } else {
+ if (typeof event === 'string') {
+ event = {
+ type: event
+ }
+ }
+ event.data = args
+ event.target = event.target || item;
+ can.dispatch.call(item, event)
+ }
+ }
+
+ can.delegate = function(selector, ev, cb) {
+ if (this.on || this.nodeType) {
+ dojoAddBinding(new dojo.NodeList(this), selector + ":" + ev, cb)
+ } else if (this.delegate) {
+ this.delegate(selector, ev, cb)
+ }
+ return this;
+ }
+ can.undelegate = function(selector, ev, cb) {
+ if (this.on || this.nodeType) {
+ dojoRemoveBinding(new dojo.NodeList(this), selector + ":" + ev, cb);
+ } else if (this.undelegate) {
+ this.undelegate(selector, ev, cb)
+ }
+
+ return this;
+ }
+
+
+ var optionsMap = {
+ type: "method",
+ success: undefined,
+ error: undefined
+ }
+ var updateDeferred = function(xhr, d) {
+ for (var prop in xhr) {
+ if (typeof d[prop] == 'function') {
+ d[prop] = function() {
+ xhr[prop].apply(xhr, arguments)
+ }
+ } else {
+ d[prop] = prop[xhr]
+ }
+ }
+ }
+
+ can.ajax = function(options) {
+ var type = can.capitalize((options.type || "get").toLowerCase()),
+ method = dojo["xhr" + type];
+ var success = options.success,
+ error = options.error,
+ d = new can.Deferred();
+
+ var def = method({
+ url: options.url,
+ handleAs: options.dataType,
+ sync: !options.async,
+ headers: options.headers,
+ content: options.data
+ })
+ def.then(function(data, ioargs) {
+ updateDeferred(xhr, d);
+ d.resolve(data, "success", xhr);
+ success && success(data, "success", xhr);
+ }, function(data, ioargs) {
+ updateDeferred(xhr, d);
+ d.reject(xhr, "error");
+ error(xhr, "error");
+ })
+
+ var xhr = def.ioArgs.xhr;
+
+ updateDeferred(xhr, d);
+ return d;
+
+ }
+ // Element - get the wrapped helper.
+ can.$ = function(selector) {
+ if (selector === window) {
+ return window;
+ }
+ if (typeof selector === "string") {
+ return dojo.query(selector)
+ } else {
+ return new dojo.NodeList(selector);
+ }
+ }
+
+ can.append = function(wrapped, html) {
+ return wrapped.forEach(function(node) {
+ dojo.place(html, node)
+ });
+ }
+
+
+ var data = {},
+ uuid = can.uuid = +new Date(),
+ exp = can.expando = 'can' + uuid;
+
+ function getData(node, name) {
+ var id = node[exp],
+ store = id && data[id];
+ return name === undefined ? store || setData(node) : (store && store[name]);
+ }
+
+ function setData(node, name, value) {
+ var id = node[exp] || (node[exp] = ++uuid),
+ store = data[id] || (data[id] = {});
+ if (name !== undefined) store[name] = value;
+ return store;
+ };
+
+ var cleanData = function(elems) {
+ can.trigger(new dojo.NodeList(elems), "destroyed", [], false)
+ for (var i = 0, elem;
+ (elem = elems[i]) !== undefined; i++) {
+ var id = elem[exp]
+ delete data[id];
+ }
+ };
+
+ can.data = function(wrapped, name, value) {
+ return value === undefined ? wrapped.length == 0 ? undefined : getData(wrapped[0], name) : wrapped.forEach(function(node) {
+ setData(node, name, value);
+ });
+ };
+
+ // Overwrite `dojo.destroy`, `dojo.empty` and `dojo.place`.
+ var empty = dojo.empty;
+ dojo.empty = function() {
+ for (var c; c = node.lastChild;) { // Intentional assignment.
+ dojo.destroy(c);
+ }
+ }
+
+ var destroy = dojo.destroy;
+ dojo.destroy = function(node) {
+ node = dojo.byId(node);
+ cleanData([node]);
+ node.getElementsByTagName && cleanData(node.getElementsByTagName('*'))
+
+ return destroy.apply(dojo, arguments);
+ };
+
+ can.addClass = function(wrapped, className) {
+ return wrapped.addClass(className);
+ }
+
+ can.remove = function(wrapped) {
+ // We need to remove text nodes ourselves.
+ wrapped.forEach(dojo.destroy);
+ }
+
+ can.get = function(wrapped, index) {
+ return wrapped[index];
+ }
+
+ // Add pipe to `dojo.Deferred`.
+ can.extend(dojo.Deferred.prototype, {
+ pipe: function(done, fail) {
+ var d = new dojo.Deferred();
+ this.addCallback(function() {
+ d.resolve(done.apply(this, arguments));
+ });
+
+ this.addErrback(function() {
+ if (fail) {
+ d.reject(fail.apply(this, arguments));
+ } else {
+ d.reject.apply(d, arguments);
+ }
+ });
+ return d;
+ }
+ });
+
+ return can;
+ })(__m4, {}, __m6, __m7, __m8, __m9, __m10, __m11);
+
+ // ## util/string/string.js
+ var __m2 = (function(can) {
+ // ##string.js
+ // _Miscellaneous string utility functions._
+
+ // Several of the methods in this plugin use code adapated from Prototype
+ // Prototype JavaScript framework, version 1.6.0.1.
+ // © 2005-2007 Sam Stephenson
+ var strUndHash = /_|-/,
+ strColons = /\=\=/,
+ strWords = /([A-Z]+)([A-Z][a-z])/g,
+ strLowUp = /([a-z\d])([A-Z])/g,
+ strDash = /([a-z\d])([A-Z])/g,
+ strReplacer = /\{([^\}]+)\}/g,
+ strQuote = /"/g,
+ strSingleQuote = /'/g,
+
+ // Returns the `prop` property from `obj`.
+ // If `add` is true and `prop` doesn't exist in `obj`, create it as an
+ // empty object.
+ getNext = function(obj, prop, add) {
+ var result = obj[prop];
+
+ if (result === undefined && add === true) {
+ result = obj[prop] = {}
+ }
+ return result
+ },
+
+ // Returns `true` if the object can have properties (no `null`s).
+ isContainer = function(current) {
+ return (/^f|^o/).test(typeof current);
+ };
+
+ can.extend(can, {
+ // Escapes strings for HTML.
+
+ esc: function(content) {
+ // Convert bad values into empty strings
+ var isInvalid = content === null || content === undefined || (isNaN(content) && ("" + content === 'NaN'));
+ return ("" + (isInvalid ? '' : content))
+ .replace(/&/g, '&')
+ .replace(/</g, '<')
+ .replace(/>/g, '>')
+ .replace(strQuote, '"')
+ .replace(strSingleQuote, "'");
+ },
+
+
+ getObject: function(name, roots, add) {
+
+ // The parts of the name we are looking up
+ // `['App','Models','Recipe']`
+ var parts = name ? name.split('.') : [],
+ length = parts.length,
+ current,
+ r = 0,
+ i, container, rootsLength;
+
+ // Make sure roots is an `array`.
+ roots = can.isArray(roots) ? roots : [roots || window];
+
+ rootsLength = roots.length
+
+ if (!length) {
+ return roots[0];
+ }
+
+ // For each root, mark it as current.
+ for (r; r < rootsLength; r++) {
+ current = roots[r];
+ container = undefined;
+
+ // Walk current to the 2nd to last object or until there
+ // is not a container.
+ for (i = 0; i < length && isContainer(current); i++) {
+ container = current;
+ current = getNext(container, parts[i]);
+ }
+
+ // If we found property break cycle
+ if (container !== undefined && current !== undefined) {
+ break
+ }
+ }
+
+ // Remove property from found container
+ if (add === false && current !== undefined) {
+ delete container[parts[i - 1]]
+ }
+
+ // When adding property add it to the first root
+ if (add === true && current === undefined) {
+ current = roots[0]
+
+ for (i = 0; i < length && isContainer(current); i++) {
+ current = getNext(current, parts[i], true);
+ }
+ }
+
+ return current;
+ },
+ // Capitalizes a string.
+
+ capitalize: function(s, cache) {
+ // Used to make newId.
+ return s.charAt(0).toUpperCase() + s.slice(1);
+ },
+
+ // Underscores a string.
+
+ underscore: function(s) {
+ return s
+ .replace(strColons, '/')
+ .replace(strWords, '$1_$2')
+ .replace(strLowUp, '$1_$2')
+ .replace(strDash, '_')
+ .toLowerCase();
+ },
+ // Micro-templating.
+
+ sub: function(str, data, remove) {
+ var obs = [];
+
+ str = str || '';
+
+ obs.push(str.replace(strReplacer, function(whole, inside) {
+
+ // Convert inside to type.
+ var ob = can.getObject(inside, data, remove === true ? false : undefined);
+
+ if (ob === undefined) {
+ obs = null;
+ return "";
+ }
+
+ // If a container, push into objs (which will return objects found).
+ if (isContainer(ob) && obs) {
+ obs.push(ob);
+ return "";
+ }
+
+ return "" + ob;
+ }));
+
+ return obs === null ? obs : (obs.length <= 1 ? obs[0] : obs);
+ },
+
+ // These regex's are used throughout the rest of can, so let's make
+ // them available.
+ replacer: strReplacer,
+ undHash: strUndHash
+ });
+ return can;
+ })(__m3);
+
+ // ## construct/construct.js
+ var __m1 = (function(can) {
+
+ // ## construct.js
+ // `can.Construct`
+ // _This is a modified version of
+ // [John Resig's class](http://ejohn.org/blog/simple-javascript-inheritance/).
+ // It provides class level inheritance and callbacks._
+
+ // A private flag used to initialize a new class instance without
+ // initializing it's bindings.
+ var initializing = 0;
+
+
+ can.Construct = function() {
+ if (arguments.length) {
+ return can.Construct.extend.apply(can.Construct, arguments);
+ }
+ };
+
+
+ can.extend(can.Construct, {
+
+ newInstance: function() {
+ // Get a raw instance object (`init` is not called).
+ var inst = this.instance(),
+ arg = arguments,
+ args;
+
+ // Call `setup` if there is a `setup`
+ if (inst.setup) {
+ args = inst.setup.apply(inst, arguments);
+ }
+
+ // Call `init` if there is an `init`
+ // If `setup` returned `args`, use those as the arguments
+ if (inst.init) {
+ inst.init.apply(inst, args || arguments);
+ }
+
+ return inst;
+ },
+ // Overwrites an object with methods. Used in the `super` plugin.
+ // `newProps` - New properties to add.
+ // `oldProps` - Where the old properties might be (used with `super`).
+ // `addTo` - What we are adding to.
+ _inherit: function(newProps, oldProps, addTo) {
+ can.extend(addTo || newProps, newProps || {})
+ },
+ // used for overwriting a single property.
+ // this should be used for patching other objects
+ // the super plugin overwrites this
+ _overwrite: function(what, oldProps, propName, val) {
+ what[propName] = val;
+ },
+ // Set `defaults` as the merger of the parent `defaults` and this
+ // object's `defaults`. If you overwrite this method, make sure to
+ // include option merging logic.
+
+ setup: function(base, fullName) {
+ this.defaults = can.extend(true, {}, base.defaults, this.defaults);
+ },
+ // Create's a new `class` instance without initializing by setting the
+ // `initializing` flag.
+ instance: function() {
+
+ // Prevents running `init`.
+ initializing = 1;
+
+ var inst = new this();
+
+ // Allow running `init`.
+ initializing = 0;
+
+ return inst;
+ },
+ // Extends classes.
+
+ extend: function(fullName, klass, proto) {
+ // Figure out what was passed and normalize it.
+ if (typeof fullName != 'string') {
+ proto = klass;
+ klass = fullName;
+ fullName = null;
+ }
+
+ if (!proto) {
+ proto = klass;
+ klass = null;
+ }
+ proto = proto || {};
+
+ var _super_class = this,
+ _super = this.prototype,
+ name, shortName, namespace, prototype;
+
+ // Instantiate a base class (but only create the instance,
+ // don't run the init constructor).
+ prototype = this.instance();
+
+ // Copy the properties over onto the new prototype.
+ can.Construct._inherit(proto, _super, prototype);
+
+ // The dummy class constructor.
+
+ function Constructor() {
+ // All construction is actually done in the init method.
+ if (!initializing) {
+ return this.constructor !== Constructor && arguments.length ?
+ // We are being called without `new` or we are extending.
+ arguments.callee.extend.apply(arguments.callee, arguments) :
+ // We are being called with `new`.
+ this.constructor.newInstance.apply(this.constructor, arguments);
+ }
+ }
+
+ // Copy old stuff onto class (can probably be merged w/ inherit)
+ for (name in _super_class) {
+ if (_super_class.hasOwnProperty(name)) {
+ Constructor[name] = _super_class[name];
+ }
+ }
+
+ // Copy new static properties on class.
+ can.Construct._inherit(klass, _super_class, Constructor);
+
+ // Setup namespaces.
+ if (fullName) {
+
+ var parts = fullName.split('.'),
+ shortName = parts.pop(),
+ current = can.getObject(parts.join('.'), window, true),
+ namespace = current,
+ _fullName = can.underscore(fullName.replace(/\./g, "_")),
+ _shortName = can.underscore(shortName);
+
+
+
+ current[shortName] = Constructor;
+ }
+
+ // Set things that shouldn't be overwritten.
+ can.extend(Constructor, {
+ constructor: Constructor,
+ prototype: prototype,
+
+ namespace: namespace,
+
+ _shortName: _shortName,
+
+ fullName: fullName,
+ _fullName: _fullName
+ });
+
+ // Dojo and YUI extend undefined
+ if (shortName !== undefined) {
+ Constructor.shortName = shortName;
+ }
+
+ // Make sure our prototype looks nice.
+ Constructor.prototype.constructor = Constructor;
+
+
+ // Call the class `setup` and `init`
+ var t = [_super_class].concat(can.makeArray(arguments)),
+ args = Constructor.setup.apply(Constructor, t);
+
+ if (Constructor.init) {
+ Constructor.init.apply(Constructor, args || t);
+ }
+
+
+ return Constructor;
+
+ }
+
+ });
+ return can.Construct;
+ })(__m2);
+
+ // ## util/bind/bind.js
+ var __m13 = (function(can) {
+
+
+ // ## Bind helpers
+ can.bindAndSetup = function() {
+ // Add the event to this object
+ can.addEvent.apply(this, arguments);
+ // If not initializing, and the first binding
+ // call bindsetup if the function exists.
+ if (!this._init) {
+ if (!this._bindings) {
+ this._bindings = 1;
+ // setup live-binding
+ this._bindsetup && this._bindsetup();
+
+ } else {
+ this._bindings++;
+ }
+
+ }
+
+ return this;
+ };
+
+ can.unbindAndTeardown = function(ev, handler) {
+ // Remove the event handler
+ can.removeEvent.apply(this, arguments);
+
+ this._bindings--;
+ // If there are no longer any bindings and
+ // there is a bindteardown method, call it.
+ if (!this._bindings) {
+ this._bindteardown && this._bindteardown();
+ }
+ return this;
+ }
+
+ return can;
+
+ })(__m3);
+
+ // ## observe/observe.js
+ var __m12 = (function(can, bind) {
+ // ## observe.js
+ // `can.Observe`
+ // _Provides the observable pattern for JavaScript Objects._
+ // Returns `true` if something is an object with properties of its own.
+ var canMakeObserve = function(obj) {
+ return obj && !can.isDeferred(obj) && (can.isArray(obj) || can.isPlainObject(obj) || (obj instanceof can.Observe));
+ },
+
+ // Removes all listeners.
+ unhookup = function(items, namespace) {
+ return can.each(items, function(item) {
+ if (item && item.unbind) {
+ item.unbind("change" + namespace);
+ }
+ });
+ },
+ // Listens to changes on `child` and "bubbles" the event up.
+ // `child` - The object to listen for changes on.
+ // `prop` - The property name is at on.
+ // `parent` - The parent object of prop.
+ // `ob` - (optional) The Observe object constructor
+ // `list` - (optional) The observable list constructor
+ hookupBubble = function(child, prop, parent, Ob, List) {
+ Ob = Ob || Observe;
+ List = List || Observe.List;
+
+ // If it's an `array` make a list, otherwise a child.
+ if (child instanceof Observe) {
+ // We have an `observe` already...
+ // Make sure it is not listening to this already
+ // It's only listening if it has bindings already.
+ parent._bindings && unhookup([child], parent._cid);
+ } else if (can.isArray(child)) {
+ child = new List(child);
+ } else {
+ child = new Ob(child);
+ }
+ // only listen if something is listening to you
+ if (parent._bindings) {
+ // Listen to all changes and `batchTrigger` upwards.
+ bindToChildAndBubbleToParent(child, prop, parent)
+ }
+
+
+ return child;
+ },
+ bindToChildAndBubbleToParent = function(child, prop, parent) {
+ child.bind("change" + parent._cid, function() {
+ // `batchTrigger` the type on this...
+ var args = can.makeArray(arguments),
+ ev = args.shift();
+ args[0] = (prop === "*" ? [parent.indexOf(child), args[0]] : [prop, args[0]]).join(".");
+
+ // track objects dispatched on this observe
+ ev.triggeredNS = ev.triggeredNS || {};
+
+ // if it has already been dispatched exit
+ if (ev.triggeredNS[parent._cid]) {
+ return;
+ }
+
+ ev.triggeredNS[parent._cid] = true;
+ // send change event with modified attr to parent
+ can.trigger(parent, ev, args);
+ // send modified attr event to parent
+ //can.trigger(parent, args[0], args);
+ });
+ }
+ // An `id` to track events for a given observe.
+ observeId = 0,
+ // A helper used to serialize an `Observe` or `Observe.List`.
+ // `observe` - The observable.
+ // `how` - To serialize with `attr` or `serialize`.
+ // `where` - To put properties, in an `{}` or `[]`.
+ serialize = function(observe, how, where) {
+ // Go through each property.
+ observe.each(function(val, name) {
+ // If the value is an `object`, and has an `attrs` or `serialize` function.
+ where[name] = canMakeObserve(val) && can.isFunction(val[how]) ?
+ // Call `attrs` or `serialize` to get the original data back.
+ val[how]() :
+ // Otherwise return the value.
+ val;
+ });
+ return where;
+ },
+ attrParts = function(attr, keepKey) {
+ if (keepKey) {
+ return [attr];
+ }
+ return can.isArray(attr) ? attr : ("" + attr).split(".");
+ },
+ // Which batch of events this is for -- might not want to send multiple
+ // messages on the same batch. This is mostly for event delegation.
+ batchNum = 1,
+ // how many times has start been called without a stop
+ transactions = 0,
+ // an array of events within a transaction
+ batchEvents = [],
+ stopCallbacks = [],
+ makeBindSetup = function(wildcard) {
+ return function() {
+ var parent = this;
+ this._each(function(child, prop) {
+ if (child && child.bind) {
+ bindToChildAndBubbleToParent(child, wildcard || prop, parent)
+ }
+ })
+ };
+ };
+
+
+ var Observe = can.Map = can.Observe = can.Construct({
+
+ // keep so it can be overwritten
+ bind: can.bindAndSetup,
+ unbind: can.unbindAndTeardown,
+ id: "id",
+ canMakeObserve: canMakeObserve,
+ // starts collecting events
+ // takes a callback for after they are updated
+ // how could you hook into after ejs
+
+ startBatch: function(batchStopHandler) {
+ transactions++;
+ batchStopHandler && stopCallbacks.push(batchStopHandler);
+ },
+
+ stopBatch: function(force, callStart) {
+ if (force) {
+ transactions = 0;
+ } else {
+ transactions--;
+ }
+
+ if (transactions == 0) {
+ var items = batchEvents.slice(0),
+ callbacks = stopCallbacks.slice(0);
+ batchEvents = [];
+ stopCallbacks = [];
+ batchNum++;
+ callStart && this.startBatch();
+ can.each(items, function(args) {
+ can.trigger.apply(can, args);
+ });
+ can.each(callbacks, function(cb) {
+ cb();
+ });
+ }
+ },
+
+ triggerBatch: function(item, event, args) {
+ // Don't send events if initalizing.
+ if (!item._init) {
+ if (transactions == 0) {
+ return can.trigger(item, event, args);
+ } else {
+ event = typeof event === "string" ? {
+ type: event
+ } :
+ event;
+ event.batchNum = batchNum;
+ batchEvents.push([
+ item,
+ event,
+ args
+ ]);
+ }
+ }
+ },
+
+ keys: function(observe) {
+ var keys = [];
+ Observe.__reading && Observe.__reading(observe, '__keys');
+ for (var keyName in observe._data) {
+ keys.push(keyName);
+ }
+ return keys;
+ }
+ },
+
+ {
+ setup: function(obj) {
+ // `_data` is where we keep the properties.
+ this._data = {};
+
+ // The namespace this `object` uses to listen to events.
+ can.cid(this, ".observe");
+ // Sets all `attrs`.
+ this._init = 1;
+ this.attr(obj);
+ this.bind('change' + this._cid, can.proxy(this._changes, this));
+ delete this._init;
+ },
+ _bindsetup: makeBindSetup(),
+ _bindteardown: function() {
+ var cid = this._cid;
+ this._each(function(child) {
+ unhookup([child], cid)
+ })
+ },
+ _changes: function(ev, attr, how, newVal, oldVal) {
+ Observe.triggerBatch(this, {
+ type: attr,
+ batchNum: ev.batchNum
+ }, [newVal, oldVal]);
+ },
+ _triggerChange: function(attr, how, newVal, oldVal) {
+ Observe.triggerBatch(this, "change", can.makeArray(arguments))
+ },
+ // no live binding iterator
+ _each: function(callback) {
+ var data = this.__get();
+ for (var prop in data) {
+ if (data.hasOwnProperty(prop)) {
+ callback(data[prop], prop)
+ }
+ }
+ },
+
+ attr: function(attr, val) {
+ // This is super obfuscated for space -- basically, we're checking
+ // if the type of the attribute is not a `number` or a `string`.
+ var type = typeof attr;
+ if (type !== "string" && type !== "number") {
+ return this._attrs(attr, val)
+ } else if (val === undefined) { // If we are getting a value.
+ // Let people know we are reading.
+ Observe.__reading && Observe.__reading(this, attr)
+ return this._get(attr)
+ } else {
+ // Otherwise we are setting.
+ this._set(attr, val);
+ return this;
+ }
+ },
+
+ each: function() {
+ Observe.__reading && Observe.__reading(this, '__keys');
+ return can.each.apply(undefined, [this.__get()].concat(can.makeArray(arguments)))
+ },
+
+ removeAttr: function(attr) {
+ // Info if this is List or not
+ var isList = this instanceof can.Observe.List,
+ // Convert the `attr` into parts (if nested).
+ parts = attrParts(attr),
+ // The actual property to remove.
+ prop = parts.shift(),
+ // The current value.
+ current = isList ? this[prop] : this._data[prop];
+
+ // If we have more parts, call `removeAttr` on that part.
+ if (parts.length) {
+ return current.removeAttr(parts)
+ } else {
+ if (isList) {
+ this.splice(prop, 1)
+ } else if (prop in this._data) {
+ // Otherwise, `delete`.
+ delete this._data[prop];
+ // Create the event.
+ if (!(prop in this.constructor.prototype)) {
+ delete this[prop]
+ }
+ // Let others know the number of keys have changed
+ Observe.triggerBatch(this, "__keys");
+ this._triggerChange(prop, "remove", undefined, current);
+
+ }
+ return current;
+ }
+ },
+ // Reads a property from the `object`.
+ _get: function(attr) {
+ var value = typeof attr === 'string' && !! ~attr.indexOf('.') && this.__get(attr);
+ if (value) {
+ return value;
+ }
+
+ // break up the attr (`"foo.bar"`) into `["foo","bar"]`
+ var parts = attrParts(attr),
+ // get the value of the first attr name (`"foo"`)
+ current = this.__get(parts.shift());
+ // if there are other attributes to read
+ return parts.length ?
+ // and current has a value
+ current ?
+ // lookup the remaining attrs on current
+ current._get(parts) :
+ // or if there's no current, return undefined
+ undefined :
+ // if there are no more parts, return current
+ current;
+ },
+ // Reads a property directly if an `attr` is provided, otherwise
+ // returns the "real" data object itself.
+ __get: function(attr) {
+ return attr ? this._data[attr] : this._data;
+ },
+ // Sets `attr` prop as value on this object where.
+ // `attr` - Is a string of properties or an array of property values.
+ // `value` - The raw value to set.
+ _set: function(attr, value, keepKey) {
+ // Convert `attr` to attr parts (if it isn't already).
+ var parts = attrParts(attr, keepKey),
+ // The immediate prop we are setting.
+ prop = parts.shift(),
+ // The current value.
+ current = this.__get(prop);
+
+ // If we have an `object` and remaining parts.
+ if (canMakeObserve(current) && parts.length) {
+ // That `object` should set it (this might need to call attr).
+ current._set(parts, value)
+ } else if (!parts.length) {
+ // We're in "real" set territory.
+ if (this.__convert) {
+ value = this.__convert(prop, value)
+ }
+ this.__set(prop, value, current)
+ } else {
+ throw "can.Observe: Object does not exist"
+ }
+ },
+ __set: function(prop, value, current) {
+
+ // Otherwise, we are setting it on this `object`.
+ // TODO: Check if value is object and transform
+ // are we changing the value.
+ if (value !== current) {
+ // Check if we are adding this for the first time --
+ // if we are, we need to create an `add` event.
+ var changeType = this.__get().hasOwnProperty(prop) ? "set" : "add";
+
+ // Set the value on data.
+ this.___set(prop,
+
+ // If we are getting an object.
+ canMakeObserve(value) ?
+
+ // Hook it up to send event.
+ hookupBubble(value, prop, this) :
+ // Value is normal.
+ value);
+
+ if (changeType == "add") {
+ // If there is no current value, let others know that
+ // the the number of keys have changed
+
+ Observe.triggerBatch(this, "__keys", undefined);
+
+ }
+ // `batchTrigger` the change event.
+ this._triggerChange(prop, changeType, value, current);
+
+ //Observe.triggerBatch(this, prop, [value, current]);
+ // If we can stop listening to our old value, do it.
+ current && unhookup([current], this._cid);
+ }
+
+ },
+ // Directly sets a property on this `object`.
+ ___set: function(prop, val) {
+ this._data[prop] = val;
+ // Add property directly for easy writing.
+ // Check if its on the `prototype` so we don't overwrite methods like `attrs`.
+ if (!(prop in this.constructor.prototype)) {
+ this[prop] = val
+ }
+ },
+
+
+ bind: can.bindAndSetup,
+
+ unbind: can.unbindAndTeardown,
+
+ serialize: function() {
+ return serialize(this, 'serialize', {});
+ },
+
+ _attrs: function(props, remove) {
+
+ if (props === undefined) {
+ return serialize(this, 'attr', {})
+ }
+
+ props = can.extend({}, props);
+ var prop,
+ self = this,
+ newVal;
+ Observe.startBatch();
+ this.each(function(curVal, prop) {
+ newVal = props[prop];
+
+ // If we are merging...
+ if (newVal === undefined) {
+ remove && self.removeAttr(prop);
+ return;
+ }
+
+ if (self.__convert) {
+ newVal = self.__convert(prop, newVal)
+ }
+
+ // if we're dealing with models, want to call _set to let converter run
+ if (newVal instanceof can.Observe) {
+ self.__set(prop, newVal, curVal)
+ // if its an object, let attr merge
+ } else if (canMakeObserve(curVal) && canMakeObserve(newVal) && curVal.attr) {
+ curVal.attr(newVal, remove)
+ // otherwise just set
+ } else if (curVal != newVal) {
+ self.__set(prop, newVal, curVal)
+ }
+
+ delete props[prop];
+ })
+ // Add remaining props.
+ for (var prop in props) {
+ newVal = props[prop];
+ this._set(prop, newVal, true)
+ }
+ Observe.stopBatch()
+ return this;
+ },
+
+
+ compute: function(prop) {
+ return can.compute(this, prop);
+ }
+ });
+ // Helpers for `observable` lists.
+ var splice = [].splice,
+
+ list = Observe(
+
+ {
+ setup: function(instances, options) {
+ this.length = 0;
+ can.cid(this, ".observe")
+ this._init = 1;
+ if (can.isDeferred(instances)) {
+ this.replace(instances)
+ } else {
+ this.push.apply(this, can.makeArray(instances || []));
+ }
+ // this change needs to be ignored
+ this.bind('change' + this._cid, can.proxy(this._changes, this));
+ can.extend(this, options);
+ delete this._init;
+ },
+ _triggerChange: function(attr, how, newVal, oldVal) {
+
+ Observe.prototype._triggerChange.apply(this, arguments)
+ // `batchTrigger` direct add and remove events...
+ if (!~attr.indexOf('.')) {
+
+ if (how === 'add') {
+ Observe.triggerBatch(this, how, [newVal, +attr]);
+ Observe.triggerBatch(this, 'length', [this.length]);
+ } else if (how === 'remove') {
+ Observe.triggerBatch(this, how, [oldVal, +attr]);
+ Observe.triggerBatch(this, 'length', [this.length]);
+ } else {
+ Observe.triggerBatch(this, how, [newVal, +attr])
+ }
+
+ }
+
+ },
+ __get: function(attr) {
+ return attr ? this[attr] : this;
+ },
+ ___set: function(attr, val) {
+ this[attr] = val;
+ if (+attr >= this.length) {
+ this.length = (+attr + 1)
+ }
+ },
+ _each: function(callback) {
+ var data = this.__get();
+ for (var i = 0; i < data.length; i++) {
+ callback(data[i], i)
+ }
+ },
+ _bindsetup: makeBindSetup("*"),
+ // Returns the serialized form of this list.
+
+ serialize: function() {
+ return serialize(this, 'serialize', []);
+ },
+
+ splice: function(index, howMany) {
+ var args = can.makeArray(arguments),
+ i;
+
+ for (i = 2; i < args.length; i++) {
+ var val = args[i];
+ if (canMakeObserve(val)) {
+ args[i] = hookupBubble(val, "*", this, this.constructor.Observe, this.constructor)
+ }
+ }
+ if (howMany === undefined) {
+ howMany = args[1] = this.length - index;
+ }
+ var removed = splice.apply(this, args);
+ can.Observe.startBatch();
+ if (howMany > 0) {
+ this._triggerChange("" + index, "remove", undefined, removed);
+ unhookup(removed, this._cid);
+ }
+ if (args.length > 2) {
+ this._triggerChange("" + index, "add", args.slice(2), removed);
+ }
+ can.Observe.stopBatch();
+ return removed;
+ },
+
+ _attrs: function(items, remove) {
+ if (items === undefined) {
+ return serialize(this, 'attr', []);
+ }
+
+ // Create a copy.
+ items = can.makeArray(items);
+
+ Observe.startBatch();
+ this._updateAttrs(items, remove);
+ Observe.stopBatch()
+ },
+
+ _updateAttrs: function(items, remove) {
+ var len = Math.min(items.length, this.length);
+
+ for (var prop = 0; prop < len; prop++) {
+ var curVal = this[prop],
+ newVal = items[prop];
+
+ if (canMakeObserve(curVal) && canMakeObserve(newVal)) {
+ curVal.attr(newVal, remove)
+ } else if (curVal != newVal) {
+ this._set(prop, newVal)
+ } else {
+
+ }
+ }
+ if (items.length > this.length) {
+ // Add in the remaining props.
+ this.push.apply(this, items.slice(this.length));
+ } else if (items.length < this.length && remove) {
+ this.splice(items.length)
+ }
+ }
+ }),
+
+ // Converts to an `array` of arguments.
+ getArgs = function(args) {
+ return args[0] && can.isArray(args[0]) ?
+ args[0] :
+ can.makeArray(args);
+ };
+ // Create `push`, `pop`, `shift`, and `unshift`
+ can.each({
+
+ push: "length",
+
+ unshift: 0
+ },
+ // Adds a method
+ // `name` - The method name.
+ // `where` - Where items in the `array` should be added.
+
+ function(where, name) {
+ var orig = [][name]
+ list.prototype[name] = function() {
+ // Get the items being added.
+ var args = [],
+ // Where we are going to add items.
+ len = where ? this.length : 0,
+ i = arguments.length,
+ res,
+ val,
+ constructor = this.constructor;
+
+ // Go through and convert anything to an `observe` that needs to be converted.
+ while (i--) {
+ val = arguments[i];
+ args[i] = canMakeObserve(val) ?
+ hookupBubble(val, "*", this, this.constructor.Observe, this.constructor) :
+ val;
+ }
+
+ // Call the original method.
+ res = orig.apply(this, args);
+
+ if (!this.comparator || args.length) {
+
+ this._triggerChange("" + len, "add", args, undefined);
+ }
+
+ return res;
+ }
+ });
+
+ can.each({
+
+ pop: "length",
+
+ shift: 0
+ },
+ // Creates a `remove` type method
+
+ function(where, name) {
+ list.prototype[name] = function() {
+
+ var args = getArgs(arguments),
+ len = where && this.length ? this.length - 1 : 0;
+
+ var res = [][name].apply(this, args)
+
+ // Create a change where the args are
+ // `len` - Where these items were removed.
+ // `remove` - Items removed.
+ // `undefined` - The new values (there are none).
+ // `res` - The old, removed values (should these be unbound).
+ this._triggerChange("" + len, "remove", undefined, [res])
+
+ if (res && res.unbind) {
+ res.unbind("change" + this._cid)
+ }
+ return res;
+ }
+ });
+
+ can.extend(list.prototype, {
+
+ indexOf: function(item) {
+ this.attr('length')
+ return can.inArray(item, this)
+ },
+
+
+ join: [].join,
+
+
+ reverse: [].reverse,
+
+
+ slice: function() {
+ var temp = Array.prototype.slice.apply(this, arguments);
+ return new this.constructor(temp);
+ },
+
+
+ concat: function() {
+ var args = [];
+ can.each(can.makeArray(arguments), function(arg, i) {
+ args[i] = arg instanceof can.Observe.List ? arg.serialize() : arg;
+ });
+ return new this.constructor(Array.prototype.concat.apply(this.serialize(), args));
+ },
+
+
+ forEach: function(cb, thisarg) {
+ can.each(this, cb, thisarg || this);
+ },
+
+
+ replace: function(newList) {
+ if (can.isDeferred(newList)) {
+ newList.then(can.proxy(this.replace, this));
+ } else {
+ this.splice.apply(this, [0, this.length].concat(can.makeArray(newList || [])));
+ }
+
+ return this;
+ }
+ });
+
+ can.List = Observe.List = list;
+ Observe.setup = function() {
+ can.Construct.setup.apply(this, arguments);
+ // I would prefer not to do it this way. It should
+ // be using the attributes plugin to do this type of conversion.
+ this.List = Observe.List({
+ Observe: this
+ }, {});
+ }
+ return Observe;
+ })(__m3, __m13, __m1);
+
+ // ## observe/compute/compute.js
+ var __m14 = (function(can, bind) {
+
+ // returns the
+ // - observes and attr methods are called by func
+ // - the value returned by func
+ // ex: `{value: 100, observed: [{obs: o, attr: "completed"}]}`
+ var getValueAndObserved = function(func, self) {
+
+ var oldReading;
+ if (can.Observe) {
+ // Set a callback on can.Observe to know
+ // when an attr is read.
+ // Keep a reference to the old reader
+ // if there is one. This is used
+ // for nested live binding.
+ oldReading = can.Observe.__reading;
+ can.Observe.__reading = function(obj, attr) {
+ // Add the observe and attr that was read
+ // to `observed`
+ observed.push({
+ obj: obj,
+ attr: attr + ""
+ });
+ };
+ }
+
+ var observed = [],
+ // Call the "wrapping" function to get the value. `observed`
+ // will have the observe/attribute pairs that were read.
+ value = func.call(self);
+
+ // Set back so we are no longer reading.
+ if (can.Observe) {
+ can.Observe.__reading = oldReading;
+ }
+ return {
+ value: value,
+ observed: observed
+ };
+ },
+ // Calls `callback(newVal, oldVal)` everytime an observed property
+ // called within `getterSetter` is changed and creates a new result of `getterSetter`.
+ // Also returns an object that can teardown all event handlers.
+ computeBinder = function(getterSetter, context, callback, computeState) {
+ // track what we are observing
+ var observing = {},
+ // a flag indicating if this observe/attr pair is already bound
+ matched = true,
+ // the data to return
+ data = {
+ // we will maintain the value while live-binding is taking place
+ value: undefined,
+ // a teardown method that stops listening
+ teardown: function() {
+ for (var name in observing) {
+ var ob = observing[name];
+ ob.observe.obj.unbind(ob.observe.attr, onchanged);
+ delete observing[name];
+ }
+ }
+ },
+ batchNum;
+
+ // when a property value is changed
+ var onchanged = function(ev) {
+ // If the compute is no longer bound (because the same change event led to an unbind)
+ // then do not call getValueAndBind, or we will leak bindings.
+ if (computeState && !computeState.bound) {
+ return;
+ }
+ if (ev.batchNum === undefined || ev.batchNum !== batchNum) {
+ // store the old value
+ var oldValue = data.value,
+ // get the new value
+ newvalue = getValueAndBind();
+
+ // update the value reference (in case someone reads)
+ data.value = newvalue;
+ // if a change happened
+ if (newvalue !== oldValue) {
+ callback(newvalue, oldValue);
+ }
+ batchNum = batchNum = ev.batchNum;
+ }
+
+
+ };
+
+ // gets the value returned by `getterSetter` and also binds to any attributes
+ // read by the call
+ var getValueAndBind = function() {
+ var info = getValueAndObserved(getterSetter, context),
+ newObserveSet = info.observed;
+
+ var value = info.value;
+ matched = !matched;
+
+ // go through every attribute read by this observe
+ can.each(newObserveSet, function(ob) {
+ // if the observe/attribute pair is being observed
+ if (observing[ob.obj._cid + "|" + ob.attr]) {
+ // mark at as observed
+ observing[ob.obj._cid + "|" + ob.attr].matched = matched;
+ } else {
+ // otherwise, set the observe/attribute on oldObserved, marking it as being observed
+ observing[ob.obj._cid + "|" + ob.attr] = {
+ matched: matched,
+ observe: ob
+ };
+ ob.obj.bind(ob.attr, onchanged);
+ }
+ });
+
+ // Iterate through oldObserved, looking for observe/attributes
+ // that are no longer being bound and unbind them
+ for (var name in observing) {
+ var ob = observing[name];
+ if (ob.matched !== matched) {
+ ob.observe.obj.unbind(ob.observe.attr, onchanged);
+ delete observing[name];
+ }
+ }
+ return value;
+ };
+ // set the initial value
+ data.value = getValueAndBind();
+
+ data.isListening = !can.isEmptyObject(observing);
+ return data;
+ }
+
+ // if no one is listening ... we can not calculate every time
+
+ can.compute = function(getterSetter, context, eventName) {
+ if (getterSetter && getterSetter.isComputed) {
+ return getterSetter;
+ }
+ // stores the result of computeBinder
+ var computedData,
+ // how many listeners to this this compute
+ bindings = 0,
+ // the computed object
+ computed,
+ // an object that keeps track if the computed is bound
+ // onchanged needs to know this. It's possible a change happens and results in
+ // something that unbinds the compute, it needs to not to try to recalculate who it
+ // is listening to
+ computeState = {
+ bound: false,
+ // true if this compute is calculated from other computes and observes
+ hasDependencies: false
+ },
+ // The following functions are overwritten depending on how compute() is called
+ // a method to setup listening
+ on = function() {},
+ // a method to teardown listening
+ off = function() {},
+ // the current cached value (only valid if bound = true)
+ value,
+ // how to read the value
+ get = function() {
+ return value
+ },
+ // sets the value
+ set = function(newVal) {
+ value = newVal;
+ },
+ // this compute can be a dependency of other computes
+ canReadForChangeEvent = true;
+
+ computed = function(newVal) {
+ // setting ...
+ if (arguments.length) {
+ // save a reference to the old value
+ var old = value;
+
+ // setter may return a value if
+ // setter is for a value maintained exclusively by this compute
+ var setVal = set.call(context, newVal, old);
+
+ // if this has dependencies return the current value
+ if (computed.hasDependencies) {
+ return get.call(context);
+ }
+
+ if (setVal === undefined) {
+ // it's possible, like with the DOM, setting does not
+ // fire a change event, so we must read
+ value = get.call(context);
+ } else {
+ value = setVal;
+ }
+ // fire the change
+ if (old !== value) {
+ can.Observe.triggerBatch(computed, "change", [value, old]);
+ }
+ return value;
+ } else {
+ // Let others know to listen to changes in this compute
+ if (can.Observe.__reading && canReadForChangeEvent) {
+ can.Observe.__reading(computed, 'change');
+ }
+ // if we are bound, use the cached value
+ if (computeState.bound) {
+ return value;
+ } else {
+ return get.call(context);
+ }
+ }
+ }
+ if (typeof getterSetter === "function") {
+ set = getterSetter;
+ get = getterSetter;
+ canReadForChangeEvent = eventName === false ? false : true;
+ computed.hasDependencies = false;
+ on = function(update) {
+ computedData = computeBinder(getterSetter, context || this, update, computeState);
+ computed.hasDependencies = computedData.isListening
+ value = computedData.value;
+ }
+ off = function() {
+ computedData.teardown();
+ }
+ } else if (context) {
+
+ if (typeof context == "string") {
+ // `can.compute(obj, "propertyName", [eventName])`
+
+ var propertyName = context,
+ isObserve = getterSetter instanceof can.Observe;
+ if (isObserve) {
+ computed.hasDependencies = true;
+ }
+ get = function() {
+ if (isObserve) {
+ return getterSetter.attr(propertyName);
+ } else {
+ return getterSetter[propertyName];
+ }
+ }
+ set = function(newValue) {
+ if (isObserve) {
+ getterSetter.attr(propertyName, newValue)
+ } else {
+ getterSetter[propertyName] = newValue;
+ }
+ }
+ var handler;
+ on = function(update) {
+ handler = function() {
+ update(get(), value)
+ };
+ can.bind.call(getterSetter, eventName || propertyName, handler)
+
+ // use getValueAndObserved because
+ // we should not be indicating that some parent
+ // reads this property if it happens to be binding on it
+ value = getValueAndObserved(get).value
+ }
+ off = function() {
+ can.unbind.call(getterSetter, eventName || propertyName, handler)
+ }
+
+ } else {
+ // `can.compute(initialValue, setter)`
+ if (typeof context === "function") {
+ value = getterSetter;
+ set = context;
+ } else {
+ // `can.compute(initialValue,{get:, set:, on:, off:})`
+ value = getterSetter;
+ var options = context;
+ get = options.get || get;
+ set = options.set || set;
+ on = options.on || on;
+ off = options.off || off;
+ }
+
+ }
+
+
+
+ } else {
+ // `can.compute(5)`
+ value = getterSetter;
+ }
+
+ computed.isComputed = true;
+
+ can.cid(computed, "compute")
+
+ var updater = function(newValue, oldValue) {
+ value = newValue;
+ // might need a way to look up new and oldVal
+ can.Observe.triggerBatch(computed, "change", [newValue, oldValue])
+ }
+
+ return can.extend(computed, {
+ _bindsetup: function() {
+ computeState.bound = true;
+ // setup live-binding
+ on.call(this, updater)
+ },
+ _bindteardown: function() {
+ off.call(this, updater)
+ computeState.bound = false;
+ },
+
+ bind: can.bindAndSetup,
+
+ unbind: can.unbindAndTeardown
+ });
+ };
+ can.compute.binder = computeBinder;
+ return can.compute;
+ })(__m3, __m13);
+
+ // ## model/model.js
+ var __m15 = (function(can) {
+
+ // ## model.js
+ // `can.Model`
+ // _A `can.Observe` that connects to a RESTful interface._
+ // Generic deferred piping function
+
+ var pipe = function(def, model, func) {
+ var d = new can.Deferred();
+ def.then(function() {
+ var args = can.makeArray(arguments);
+ args[0] = model[func](args[0]);
+ d.resolveWith(d, args);
+ }, function() {
+ d.rejectWith(this, arguments);
+ });
+
+ if (typeof def.abort === 'function') {
+ d.abort = function() {
+ return def.abort();
+ }
+ }
+
+ return d;
+ },
+ modelNum = 0,
+ ignoreHookup = /change.observe\d+/,
+ getId = function(inst) {
+ // Instead of using attr, use __get for performance.
+ // Need to set reading
+ can.Observe.__reading && can.Observe.__reading(inst, inst.constructor.id)
+ return inst.__get(inst.constructor.id);
+ },
+ // Ajax `options` generator function
+ ajax = function(ajaxOb, data, type, dataType, success, error) {
+
+ var params = {};
+
+ // If we get a string, handle it.
+ if (typeof ajaxOb == "string") {
+ // If there's a space, it's probably the type.
+ var parts = ajaxOb.split(/\s+/);
+ params.url = parts.pop();
+ if (parts.length) {
+ params.type = parts.pop();
+ }
+ } else {
+ can.extend(params, ajaxOb);
+ }
+
+ // If we are a non-array object, copy to a new attrs.
+ params.data = typeof data == "object" && !can.isArray(data) ?
+ can.extend(params.data || {}, data) : data;
+
+ // Get the url with any templated values filled out.
+ params.url = can.sub(params.url, params.data, true);
+
+ return can.ajax(can.extend({
+ type: type || "post",
+ dataType: dataType || "json",
+ success: success,
+ error: error
+ }, params));
+ },
+ makeRequest = function(self, type, success, error, method) {
+ var args;
+ // if we pass an array as `self` it it means we are coming from
+ // the queued request, and we're passing already serialized data
+ // self's signature will be: [self, serializedData]
+ if (can.isArray(self)) {
+ args = self[1];
+ self = self[0];
+ } else {
+ args = self.serialize();
+ }
+ args = [args];
+ var deferred,
+ // The model.
+ model = self.constructor,
+ jqXHR;
+
+ // `destroy` does not need data.
+ if (type == 'destroy') {
+ args.shift();
+ }
+ // `update` and `destroy` need the `id`.
+ if (type !== 'create') {
+ args.unshift(getId(self));
+ }
+
+
+ jqXHR = model[type].apply(model, args);
+
+ deferred = jqXHR.pipe(function(data) {
+ self[method || type + "d"](data, jqXHR);
+ return self;
+ });
+
+ // Hook up `abort`
+ if (jqXHR.abort) {
+ deferred.abort = function() {
+ jqXHR.abort();
+ };
+ }
+
+ deferred.then(success, error);
+ return deferred;
+ },
+
+ // This object describes how to make an ajax request for each ajax method.
+ // The available properties are:
+ // `url` - The default url to use as indicated as a property on the model.
+ // `type` - The default http request type
+ // `data` - A method that takes the `arguments` and returns `data` used for ajax.
+
+ ajaxMethods = {
+
+ create: {
+ url: "_shortName",
+ type: "post"
+ },
+
+ update: {
+ data: function(id, attrs) {
+ attrs = attrs || {};
+ var identity = this.id;
+ if (attrs[identity] && attrs[identity] !== id) {
+ attrs["new" + can.capitalize(id)] = attrs[identity];
+ delete attrs[identity];
+ }
+ attrs[identity] = id;
+ return attrs;
+ },
+ type: "put"
+ },
+
+ destroy: {
+ type: "delete",
+ data: function(id) {
+ var args = {};
+ args.id = args[this.id] = id;
+ return args;
+ }
+ },
+
+ findAll: {
+ url: "_shortName"
+ },
+
+ findOne: {}
+ },
+ // Makes an ajax request `function` from a string.
+ // `ajaxMethod` - The `ajaxMethod` object defined above.
+ // `str` - The string the user provided. Ex: `findAll: "/recipes.json"`.
+ ajaxMaker = function(ajaxMethod, str) {
+ // Return a `function` that serves as the ajax method.
+ return function(data) {
+ // If the ajax method has it's own way of getting `data`, use that.
+ data = ajaxMethod.data ?
+ ajaxMethod.data.apply(this, arguments) :
+ // Otherwise use the data passed in.
+ data;
+ // Return the ajax method with `data` and the `type` provided.
+ return ajax(str || this[ajaxMethod.url || "_url"], data, ajaxMethod.type || "get")
+ }
+ }
+
+
+
+ can.Model = can.Observe({
+ fullName: "can.Model",
+ _reqs: 0,
+ setup: function(base) {
+ // create store here if someone wants to use model without inheriting from it
+ this.store = {};
+ can.Observe.setup.apply(this, arguments);
+ // Set default list as model list
+ if (!can.Model) {
+ return;
+ }
+ this.List = ML({
+ Observe: this
+ }, {});
+ var self = this,
+ clean = can.proxy(this._clean, self);
+
+
+ // go through ajax methods and set them up
+ can.each(ajaxMethods, function(method, name) {
+ // if an ajax method is not a function, it's either
+ // a string url like findAll: "/recipes" or an
+ // ajax options object like {url: "/recipes"}
+ if (!can.isFunction(self[name])) {
+ // use ajaxMaker to convert that into a function
+ // that returns a deferred with the data
+ self[name] = ajaxMaker(method, self[name]);
+ }
+ // check if there's a make function like makeFindAll
+ // these take deferred function and can do special
+ // behavior with it (like look up data in a store)
+ if (self["make" + can.capitalize(name)]) {
+ // pass the deferred method to the make method to get back
+ // the "findAll" method.
+ var newMethod = self["make" + can.capitalize(name)](self[name]);
+ can.Construct._overwrite(self, base, name, function() {
+ // increment the numer of requests
+ can.Model._reqs++;
+ var def = newMethod.apply(this, arguments);
+ var then = def.then(clean, clean);
+ then.abort = def.abort;
+
+ // attach abort to our then and return it
+ return then;
+ })
+ }
+ });
+
+ if (self.fullName == "can.Model" || !self.fullName) {
+ self.fullName = "Model" + (++modelNum);
+ }
+ // Add ajax converters.
+ can.Model._reqs = 0;
+ this._url = this._shortName + "/{" + this.id + "}"
+ },
+ _ajax: ajaxMaker,
+ _makeRequest: makeRequest,
+ _clean: function() {
+ can.Model._reqs--;
+ if (!can.Model._reqs) {
+ for (var id in this.store) {
+ if (!this.store[id]._bindings) {
+ delete this.store[id];
+ }
+ }
+ }
+ return arguments[0];
+ },
+
+ models: function(instancesRawData, oldList) {
+ // until "end of turn", increment reqs counter so instances will be added to the store
+ can.Model._reqs++;
+ if (!instancesRawData) {
+ return;
+ }
+
+ if (instancesRawData instanceof this.List) {
+ return instancesRawData;
+ }
+
+ // Get the list type.
+ var self = this,
+ tmp = [],
+ res = oldList instanceof can.Observe.List ? oldList : new(self.List || ML),
+ // Did we get an `array`?
+ arr = can.isArray(instancesRawData),
+
+ // Did we get a model list?
+ ml = (instancesRawData instanceof ML),
+
+ // Get the raw `array` of objects.
+ raw = arr ?
+
+ // If an `array`, return the `array`.
+ instancesRawData :
+
+ // Otherwise if a model list.
+ (ml ?
+
+ // Get the raw objects from the list.
+ instancesRawData.serialize() :
+
+ // Get the object's data.
+ instancesRawData.data),
+ i = 0;
+
+
+
+ if (res.length) {
+ res.splice(0);
+ }
+
+ can.each(raw, function(rawPart) {
+ tmp.push(self.model(rawPart));
+ });
+
+ // We only want one change event so push everything at once
+ res.push.apply(res, tmp);
+
+ if (!arr) { // Push other stuff onto `array`.
+ can.each(instancesRawData, function(val, prop) {
+ if (prop !== 'data') {
+ res.attr(prop, val);
+ }
+ })
+ }
+ // at "end of turn", clean up the store
+ setTimeout(can.proxy(this._clean, this), 1);
+ return res;
+ },
+
+ model: function(attributes) {
+ if (!attributes) {
+ return;
+ }
+ if (attributes instanceof this) {
+ attributes = attributes.serialize();
+ }
+ var id = attributes[this.id],
+ model = (id || id === 0) && this.store[id] ?
+ this.store[id].attr(attributes, this.removeAttr || false) : new this(attributes);
+ if (can.Model._reqs) {
+ this.store[attributes[this.id]] = model;
+ }
+ return model;
+ }
+ },
+
+
+ {
+
+ isNew: function() {
+ var id = getId(this);
+ return !(id || id === 0); // If `null` or `undefined`
+ },
+
+ save: function(success, error) {
+ return makeRequest(this, this.isNew() ? 'create' : 'update', success, error);
+ },
+
+ destroy: function(success, error) {
+ if (this.isNew()) {
+ var self = this;
+ var def = can.Deferred();
+ def.then(success, error);
+ return def.done(function(data) {
+ self.destroyed(data)
+ }).resolve(self);
+ }
+ return makeRequest(this, 'destroy', success, error, 'destroyed');
+ },
+
+ _bindsetup: function() {
+ this.constructor.store[this.__get(this.constructor.id)] = this;
+ return can.Observe.prototype._bindsetup.apply(this, arguments);
+ },
+
+ _bindteardown: function() {
+ delete this.constructor.store[getId(this)];
+ return can.Observe.prototype._bindteardown.apply(this, arguments)
+ },
+ // Change `id`.
+ ___set: function(prop, val) {
+ can.Observe.prototype.___set.call(this, prop, val)
+ // If we add an `id`, move it to the store.
+ if (prop === this.constructor.id && this._bindings) {
+ this.constructor.store[getId(this)] = this;
+ }
+ }
+ });
+
+ can.each({
+ makeFindAll: "models",
+ makeFindOne: "model",
+ makeCreate: "model",
+ makeUpdate: "model"
+ }, function(method, name) {
+ can.Model[name] = function(oldMethod) {
+ return function() {
+ var args = can.makeArray(arguments),
+ oldArgs = can.isFunction(args[1]) ? args.splice(0, 1) : args.splice(0, 2),
+ def = pipe(oldMethod.apply(this, oldArgs), this, method);
+ def.then(args[0], args[1]);
+ // return the original promise
+ return def;
+ };
+ };
+ });
+
+ can.each([
+
+ "created",
+
+ "updated",
+
+ "destroyed"
+ ], function(funcName) {
+ can.Model.prototype[funcName] = function(attrs) {
+ var stub,
+ constructor = this.constructor;
+
+ // Update attributes if attributes have been passed
+ stub = attrs && typeof attrs == 'object' && this.attr(attrs.attr ? attrs.attr() : attrs);
+
+ // triggers change event that bubble's like
+ // handler( 'change','1.destroyed' ). This is used
+ // to remove items on destroyed from Model Lists.
+ // but there should be a better way.
+ can.trigger(this, "change", funcName)
+
+
+ // Call event on the instance's Class
+ can.trigger(constructor, funcName, this);
+ };
+ });
+
+ // Model lists are just like `Observe.List` except that when their items are
+ // destroyed, it automatically gets removed from the list.
+
+ var ML = can.Model.List = can.Observe.List({
+ setup: function(params) {
+ if (can.isPlainObject(params) && !can.isArray(params)) {
+ can.Observe.List.prototype.setup.apply(this);
+ this.replace(this.constructor.Observe.findAll(params))
+ } else {
+ can.Observe.List.prototype.setup.apply(this, arguments);
+ }
+ },
+ _changes: function(ev, attr) {
+ can.Observe.List.prototype._changes.apply(this, arguments);
+ if (/\w+\.destroyed/.test(attr)) {
+ var index = this.indexOf(ev.target);
+ if (index != -1) {
+ this.splice(index, 1);
+ }
+ }
+ }
+ })
+
+ return can.Model;
+ })(__m3, __m12);
+
+ // ## view/view.js
+ var __m16 = (function(can) {
+ // ## view.js
+ // `can.view`
+ // _Templating abstraction._
+
+ var isFunction = can.isFunction,
+ makeArray = can.makeArray,
+ // Used for hookup `id`s.
+ hookupId = 1,
+
+ $view = can.view = can.template = function(view, data, helpers, callback) {
+ // If helpers is a `function`, it is actually a callback.
+ if (isFunction(helpers)) {
+ callback = helpers;
+ helpers = undefined;
+ }
+
+ var pipe = function(result) {
+ return $view.frag(result);
+ },
+ // In case we got a callback, we need to convert the can.view.render
+ // result to a document fragment
+ wrapCallback = isFunction(callback) ? function(frag) {
+ callback(pipe(frag));
+ } : null,
+ // Get the result.
+ result = $view.render(view, data, helpers, wrapCallback),
+ deferred = can.Deferred();
+
+ if (isFunction(result)) {
+ return result;
+ }
+
+ if (can.isDeferred(result)) {
+ result.then(function(result, data) {
+ deferred.resolve.call(deferred, pipe(result), data);
+ }, function() {
+ deferred.fail.apply(deferred, arguments);
+ });
+ return deferred;
+ }
+
+ // Convert it into a dom frag.
+ return pipe(result);
+ };
+
+ can.extend($view, {
+ // creates a frag and hooks it up all at once
+ frag: function(result, parentNode) {
+ return $view.hookup($view.fragment(result), parentNode);
+ },
+
+ // simply creates a frag
+ // this is used internally to create a frag
+ // insert it
+ // then hook it up
+ fragment: function(result) {
+ var frag = can.buildFragment(result, document.body);
+ // If we have an empty frag...
+ if (!frag.childNodes.length) {
+ frag.appendChild(document.createTextNode(''));
+ }
+ return frag;
+ },
+
+ // Convert a path like string into something that's ok for an `element` ID.
+ toId: function(src) {
+ return can.map(src.toString().split(/\/|\./g), function(part) {
+ // Dont include empty strings in toId functions
+ if (part) {
+ return part;
+ }
+ }).join("_");
+ },
+
+ hookup: function(fragment, parentNode) {
+ var hookupEls = [],
+ id,
+ func;
+
+ // Get all `childNodes`.
+ can.each(fragment.childNodes ? can.makeArray(fragment.childNodes) : fragment, function(node) {
+ if (node.nodeType === 1) {
+ hookupEls.push(node);
+ hookupEls.push.apply(hookupEls, can.makeArray(node.getElementsByTagName('*')));
+ }
+ });
+
+ // Filter by `data-view-id` attribute.
+ can.each(hookupEls, function(el) {
+ if (el.getAttribute && (id = el.getAttribute('data-view-id')) && (func = $view.hookups[id])) {
+ func(el, parentNode, id);
+ delete $view.hookups[id];
+ el.removeAttribute('data-view-id');
+ }
+ });
+
+ return fragment;
+ },
+
+
+ hookups: {},
+
+
+ hook: function(cb) {
+ $view.hookups[++hookupId] = cb;
+ return " data-view-id='" + hookupId + "'";
+ },
+
+
+ cached: {},
+
+ cachedRenderers: {},
+
+
+ cache: true,
+
+
+ register: function(info) {
+ this.types["." + info.suffix] = info;
+ },
+
+ types: {},
+
+
+ ext: ".ejs",
+
+
+ registerScript: function() {},
+
+
+ preload: function() {},
+
+
+ render: function(view, data, helpers, callback) {
+ // If helpers is a `function`, it is actually a callback.
+ if (isFunction(helpers)) {
+ callback = helpers;
+ helpers = undefined;
+ }
+
+ // See if we got passed any deferreds.
+ var deferreds = getDeferreds(data);
+
+ if (deferreds.length) { // Does data contain any deferreds?
+ // The deferred that resolves into the rendered content...
+ var deferred = new can.Deferred(),
+ dataCopy = can.extend({}, data);
+
+ // Add the view request to the list of deferreds.
+ deferreds.push(get(view, true))
+
+ // Wait for the view and all deferreds to finish...
+ can.when.apply(can, deferreds).then(function(resolved) {
+ // Get all the resolved deferreds.
+ var objs = makeArray(arguments),
+ // Renderer is the last index of the data.
+ renderer = objs.pop(),
+ // The result of the template rendering with data.
+ result;
+
+ // Make data look like the resolved deferreds.
+ if (can.isDeferred(data)) {
+ dataCopy = usefulPart(resolved);
+ } else {
+ // Go through each prop in data again and
+ // replace the defferreds with what they resolved to.
+ for (var prop in data) {
+ if (can.isDeferred(data[prop])) {
+ dataCopy[prop] = usefulPart(objs.shift());
+ }
+ }
+ }
+
+ // Get the rendered result.
+ result = renderer(dataCopy, helpers);
+
+ // Resolve with the rendered view.
+ deferred.resolve(result, dataCopy);
+
+ // If there's a `callback`, call it back with the result.
+ callback && callback(result, dataCopy);
+ }, function() {
+ deferred.reject.apply(deferred, arguments)
+ });
+ // Return the deferred...
+ return deferred;
+ } else {
+ // No deferreds! Render this bad boy.
+ var response,
+ // If there's a `callback` function
+ async = isFunction(callback),
+ // Get the `view` type
+ deferred = get(view, async);
+
+ // If we are `async`...
+ if (async) {
+ // Return the deferred
+ response = deferred;
+ // And fire callback with the rendered result.
+ deferred.then(function(renderer) {
+ callback(data ? renderer(data, helpers) : renderer);
+ })
+ } else {
+ // if the deferred is resolved, call the cached renderer instead
+ // this is because it's possible, with recursive deferreds to
+ // need to render a view while its deferred is _resolving_. A _resolving_ deferred
+ // is a deferred that was just resolved and is calling back it's success callbacks.
+ // If a new success handler is called while resoliving, it does not get fired by
+ // jQuery's deferred system. So instead of adding a new callback
+ // we use the cached renderer.
+ // We also add __view_id on the deferred so we can look up it's cached renderer.
+ // In the future, we might simply store either a deferred or the cached result.
+ if (deferred.state() === "resolved" && deferred.__view_id) {
+ var currentRenderer = $view.cachedRenderers[deferred.__view_id];
+ return data ? currentRenderer(data, helpers) : currentRenderer;
+ } else {
+ // Otherwise, the deferred is complete, so
+ // set response to the result of the rendering.
+ deferred.then(function(renderer) {
+ response = data ? renderer(data, helpers) : renderer;
+ });
+ }
+ }
+
+ return response;
+ }
+ },
+
+
+ registerView: function(id, text, type, def) {
+ // Get the renderer function.
+ var func = (type || $view.types[$view.ext]).renderer(id, text);
+ def = def || new can.Deferred();
+
+ // Cache if we are caching.
+ if ($view.cache) {
+ $view.cached[id] = def;
+ def.__view_id = id;
+ $view.cachedRenderers[id] = func;
+ }
+
+ // Return the objects for the response's `dataTypes`
+ // (in this case view).
+ return def.resolve(func);
+ }
+ });
+
+ // Makes sure there's a template, if not, have `steal` provide a warning.
+ var checkText = function(text, url) {
+ if (!text.length) {
+
+ throw "can.view: No template or empty template:" + url;
+ }
+ },
+ // `Returns a `view` renderer deferred.
+ // `url` - The url to the template.
+ // `async` - If the ajax request should be asynchronous.
+ // Returns a deferred.
+ get = function(url, async) {
+ var suffix = url.match(/\.[\w\d]+$/),
+ type,
+ // If we are reading a script element for the content of the template,
+ // `el` will be set to that script element.
+ el,
+ // A unique identifier for the view (used for caching).
+ // This is typically derived from the element id or
+ // the url for the template.
+ id,
+ // The ajax request used to retrieve the template content.
+ jqXHR;
+
+ //If the url has a #, we assume we want to use an inline template
+ //from a script element and not current page's HTML
+ if (url.match(/^#/)) {
+ url = url.substr(1);
+ }
+ // If we have an inline template, derive the suffix from the `text/???` part.
+ // This only supports `<script>` tags.
+ if (el = document.getElementById(url)) {
+ suffix = "." + el.type.match(/\/(x\-)?(.+)/)[2];
+ }
+
+ // If there is no suffix, add one.
+ if (!suffix && !$view.cached[url]) {
+ url += (suffix = $view.ext);
+ }
+
+ if (can.isArray(suffix)) {
+ suffix = suffix[0]
+ }
+
+ // Convert to a unique and valid id.
+ id = $view.toId(url);
+
+ // If an absolute path, use `steal` to get it.
+ // You should only be using `//` if you are using `steal`.
+ if (url.match(/^\/\//)) {
+ var sub = url.substr(2);
+ url = !window.steal ?
+ sub :
+ steal.config().root.mapJoin("" + steal.id(sub));
+ }
+
+ // Set the template engine type.
+ type = $view.types[suffix];
+
+ // If it is cached,
+ if ($view.cached[id]) {
+ // Return the cached deferred renderer.
+ return $view.cached[id];
+
+ // Otherwise if we are getting this from a `<script>` element.
+ } else if (el) {
+ // Resolve immediately with the element's `innerHTML`.
+ return $view.registerView(id, el.innerHTML, type);
+ } else {
+ // Make an ajax request for text.
+ var d = new can.Deferred();
+ can.ajax({
+ async: async,
+ url: url,
+ dataType: "text",
+ error: function(jqXHR) {
+ checkText("", url);
+ d.reject(jqXHR);
+ },
+ success: function(text) {
+ // Make sure we got some text back.
+ checkText(text, url);
+ $view.registerView(id, text, type, d)
+ }
+ });
+ return d;
+ }
+ },
+ // Gets an `array` of deferreds from an `object`.
+ // This only goes one level deep.
+ getDeferreds = function(data) {
+ var deferreds = [];
+
+ // pull out deferreds
+ if (can.isDeferred(data)) {
+ return [data]
+ } else {
+ for (var prop in data) {
+ if (can.isDeferred(data[prop])) {
+ deferreds.push(data[prop]);
+ }
+ }
+ }
+ return deferreds;
+ },
+ // Gets the useful part of a resolved deferred.
+ // This is for `model`s and `can.ajax` that resolve to an `array`.
+ usefulPart = function(resolved) {
+ return can.isArray(resolved) && resolved[1] === 'success' ? resolved[0] : resolved
+ };
+
+ //!steal-pluginify-remove-start
+ if (window.steal) {
+ steal.type("view js", function(options, success, error) {
+ var type = $view.types["." + options.type],
+ id = $view.toId(options.id);
+
+ options.text = "steal('" + (type.plugin || "can/view/" + options.type) + "',function(can){return " + "can.view.preload('" + id + "'," + options.text + ");\n})";
+ success();
+ })
+ }
+ //!steal-pluginify-remove-end
+
+ can.extend($view, {
+ register: function(info) {
+ this.types["." + info.suffix] = info;
+
+ //!steal-pluginify-remove-start
+ if (window.steal) {
+ steal.type(info.suffix + " view js", function(options, success, error) {
+ var type = $view.types["." + options.type],
+ id = $view.toId(options.id + '');
+
+ options.text = type.script(id, options.text)
+ success();
+ })
+ };
+ //!steal-pluginify-remove-end
+
+ $view[info.suffix] = function(id, text) {
+ if (!text) {
+ // Return a nameless renderer
+ var renderer = function() {
+ return $view.frag(renderer.render.apply(this, arguments));
+ }
+ renderer.render = function() {
+ var renderer = info.renderer(null, id);
+ return renderer.apply(renderer, arguments);
+ }
+ return renderer;
+ }
+
+ $view.preload(id, info.renderer(id, text));
+ return can.view(id);
+ }
+ },
+ registerScript: function(type, id, src) {
+ return "can.view.preload('" + id + "'," + $view.types["." + type].script(id, src) + ");";
+ },
+ preload: function(id, renderer) {
+ $view.cached[id] = new can.Deferred().resolve(function(data, helpers) {
+ return renderer.call(data, data, helpers);
+ });
+
+ function frag() {
+ return $view.frag(renderer.apply(this, arguments));
+ }
+ // expose the renderer for mustache
+ frag.render = renderer;
+ return frag;
+ }
+
+ });
+
+ return can;
+ })(__m3);
+
+ // ## view/elements.js
+ var __m19 = (function() {
+
+ var elements = {
+ tagToContentPropMap: {
+ option: "textContent" in document.createElement("option") ? "textContent" : "innerText",
+ textarea: "value"
+ },
+
+ attrMap: {
+ "class": "className",
+ "value": "value",
+ "innerText": "innerText",
+ "textContent": "textContent",
+ "checked": true,
+ "disabled": true,
+ "readonly": true,
+ "required": true
+ },
+ // elements whos default value we should set
+ defaultValue: ["input", "textarea"],
+ // a map of parent element to child elements
+ tagMap: {
+ "": "span",
+ table: "tbody",
+ tr: "td",
+ ol: "li",
+ ul: "li",
+ tbody: "tr",
+ thead: "tr",
+ tfoot: "tr",
+ select: "option",
+ optgroup: "option"
+ },
+ // a tag's parent element
+ reverseTagMap: {
+ tr: "tbody",
+ option: "select",
+ td: "tr",
+ th: "tr",
+ li: "ul"
+ },
+
+ getParentNode: function(el, defaultParentNode) {
+ return defaultParentNode && el.parentNode.nodeType === 11 ? defaultParentNode : el.parentNode;
+ },
+ // set an attribute on an element
+ setAttr: function(el, attrName, val) {
+ var tagName = el.nodeName.toString().toLowerCase(),
+ prop = elements.attrMap[attrName];
+ // if this is a special property
+ if (prop === true) {
+ el[attrName] = true;
+ } else if (prop) {
+ // set the value as true / false
+ el[prop] = val;
+ if (prop === "value" && can.inArray(tagName, elements.defaultValue) >= 0) {
+ el.defaultValue = val;
+ }
+ } else {
+ el.setAttribute(attrName, val);
+ }
+ },
+ // gets the value of an attribute
+ getAttr: function(el, attrName) {
+ // Default to a blank string for IE7/8
+ return (elements.attrMap[attrName] && el[elements.attrMap[attrName]] ?
+ el[elements.attrMap[attrName]] :
+ el.getAttribute(attrName)) || '';
+ },
+ // removes the attribute
+ removeAttr: function(el, attrName) {
+ if (elements.attrMap[attrName] === true) {
+ el[attrName] = false;
+ } else {
+ el.removeAttribute(attrName);
+ }
+ },
+ contentText: function(text) {
+ if (typeof text == 'string') {
+ return text;
+ }
+ // If has no value, return an empty string.
+ if (!text && text !== 0) {
+ return '';
+ }
+ return "" + text;
+ }
+ };
+
+ return elements;
+ })();
+
+ // ## view/scanner.js
+ var __m18 = (function(can, elements) {
+
+ var newLine = /(\r|\n)+/g,
+ // Escapes characters starting with `\`.
+ clean = function(content) {
+ return content
+ .split('\\').join("\\\\")
+ .split("\n").join("\\n")
+ .split('"').join('\\"')
+ .split("\t").join("\\t");
+ },
+ // Returns a tagName to use as a temporary placeholder for live content
+ // looks forward ... could be slow, but we only do it when necessary
+ getTag = function(tagName, tokens, i) {
+ // if a tagName is provided, use that
+ if (tagName) {
+ return tagName;
+ } else {
+ // otherwise go searching for the next two tokens like "<",TAG
+ while (i < tokens.length) {
+ if (tokens[i] == "<" && elements.reverseTagMap[tokens[i + 1]]) {
+ return elements.reverseTagMap[tokens[i + 1]];
+ }
+ i++;
+ }
+ }
+ return '';
+ },
+ bracketNum = function(content) {
+ return (--content.split("{").length) - (--content.split("}").length);
+ },
+ myEval = function(script) {
+ eval(script);
+ },
+ attrReg = /([^\s]+)[\s]*=[\s]*$/,
+ // Commands for caching.
+ startTxt = 'var ___v1ew = [];',
+ finishTxt = "return ___v1ew.join('')",
+ put_cmd = "___v1ew.push(",
+ insert_cmd = put_cmd,
+ // Global controls (used by other functions to know where we are).
+ // Are we inside a tag?
+ htmlTag = null,
+ // Are we within a quote within a tag?
+ quote = null,
+ // What was the text before the current quote? (used to get the `attr` name)
+ beforeQuote = null,
+ // Whether a rescan is in progress
+ rescan = null,
+ // Used to mark where the element is.
+ status = function() {
+ // `t` - `1`.
+ // `h` - `0`.
+ // `q` - String `beforeQuote`.
+ return quote ? "'" + beforeQuote.match(attrReg)[1] + "'" : (htmlTag ? 1 : 0);
+ };
+
+ can.view.Scanner = Scanner = function(options) {
+ // Set options on self
+ can.extend(this, {
+ text: {},
+ tokens: []
+ }, options);
+
+ // Cache a token lookup
+ this.tokenReg = [];
+ this.tokenSimple = {
+ "<": "<",
+ ">": ">",
+ '"': '"',
+ "'": "'"
+ };
+ this.tokenComplex = [];
+ this.tokenMap = {};
+ for (var i = 0, token; token = this.tokens[i]; i++) {
+
+
+ // Save complex mappings (custom regexp)
+ if (token[2]) {
+ this.tokenReg.push(token[2]);
+ this.tokenComplex.push({
+ abbr: token[1],
+ re: new RegExp(token[2]),
+ rescan: token[3]
+ });
+ }
+ // Save simple mappings (string only, no regexp)
+ else {
+ this.tokenReg.push(token[1]);
+ this.tokenSimple[token[1]] = token[0];
+ }
+ this.tokenMap[token[0]] = token[1];
+ }
+
+ // Cache the token registry.
+ this.tokenReg = new RegExp("(" + this.tokenReg.slice(0).concat(["<", ">", '"', "'"]).join("|") + ")", "g");
+ };
+
+ Scanner.prototype = {
+
+ helpers: [
+
+ {
+ name: /\s*\(([\$\w]+)\)\s*->([^\n]*)/,
+ fn: function(content) {
+ var quickFunc = /\s*\(([\$\w]+)\)\s*->([^\n]*)/,
+ parts = content.match(quickFunc);
+
+ return "can.proxy(function(__){var " + parts[1] + "=can.$(__);" + parts[2] + "}, this);";
+ }
+ }
+ ],
+
+ scan: function(source, name) {
+ var tokens = [],
+ last = 0,
+ simple = this.tokenSimple,
+ complex = this.tokenComplex;
+
+ source = source.replace(newLine, "\n");
+ if (this.transform) {
+ source = this.transform(source);
+ }
+ source.replace(this.tokenReg, function(whole, part) {
+ // offset is the second to last argument
+ var offset = arguments[arguments.length - 2];
+
+ // if the next token starts after the last token ends
+ // push what's in between
+ if (offset > last) {
+ tokens.push(source.substring(last, offset));
+ }
+
+ // push the simple token (if there is one)
+ if (simple[whole]) {
+ tokens.push(whole);
+ }
+ // otherwise lookup complex tokens
+ else {
+ for (var i = 0, token; token = complex[i]; i++) {
+ if (token.re.test(whole)) {
+ tokens.push(token.abbr);
+ // Push a rescan function if one exists
+ if (token.rescan) {
+ tokens.push(token.rescan(part));
+ }
+ break;
+ }
+ }
+ }
+
+ // update the position of the last part of the last token
+ last = offset + part.length;
+ });
+
+ // if there's something at the end, add it
+ if (last < source.length) {
+ tokens.push(source.substr(last));
+ }
+
+ var content = '',
+ buff = [startTxt + (this.text.start || '')],
+ // Helper `function` for putting stuff in the view concat.
+ put = function(content, bonus) {
+ buff.push(put_cmd, '"', clean(content), '"' + (bonus || '') + ');');
+ },
+ // A stack used to keep track of how we should end a bracket
+ // `}`.
+ // Once we have a `<%= %>` with a `leftBracket`,
+ // we store how the file should end here (either `))` or `;`).
+ endStack = [],
+ // The last token, used to remember which tag we are in.
+ lastToken,
+ // The corresponding magic tag.
+ startTag = null,
+ // Was there a magic tag inside an html tag?
+ magicInTag = false,
+ // The current tag name.
+ tagName = '',
+ // stack of tagNames
+ tagNames = [],
+ // Pop from tagNames?
+ popTagName = false,
+ // Declared here.
+ bracketCount,
+ i = 0,
+ token,
+ tmap = this.tokenMap;
+
+ // Reinitialize the tag state goodness.
+ htmlTag = quote = beforeQuote = null;
+
+ for (;
+ (token = tokens[i++]) !== undefined;) {
+ if (startTag === null) {
+ switch (token) {
+ case tmap.left:
+ case tmap.escapeLeft:
+ case tmap.returnLeft:
+ magicInTag = htmlTag && 1;
+ case tmap.commentLeft:
+ // A new line -- just add whatever content within a clean.
+ // Reset everything.
+ startTag = token;
+ if (content.length) {
+ put(content);
+ }
+ content = '';
+ break;
+ case tmap.escapeFull:
+ // This is a full line escape (a line that contains only whitespace and escaped logic)
+ // Break it up into escape left and right
+ magicInTag = htmlTag && 1;
+ rescan = 1;
+ startTag = tmap.escapeLeft;
+ if (content.length) {
+ put(content);
+ }
+ rescan = tokens[i++];
+ content = rescan.content || rescan;
+ if (rescan.before) {
+ put(rescan.before);
+ }
+ tokens.splice(i, 0, tmap.right);
+ break;
+ case tmap.commentFull:
+ // Ignore full line comments.
+ break;
+ case tmap.templateLeft:
+ content += tmap.left;
+ break;
+ case '<':
+ // Make sure we are not in a comment.
+ if (tokens[i].indexOf("!--") !== 0) {
+ htmlTag = 1;
+ magicInTag = 0;
+ }
+ content += token;
+ break;
+ case '>':
+ htmlTag = 0;
+ // content.substr(-1) doesn't work in IE7/8
+ var emptyElement = content.substr(content.length - 1) == "/" || content.substr(content.length - 2) == "--";
+ // if there was a magic tag
+ // or it's an element that has text content between its tags,
+ // but content is not other tags add a hookup
+ // TODO: we should only add `can.EJS.pending()` if there's a magic tag
+ // within the html tags.
+ if (magicInTag || !popTagName && elements.tagToContentPropMap[tagNames[tagNames.length - 1]]) {
+ // make sure / of /> is on the left of pending
+ if (emptyElement) {
+ put(content.substr(0, content.length - 1), ",can.view.pending(),\"/>\"");
+ } else {
+ put(content, ",can.view.pending(),\">\"");
+ }
+ content = '';
+ magicInTag = 0;
+ } else {
+ content += token;
+ }
+ // if it's a tag like <input/>
+ if (emptyElement || popTagName) {
+ // remove the current tag in the stack
+ tagNames.pop();
+ // set the current tag to the previous parent
+ tagName = tagNames[tagNames.length - 1];
+ // Don't pop next time
+ popTagName = false;
+ }
+ break;
+ case "'":
+ case '"':
+ // If we are in an html tag, finding matching quotes.
+ if (htmlTag) {
+ // We have a quote and it matches.
+ if (quote && quote === token) {
+ // We are exiting the quote.
+ quote = null;
+ // Otherwise we are creating a quote.
+ // TODO: does this handle `\`?
+ } else if (quote === null) {
+ quote = token;
+ beforeQuote = lastToken;
+ }
+ }
+ default:
+ // Track the current tag
+ if (lastToken === '<') {
+ tagName = token.split(/\s/)[0];
+ if (tagName.indexOf("/") === 0 && tagNames[tagNames.length - 1] === tagName.substr(1)) {
+ // set tagName to the last tagName
+ // if there are no more tagNames, we'll rely on getTag.
+ tagName = tagNames[tagNames.length - 1];
+ popTagName = true;
+ } else {
+ tagNames.push(tagName);
+ }
+ }
+ content += token;
+ break;
+ }
+ } else {
+ // We have a start tag.
+ switch (token) {
+ case tmap.right:
+ case tmap.returnRight:
+ switch (startTag) {
+ case tmap.left:
+ // Get the number of `{ minus }`
+ bracketCount = bracketNum(content);
+
+ // We are ending a block.
+ if (bracketCount == 1) {
+
+ // We are starting on.
+ buff.push(insert_cmd, "can.view.txt(0,'" + getTag(tagName, tokens, i) + "'," + status() + ",this,function(){", startTxt, content);
+
+ endStack.push({
+ before: "",
+ after: finishTxt + "}));\n"
+ });
+ } else {
+
+ // How are we ending this statement?
+ last = // If the stack has value and we are ending a block...
+ endStack.length && bracketCount == -1 ? // Use the last item in the block stack.
+ endStack.pop() : // Or use the default ending.
+ {
+ after: ";"
+ };
+
+ // If we are ending a returning block,
+ // add the finish text which returns the result of the
+ // block.
+ if (last.before) {
+ buff.push(last.before);
+ }
+ // Add the remaining content.
+ buff.push(content, ";", last.after);
+ }
+ break;
+ case tmap.escapeLeft:
+ case tmap.returnLeft:
+ // We have an extra `{` -> `block`.
+ // Get the number of `{ minus }`.
+ bracketCount = bracketNum(content);
+ // If we have more `{`, it means there is a block.
+ if (bracketCount) {
+ // When we return to the same # of `{` vs `}` end with a `doubleParent`.
+ endStack.push({
+ before: finishTxt,
+ after: "}));"
+ });
+ }
+
+ var escaped = startTag === tmap.escapeLeft ? 1 : 0,
+ commands = {
+ insert: insert_cmd,
+ tagName: getTag(tagName, tokens, i),
+ status: status()
+ };
+
+ for (var ii = 0; ii < this.helpers.length; ii++) {
+ // Match the helper based on helper
+ // regex name value
+ var helper = this.helpers[ii];
+ if (helper.name.test(content)) {
+ content = helper.fn(content, commands);
+
+ // dont escape partials
+ if (helper.name.source == /^>[\s]*\w*/.source) {
+ escaped = 0;
+ }
+ break;
+ }
+ }
+
+ // Handle special cases
+ if (typeof content == 'object') {
+ if (content.raw) {
+ buff.push(content.raw);
+ }
+ } else {
+ // If we have `<%== a(function(){ %>` then we want
+ // `can.EJS.text(0,this, function(){ return a(function(){ var _v1ew = [];`.
+ buff.push(insert_cmd, "can.view.txt(" + escaped + ",'" + tagName + "'," + status() + ",this,function(){ " + (this.text.escape || '') + "return ", content,
+ // If we have a block.
+ bracketCount ?
+ // Start with startTxt `"var _v1ew = [];"`.
+ startTxt :
+ // If not, add `doubleParent` to close push and text.
+ "}));");
+ }
+
+ if (rescan && rescan.after && rescan.after.length) {
+ put(rescan.after.length);
+ rescan = null;
+ }
+ break;
+ }
+ startTag = null;
+ content = '';
+ break;
+ case tmap.templateLeft:
+ content += tmap.left;
+ break;
+ default:
+ content += token;
+ break;
+ }
+ }
+ lastToken = token;
+ }
+
+ // Put it together...
+ if (content.length) {
+ // Should be `content.dump` in Ruby.
+ put(content);
+ }
+ buff.push(";");
+
+ var template = buff.join(''),
+ out = {
+ out: 'with(_VIEW) { with (_CONTEXT) {' + template + " " + finishTxt + "}}"
+ };
+ // Use `eval` instead of creating a function, because it is easier to debug.
+ myEval.call(out, 'this.fn = (function(_CONTEXT,_VIEW){' + out.out + '});\r\n//@ sourceURL=' + name + ".js");
+
+ return out;
+ }
+ };
+
+ return Scanner;
+ })(__m16, __m19);
+
+ // ## view/node_lists.js
+ var __m22 = (function(can) {
+
+ // text node expando test
+ var canExpando = true;
+ try {
+ document.createTextNode('')._ = 0;
+ } catch (ex) {
+ canExpando = false;
+ }
+
+ // a mapping of element ids to nodeList ids
+ var nodeMap = {},
+ // a mapping of ids to text nodes
+ textNodeMap = {},
+ // a mapping of nodeList ids to nodeList
+ nodeListMap = {},
+ expando = "ejs_" + Math.random(),
+ _id = 0,
+ id = function(node) {
+ if (canExpando || node.nodeType !== 3) {
+ if (node[expando]) {
+ return node[expando];
+ } else {
+ return node[expando] = (node.nodeName ? "element_" : "obj_") + (++_id);
+ }
+ } else {
+ for (var textNodeID in textNodeMap) {
+ if (textNodeMap[textNodeID] === node) {
+ return textNodeID;
+ }
+ }
+
+ textNodeMap["text_" + (++_id)] = node;
+ return "text_" + _id;
+ }
+ },
+ // removes a nodeListId from a node's nodeListIds
+ removeNodeListId = function(node, nodeListId) {
+ var nodeListIds = nodeMap[id(node)];
+ if (nodeListIds) {
+ var index = can.inArray(nodeListId, nodeListIds);
+
+ if (index >= 0) {
+ nodeListIds.splice(index, 1);
+ }
+ if (!nodeListIds.length) {
+ delete nodeMap[id(node)];
+ }
+ }
+ },
+ addNodeListId = function(node, nodeListId) {
+ var nodeListIds = nodeMap[id(node)];
+ if (!nodeListIds) {
+ nodeListIds = nodeMap[id(node)] = [];
+ }
+ nodeListIds.push(nodeListId);
+ };
+
+ var nodeLists = {
+ id: id,
+ // replaces the contents of one node list with the nodes in another list
+ replace: function(oldNodeList, newNodes) {
+ // for each node in the node list
+ oldNodeList = can.makeArray(oldNodeList);
+
+ // try every set
+ //can.each( oldNodeList, function(node){
+ var node = oldNodeList[0]
+ // for each nodeList the node is in
+ can.each(can.makeArray(nodeMap[id(node)]), function(nodeListId) {
+
+ // if startNode to endNode is
+ // within list, replace that list
+ // I think the problem is not the WHOLE part is being
+ // matched
+ var nodeList = nodeListMap[nodeListId],
+ startIndex = can.inArray(node, nodeList),
+ endIndex = can.inArray(oldNodeList[oldNodeList.length - 1], nodeList);
+
+
+ // remove this nodeListId from each node
+ if (startIndex >= 0 && endIndex >= 0) {
+ for (var i = startIndex; i <= endIndex; i++) {
+ var n = nodeList[i];
+ removeNodeListId(n, nodeListId);
+ }
+ // swap in new nodes into the nodeLIst
+ nodeList.splice.apply(nodeList, [startIndex, endIndex - startIndex + 1].concat(newNodes));
+
+ // tell these new nodes they belong to the nodeList
+ can.each(newNodes, function(node) {
+ addNodeListId(node, nodeListId);
+ });
+ } else {
+ nodeLists.unregister(nodeList);
+ }
+ });
+ //});
+ },
+ // registers a list of nodes
+ register: function(nodeList) {
+ var nLId = id(nodeList);
+ nodeListMap[nLId] = nodeList;
+
+ can.each(nodeList, function(node) {
+ addNodeListId(node, nLId);
+ });
+
+ },
+ // removes mappings
+ unregister: function(nodeList) {
+ var nLId = id(nodeList);
+ can.each(nodeList, function(node) {
+ removeNodeListId(node, nLId);
+ });
+ delete nodeListMap[nLId];
+ },
+ nodeMap: nodeMap,
+ nodeListMap: nodeListMap
+ }
+ var ids = function(nodeList) {
+ return nodeList.map(function(n) {
+ return id(n) + ":" + (n.innerHTML || n.nodeValue)
+ })
+ }
+ return nodeLists;
+
+ })(__m3);
+
+ // ## view/live.js
+ var __m21 = (function(can, elements, view, nodeLists) {
+ // ## live.js
+ // The live module provides live binding for computes
+ // and can.Observe.List.
+ // Currently, it's API is designed for `can/view/render`, but
+ // it could easily be used for other purposes.
+
+ // ### Helper methods
+ // #### setup
+ // `setup(HTMLElement, bind(data), unbind(data)) -> data`
+ // Calls bind right away, but will call unbind
+ // if the element is "destroyed" (removed from the DOM).
+ var setup = function(el, bind, unbind) {
+ var teardown = function() {
+ unbind(data)
+ can.unbind.call(el, 'destroyed', teardown);
+ },
+ data = {
+ teardownCheck: function(parent) {
+ if (!parent) {
+ teardown();
+ }
+ }
+ }
+
+ can.bind.call(el, 'destroyed', teardown);
+ bind(data)
+ return data;
+ },
+ // #### listen
+ // Calls setup, but presets bind and unbind to
+ // operate on a compute
+ listen = function(el, compute, change) {
+ return setup(el, function() {
+ compute.bind("change", change);
+ }, function(data) {
+ compute.unbind("change", change);
+ if (data.nodeList) {
+ nodeLists.unregister(data.nodeList);
+ }
+ });
+ },
+ // #### getAttributeParts
+ // Breaks up a string like foo='bar' into ["foo","'bar'""]
+ getAttributeParts = function(newVal) {
+ return (newVal || "").replace(/['"]/g, '').split('=')
+ }
+ // #### insertElementsAfter
+ // Appends elements after the last item in oldElements.
+ insertElementsAfter = function(oldElements, newFrag) {
+ var last = oldElements[oldElements.length - 1];
+
+ // Insert it in the `document` or `documentFragment`
+ if (last.nextSibling) {
+ last.parentNode.insertBefore(newFrag, last.nextSibling);
+ } else {
+ last.parentNode.appendChild(newFrag);
+ }
+ };
+
+ var live = {
+ nodeLists: nodeLists,
+ list: function(el, list, func, context, parentNode) {
+ // A mapping of the index to an array
+ // of elements that represent the item.
+ // Each array is registered so child or parent
+ // live structures can update the elements
+ var nodesMap = [],
+
+ add = function(ev, items, index) {
+
+ // Collect new html and mappings
+ var frag = document.createDocumentFragment(),
+ newMappings = [];
+ can.each(items, function(item) {
+ var itemHTML = func.call(context, item),
+ itemFrag = can.view.frag(itemHTML, parentNode);
+
+ newMappings.push(can.makeArray(itemFrag.childNodes));
+ frag.appendChild(itemFrag);
+ })
+
+ // Inserting at the end of the list
+ if (!nodesMap[index]) {
+ insertElementsAfter(
+ index == 0 ? [text] :
+ nodesMap[index - 1], frag)
+ } else {
+ var el = nodesMap[index][0];
+ el.parentNode.insertBefore(frag, el)
+ }
+ // register each item
+ can.each(newMappings, function(nodeList) {
+ nodeLists.register(nodeList)
+ });
+ [].splice.apply(nodesMap, [index, 0].concat(newMappings));
+ },
+ remove = function(ev, items, index) {
+ var removedMappings = nodesMap.splice(index, items.length),
+ itemsToRemove = [];
+
+ can.each(removedMappings, function(nodeList) {
+ // add items that we will remove all at once
+ [].push.apply(itemsToRemove, nodeList)
+ // Update any parent lists to remove these items
+ nodeLists.replace(nodeList, []);
+ // unregister the list
+ nodeLists.unregister(nodeList);
+
+ });
+ can.remove(can.$(itemsToRemove));
+ },
+ parentNode = elements.getParentNode(el, parentNode),
+ text = document.createTextNode("");
+
+ // Setup binding and teardown to add and remove events
+ setup(parentNode, function() {
+ list.bind("add", add).bind("remove", remove)
+ }, function() {
+ list.unbind("add", add).unbind("remove", remove);
+ can.each(nodesMap, function(nodeList) {
+ nodeLists.unregister(nodeList);
+ })
+ })
+
+ insertElementsAfter([el], text);
+ can.remove(can.$(el));
+ add({}, list, 0);
+
+ },
+ html: function(el, compute, parentNode) {
+ var parentNode = elements.getParentNode(el, parentNode),
+
+ data = listen(parentNode, compute, function(ev, newVal, oldVal) {
+ var attached = nodes[0].parentNode;
+ // update the nodes in the DOM with the new rendered value
+ if (attached) {
+ makeAndPut(newVal);
+ }
+ data.teardownCheck(nodes[0].parentNode);
+ });
+
+ var nodes,
+ makeAndPut = function(val) {
+ // create the fragment, but don't hook it up
+ // we need to insert it into the document first
+ var frag = can.view.frag(val, parentNode),
+ // keep a reference to each node
+ newNodes = can.makeArray(frag.childNodes);
+ // Insert it in the `document` or `documentFragment`
+ insertElementsAfter(nodes || [el], frag)
+ // nodes hasn't been set yet
+ if (!nodes) {
+ can.remove(can.$(el));
+ nodes = newNodes;
+ // set the teardown nodeList
+ data.nodeList = nodes;
+ nodeLists.register(nodes);
+ } else {
+ // Update node Array's to point to new nodes
+ // and then remove the old nodes.
+ // It has to be in this order for Mootools
+ // and IE because somehow, after an element
+ // is removed from the DOM, it loses its
+ // expando values.
+ var nodesToRemove = can.makeArray(nodes);
+ nodeLists.replace(nodes, newNodes);
+ can.remove(can.$(nodesToRemove));
+ }
+ };
+ makeAndPut(compute(), [el]);
+
+ },
+ text: function(el, compute, parentNode) {
+ var parent = elements.getParentNode(el, parentNode);
+
+ // setup listening right away so we don't have to re-calculate value
+ var data = listen(el.parentNode !== parent ? el.parentNode : parent, compute, function(ev, newVal, oldVal) {
+ // Sometimes this is 'unknown' in IE and will throw an exception if it is
+ if (typeof node.nodeValue != 'unknown') {
+ node.nodeValue = "" + newVal;
+ }
+ data.teardownCheck(node.parentNode);
+ });
+
+ var node = document.createTextNode(compute());
+
+ if (el.parentNode !== parent) {
+ parent = el.parentNode;
+ parent.insertBefore(node, el);
+ parent.removeChild(el);
+ } else {
+ parent.insertBefore(node, el);
+ parent.removeChild(el);
+ }
+ },
+ attributes: function(el, compute, currentValue) {
+ var setAttrs = function(newVal) {
+ var parts = getAttributeParts(newVal),
+ newAttrName = parts.shift();
+
+ // Remove if we have a change and used to have an `attrName`.
+ if ((newAttrName != attrName) && attrName) {
+ elements.removeAttr(el, attrName);
+ }
+ // Set if we have a new `attrName`.
+ if (newAttrName) {
+ elements.setAttr(el, newAttrName, parts.join('='));
+ attrName = newAttrName;
+ }
+ }
+
+ listen(el, compute, function(ev, newVal) {
+ setAttrs(newVal)
+ })
+ // current value has been set
+ if (arguments.length >= 3) {
+ var attrName = getAttributeParts(currentValue)[0]
+ } else {
+ setAttrs(compute())
+ }
+ },
+ attributePlaceholder: '__!!__',
+ attributeReplace: /__!!__/g,
+ attribute: function(el, attributeName, compute) {
+ listen(el, compute, function(ev, newVal) {
+ elements.setAttr(el, attributeName, hook.render());
+ })
+
+ var wrapped = can.$(el),
+ hooks;
+
+ // Get the list of hookups or create one for this element.
+ // Hooks is a map of attribute names to hookup `data`s.
+ // Each hookup data has:
+ // `render` - A `function` to render the value of the attribute.
+ // `funcs` - A list of hookup `function`s on that attribute.
+ // `batchNum` - The last event `batchNum`, used for performance.
+ hooks = can.data(wrapped, 'hooks');
+ if (!hooks) {
+ can.data(wrapped, 'hooks', hooks = {});
+ }
+
+ // Get the attribute value.
+ var attr = elements.getAttr(el, attributeName),
+ // Split the attribute value by the template.
+ // Only split out the first __!!__ so if we have multiple hookups in the same attribute,
+ // they will be put in the right spot on first render
+ parts = attr.split(live.attributePlaceholder),
+ goodParts = [],
+ hook;
+ goodParts.push(parts.shift(),
+ parts.join(live.attributePlaceholder));
+
+ // If we already had a hookup for this attribute...
+ if (hooks[attributeName]) {
+ // Just add to that attribute's list of `function`s.
+ hooks[attributeName].computes.push(compute);
+ } else {
+ // Create the hookup data.
+ hooks[attributeName] = {
+ render: function() {
+ var i = 0,
+ // attr doesn't have a value in IE
+ newAttr = attr ? attr.replace(live.attributeReplace, function() {
+ return elements.contentText(hook.computes[i++]());
+ }) : elements.contentText(hook.computes[i++]());
+ return newAttr;
+ },
+ computes: [compute],
+ batchNum: undefined
+ };
+ }
+
+ // Save the hook for slightly faster performance.
+ hook = hooks[attributeName];
+
+ // Insert the value in parts.
+ goodParts.splice(1, 0, compute());
+
+ // Set the attribute.
+ elements.setAttr(el, attributeName, goodParts.join(""));
+
+ }
+ }
+ return live;
+
+ })(__m3, __m19, __m16, __m22);
+
+ // ## view/render.js
+ var __m20 = (function(can, elements, live) {
+
+ var pendingHookups = [],
+ tagChildren = function(tagName) {
+ var newTag = elements.tagMap[tagName] || "span";
+ if (newTag === "span") {
+ //innerHTML in IE doesn't honor leading whitespace after empty elements
+ return "@@!!@@";
+ }
+ return "<" + newTag + ">" + tagChildren(newTag) + "</" + newTag + ">";
+ },
+ contentText = function(input, tag) {
+
+ // If it's a string, return.
+ if (typeof input == 'string') {
+ return input;
+ }
+ // If has no value, return an empty string.
+ if (!input && input !== 0) {
+ return '';
+ }
+
+ // If it's an object, and it has a hookup method.
+ var hook = (input.hookup &&
+
+ // Make a function call the hookup method.
+
+ function(el, id) {
+ input.hookup.call(input, el, id);
+ }) ||
+
+ // Or if it's a `function`, just use the input.
+ (typeof input == 'function' && input);
+
+ // Finally, if there is a `function` to hookup on some dom,
+ // add it to pending hookups.
+ if (hook) {
+ if (tag) {
+ return "<" + tag + " " + can.view.hook(hook) + "></" + tag + ">"
+ } else {
+ pendingHookups.push(hook);
+ }
+
+ return '';
+ }
+
+ // Finally, if all else is `false`, `toString()` it.
+ return "" + input;
+ },
+ // Returns escaped/sanatized content for anything other than a live-binding
+ contentEscape = function(txt) {
+ return (typeof txt == 'string' || typeof txt == 'number') ?
+ can.esc(txt) :
+ contentText(txt);
+ };
+
+ var current;
+
+ can.extend(can.view, {
+ live: live,
+ setupLists: function() {
+
+ var old = can.view.lists,
+ data;
+
+ can.view.lists = function(list, renderer) {
+ data = {
+ list: list,
+ renderer: renderer
+ }
+ }
+ return function() {
+ can.view.lists = old;
+ return data;
+ }
+ },
+ pending: function() {
+ // TODO, make this only run for the right tagName
+ var hooks = pendingHookups.slice(0);
+ lastHookups = hooks;
+ pendingHookups = [];
+ return can.view.hook(function(el) {
+ can.each(hooks, function(fn) {
+ fn(el);
+ });
+ });
+ },
+
+
+ txt: function(escape, tagName, status, self, func) {
+ var listTeardown = can.view.setupLists(),
+ emptyHandler = function() {},
+ unbind = function() {
+ compute.unbind("change", emptyHandler)
+ };
+
+ var compute = can.compute(func, self, false);
+ // bind to get and temporarily cache the value
+ compute.bind("change", emptyHandler);
+ // call the "wrapping" function and get the binding information
+ var tag = (elements.tagMap[tagName] || "span"),
+ listData = listTeardown(),
+ value = compute();
+
+
+ if (listData) {
+ return "<" + tag + can.view.hook(function(el, parentNode) {
+ live.list(el, listData.list, listData.renderer, self, parentNode);
+ }) + "></" + tag + ">";
+ }
+
+ // If we had no observes just return the value returned by func.
+ if (!compute.hasDependencies) {
+ unbind();
+ return (escape || status !== 0 ? contentEscape : contentText)(value, status === 0 && tag);
+ }
+
+ // the property (instead of innerHTML elements) to adjust. For
+ // example options should use textContent
+ var contentProp = elements.tagToContentPropMap[tagName];
+
+
+ // The magic tag is outside or between tags.
+ if (status === 0 && !contentProp) {
+ // Return an element tag with a hookup in place of the content
+ return "<" + tag + can.view.hook(
+ escape ?
+ // If we are escaping, replace the parentNode with
+ // a text node who's value is `func`'s return value.
+
+ function(el, parentNode) {
+ live.text(el, compute, parentNode);
+ unbind();
+ } :
+ // If we are not escaping, replace the parentNode with a
+ // documentFragment created as with `func`'s return value.
+
+ function(el, parentNode) {
+ live.html(el, compute, parentNode);
+ unbind();
+ //children have to be properly nested HTML for buildFragment to work properly
+ }) + ">" + tagChildren(tag) + "</" + tag + ">";
+ // In a tag, but not in an attribute
+ } else if (status === 1) {
+ // remember the old attr name
+ pendingHookups.push(function(el) {
+ live.attributes(el, compute, compute());
+ unbind();
+ });
+ return compute();
+ } else { // In an attribute...
+ var attributeName = status === 0 ? contentProp : status;
+ // if the magic tag is inside the element, like `<option><% TAG %></option>`,
+ // we add this hookup to the last element (ex: `option`'s) hookups.
+ // Otherwise, the magic tag is in an attribute, just add to the current element's
+ // hookups.
+ (status === 0 ? lastHookups : pendingHookups).push(function(el) {
+ live.attribute(el, attributeName, compute);
+ unbind();
+ });
+ return live.attributePlaceholder;
+ }
+ }
+ });
+
+ return can;
+ })(__m16, __m19, __m21, __m2);
+
+ // ## view/ejs/ejs.js
+ var __m17 = (function(can) {
+ // ## ejs.js
+ // `can.EJS`
+ // _Embedded JavaScript Templates._
+
+ // Helper methods.
+ var extend = can.extend,
+ EJS = function(options) {
+ // Supports calling EJS without the constructor
+ // This returns a function that renders the template.
+ if (this.constructor != EJS) {
+ var ejs = new EJS(options);
+ return function(data, helpers) {
+ return ejs.render(data, helpers);
+ };
+ }
+ // If we get a `function` directly, it probably is coming from
+ // a `steal`-packaged view.
+ if (typeof options == "function") {
+ this.template = {
+ fn: options
+ };
+ return;
+ }
+ // Set options on self.
+ extend(this, options);
+ this.template = this.scanner.scan(this.text, this.name);
+ };
+
+ can.EJS = EJS;
+
+
+ EJS.prototype.
+
+ render = function(object, extraHelpers) {
+ object = object || {};
+ return this.template.fn.call(object, object, new EJS.Helpers(object, extraHelpers || {}));
+ };
+
+ extend(EJS.prototype, {
+
+ scanner: new can.view.Scanner({
+
+ tokens: [
+ ["templateLeft", "<%%"], // Template
+ ["templateRight", "%>"], // Right Template
+ ["returnLeft", "<%=="], // Return Unescaped
+ ["escapeLeft", "<%="], // Return Escaped
+ ["commentLeft", "<%#"], // Comment
+ ["left", "<%"], // Run --- this is hack for now
+ ["right", "%>"], // Right -> All have same FOR Mustache ...
+ ["returnRight", "%>"]
+ ],
+
+
+ transform: function(source) {
+ return source.replace(/<%([\s\S]+?)%>/gm, function(whole, part) {
+ var brackets = [],
+ foundBracketPair,
+ i;
+
+ // Look for brackets (for removing self-contained blocks)
+ part.replace(/[{}]/gm, function(bracket, offset) {
+ brackets.push([bracket, offset]);
+ });
+
+ // Remove bracket pairs from the list of replacements
+ do {
+ foundBracketPair = false;
+ for (i = brackets.length - 2; i >= 0; i--) {
+ if (brackets[i][0] == '{' && brackets[i + 1][0] == '}') {
+ brackets.splice(i, 2);
+ foundBracketPair = true;
+ break;
+ }
+ }
+ } while (foundBracketPair);
+
+ // Unmatched brackets found, inject EJS tags
+ if (brackets.length >= 2) {
+ var result = ['<%'],
+ bracket,
+ last = 0;
+ for (i = 0; bracket = brackets[i]; i++) {
+ result.push(part.substring(last, last = bracket[1]));
+ if ((bracket[0] == '{' && i < brackets.length - 1) || (bracket[0] == '}' && i > 0)) {
+ result.push(bracket[0] == '{' ? '{ %><% ' : ' %><% }');
+ } else {
+ result.push(bracket[0]);
+ }
+ ++last;
+ }
+ result.push(part.substring(last), '%>');
+ return result.join('');
+ }
+ // Otherwise return the original
+ else {
+ return '<%' + part + '%>';
+ }
+ });
+ }
+ })
+ });
+
+ EJS.Helpers = function(data, extras) {
+ this._data = data;
+ this._extras = extras;
+ extend(this, extras);
+ };
+
+
+ EJS.Helpers.prototype = {
+ // TODO Deprecated!!
+ list: function(list, cb) {
+
+ can.each(list, function(item, i) {
+ cb(item, i, list)
+ })
+ },
+ each: function(list, cb) {
+ // Normal arrays don't get live updated
+ if (can.isArray(list)) {
+ this.list(list, cb);
+ } else {
+ can.view.lists(list, cb);
+ }
+ }
+ };
+
+ // Options for `steal`'s build.
+ can.view.register({
+ suffix: "ejs",
+ // returns a `function` that renders the view.
+ script: function(id, src) {
+ return "can.EJS(function(_CONTEXT,_VIEW) { " + new EJS({
+ text: src,
+ name: id
+ }).template.out + " })";
+ },
+ renderer: function(id, text) {
+ return EJS({
+ text: text,
+ name: id
+ });
+ }
+ });
+
+ return can;
+ })(__m3, __m16, __m2, __m14, __m18, __m20);
+
+ // ## control/control.js
+ var __m23 = (function(can) {
+ // ## control.js
+ // `can.Control`
+ // _Controller_
+
+ // Binds an element, returns a function that unbinds.
+ var bind = function(el, ev, callback) {
+
+ can.bind.call(el, ev, callback);
+
+ return function() {
+ can.unbind.call(el, ev, callback);
+ };
+ },
+ isFunction = can.isFunction,
+ extend = can.extend,
+ each = can.each,
+ slice = [].slice,
+ paramReplacer = /\{([^\}]+)\}/g,
+ special = can.getObject("$.event.special", [can]) || {},
+
+ // Binds an element, returns a function that unbinds.
+ delegate = function(el, selector, ev, callback) {
+ can.delegate.call(el, selector, ev, callback);
+ return function() {
+ can.undelegate.call(el, selector, ev, callback);
+ };
+ },
+
+ // Calls bind or unbind depending if there is a selector.
+ binder = function(el, ev, callback, selector) {
+ return selector ?
+ delegate(el, can.trim(selector), ev, callback) :
+ bind(el, ev, callback);
+ },
+
+ basicProcessor;
+
+ var Control = can.Control = can.Construct(
+
+ {
+ // Setup pre-processes which methods are event listeners.
+
+ setup: function() {
+
+ // Allow contollers to inherit "defaults" from super-classes as it
+ // done in `can.Construct`
+ can.Construct.setup.apply(this, arguments);
+
+ // If you didn't provide a name, or are `control`, don't do anything.
+ if (can.Control) {
+
+ // Cache the underscored names.
+ var control = this,
+ funcName;
+
+ // Calculate and cache actions.
+ control.actions = {};
+ for (funcName in control.prototype) {
+ if (control._isAction(funcName)) {
+ control.actions[funcName] = control._action(funcName);
+ }
+ }
+ }
+ },
+
+ // Moves `this` to the first argument, wraps it with `jQuery` if it's an element
+ _shifter: function(context, name) {
+
+ var method = typeof name == "string" ? context[name] : name;
+
+ if (!isFunction(method)) {
+ method = context[method];
+ }
+
+ return function() {
+ context.called = name;
+ return method.apply(context, [this.nodeName ? can.$(this) : this].concat(slice.call(arguments, 0)));
+ };
+ },
+
+ // Return `true` if is an action.
+
+ _isAction: function(methodName) {
+
+ var val = this.prototype[methodName],
+ type = typeof val;
+ // if not the constructor
+ return (methodName !== 'constructor') &&
+ // and is a function or links to a function
+ (type == "function" || (type == "string" && isFunction(this.prototype[val]))) &&
+ // and is in special, a processor, or has a funny character
+ !! (special[methodName] || processors[methodName] || /[^\w]/.test(methodName));
+ },
+ // Takes a method name and the options passed to a control
+ // and tries to return the data necessary to pass to a processor
+ // (something that binds things).
+
+ _action: function(methodName, options) {
+
+ // If we don't have options (a `control` instance), we'll run this
+ // later.
+ paramReplacer.lastIndex = 0;
+ if (options || !paramReplacer.test(methodName)) {
+ // If we have options, run sub to replace templates `{}` with a
+ // value from the options or the window
+ var convertedName = options ? can.sub(methodName, [options, window]) : methodName;
+ if (!convertedName) {
+ return null;
+ }
+ // If a `{}` template resolves to an object, `convertedName` will be
+ // an array
+ var arr = can.isArray(convertedName),
+
+ // Get the name
+ name = arr ? convertedName[1] : convertedName,
+
+ // Grab the event off the end
+ parts = name.split(/\s+/g),
+ event = parts.pop();
+
+ return {
+ processor: processors[event] || basicProcessor,
+ parts: [name, parts.join(" "), event],
+ delegate: arr ? convertedName[0] : undefined
+ };
+ }
+ },
+ // An object of `{eventName : function}` pairs that Control uses to
+ // hook up events auto-magically.
+
+ processors: {},
+ // A object of name-value pairs that act as default values for a
+ // control instance
+ defaults: {}
+
+ }, {
+
+ // Sets `this.element`, saves the control in `data, binds event
+ // handlers.
+
+ setup: function(element, options) {
+
+ var cls = this.constructor,
+ pluginname = cls.pluginName || cls._fullName,
+ arr;
+
+ // Want the raw element here.
+ this.element = can.$(element)
+
+ if (pluginname && pluginname !== 'can_control') {
+ // Set element and `className` on element.
+ this.element.addClass(pluginname);
+ }
+
+ (arr = can.data(this.element, "controls")) || can.data(this.element, "controls", arr = []);
+ arr.push(this);
+
+ // Option merging.
+
+ this.options = extend({}, cls.defaults, options);
+
+ // Bind all event handlers.
+ this.on();
+
+ // Gets passed into `init`.
+
+ return [this.element, this.options];
+ },
+
+ on: function(el, selector, eventName, func) {
+ if (!el) {
+
+ // Adds bindings.
+ this.off();
+
+ // Go through the cached list of actions and use the processor
+ // to bind
+ var cls = this.constructor,
+ bindings = this._bindings,
+ actions = cls.actions,
+ element = this.element,
+ destroyCB = can.Control._shifter(this, "destroy"),
+ funcName, ready;
+
+ for (funcName in actions) {
+ // Only push if we have the action and no option is `undefined`
+ if (actions.hasOwnProperty(funcName) &&
+ (ready = actions[funcName] || cls._action(funcName, this.options))) {
+ bindings.push(ready.processor(ready.delegate || element,
+ ready.parts[2], ready.parts[1], funcName, this));
+ }
+ }
+
+
+ // Setup to be destroyed...
+ // don't bind because we don't want to remove it.
+ can.bind.call(element, "destroyed", destroyCB);
+ bindings.push(function(el) {
+ can.unbind.call(el, "destroyed", destroyCB);
+ });
+ return bindings.length;
+ }
+
+ if (typeof el == 'string') {
+ func = eventName;
+ eventName = selector;
+ selector = el;
+ el = this.element;
+ }
+
+ if (func === undefined) {
+ func = eventName;
+ eventName = selector;
+ selector = null;
+ }
+
+ if (typeof func == 'string') {
+ func = can.Control._shifter(this, func);
+ }
+
+ this._bindings.push(binder(el, eventName, func, selector));
+
+ return this._bindings.length;
+ },
+ // Unbinds all event handlers on the controller.
+
+ off: function() {
+ var el = this.element[0]
+ each(this._bindings || [], function(value) {
+ value(el);
+ });
+ // Adds bindings.
+ this._bindings = [];
+ },
+ // Prepares a `control` for garbage collection
+
+ destroy: function() {
+ //Control already destroyed
+ if (this.element === null) {
+
+ return;
+ }
+ var Class = this.constructor,
+ pluginName = Class.pluginName || Class._fullName,
+ controls;
+
+ // Unbind bindings.
+ this.off();
+
+ if (pluginName && pluginName !== 'can_control') {
+ // Remove the `className`.
+ this.element.removeClass(pluginName);
+ }
+
+ // Remove from `data`.
+ controls = can.data(this.element, "controls");
+ controls.splice(can.inArray(this, controls), 1);
+
+ can.trigger(this, "destroyed"); // In case we want to know if the `control` is removed.
+
+ this.element = null;
+ }
+ });
+
+ var processors = can.Control.processors,
+ // Processors do the binding.
+ // They return a function that unbinds when called.
+ // The basic processor that binds events.
+ basicProcessor = function(el, event, selector, methodName, control) {
+ return binder(el, event, can.Control._shifter(control, methodName), selector);
+ };
+
+ // Set common events to be processed as a `basicProcessor`
+ each(["change", "click", "contextmenu", "dblclick", "keydown", "keyup",
+ "keypress", "mousedown", "mousemove", "mouseout", "mouseover",
+ "mouseup", "reset", "resize", "scroll", "select", "submit", "focusin",
+ "focusout", "mouseenter", "mouseleave",
+ // #104 - Add touch events as default processors
+ // TOOD feature detect?
+ "touchstart", "touchmove", "touchcancel", "touchend", "touchleave"
+ ], function(v) {
+ processors[v] = basicProcessor;
+ });
+
+ return Control;
+ })(__m3, __m1);
+
+ // ## util/string/deparam/deparam.js
+ var __m25 = (function(can) {
+
+ // ## deparam.js
+ // `can.deparam`
+ // _Takes a string of name value pairs and returns a Object literal that represents those params._
+ var digitTest = /^\d+$/,
+ keyBreaker = /([^\[\]]+)|(\[\])/g,
+ paramTest = /([^?#]*)(#.*)?$/,
+ prep = function(str) {
+ return decodeURIComponent(str.replace(/\+/g, " "));
+ };
+
+
+ can.extend(can, {
+
+ deparam: function(params) {
+
+ var data = {},
+ pairs, lastPart;
+
+ if (params && paramTest.test(params)) {
+
+ pairs = params.split('&'),
+
+ can.each(pairs, function(pair) {
+
+ var parts = pair.split('='),
+ key = prep(parts.shift()),
+ value = prep(parts.join("=")),
+ current = data;
+
+ if (key) {
+ parts = key.match(keyBreaker);
+
+ for (var j = 0, l = parts.length - 1; j < l; j++) {
+ if (!current[parts[j]]) {
+ // If what we are pointing to looks like an `array`
+ current[parts[j]] = digitTest.test(parts[j + 1]) || parts[j + 1] == "[]" ? [] : {};
+ }
+ current = current[parts[j]];
+ }
+ lastPart = parts.pop();
+ if (lastPart == "[]") {
+ current.push(value);
+ } else {
+ current[lastPart] = value;
+ }
+ }
+ });
+ }
+ return data;
+ }
+ });
+ return can;
+ })(__m3, __m2);
+
+ // ## route/route.js
+ var __m24 = (function(can) {
+
+ // ## route.js
+ // `can.route`
+ // _Helps manage browser history (and client state) by synchronizing the
+ // `window.location.hash` with a `can.Observe`._
+ // Helper methods used for matching routes.
+ var
+ // `RegExp` used to match route variables of the type ':name'.
+ // Any word character or a period is matched.
+ matcher = /\:([\w\.]+)/g,
+ // Regular expression for identifying &key=value lists.
+ paramsMatcher = /^(?:&[^=]+=[^&]*)+/,
+ // Converts a JS Object into a list of parameters that can be
+ // inserted into an html element tag.
+ makeProps = function(props) {
+ var tags = [];
+ can.each(props, function(val, name) {
+ tags.push((name === 'className' ? 'class' : name) + '="' +
+ (name === "href" ? val : can.esc(val)) + '"');
+ });
+ return tags.join(" ");
+ },
+ // Checks if a route matches the data provided. If any route variable
+ // is not present in the data, the route does not match. If all route
+ // variables are present in the data, the number of matches is returned
+ // to allow discerning between general and more specific routes.
+ matchesData = function(route, data) {
+ var count = 0,
+ i = 0,
+ defaults = {};
+ // look at default values, if they match ...
+ for (var name in route.defaults) {
+ if (route.defaults[name] === data[name]) {
+ // mark as matched
+ defaults[name] = 1;
+ count++;
+ }
+ }
+ for (; i < route.names.length; i++) {
+ if (!data.hasOwnProperty(route.names[i])) {
+ return -1;
+ }
+ if (!defaults[route.names[i]]) {
+ count++;
+ }
+
+ }
+
+ return count;
+ },
+ onready = !0,
+ location = window.location,
+ wrapQuote = function(str) {
+ return (str + '').replace(/([.?*+\^$\[\]\\(){}|\-])/g, "\\$1");
+ },
+ each = can.each,
+ extend = can.extend;
+
+ can.route = function(url, defaults) {
+ defaults = defaults || {};
+ // Extract the variable names and replace with `RegExp` that will match
+ // an atual URL with values.
+ var names = [],
+ test = url.replace(matcher, function(whole, name, i) {
+ names.push(name);
+ var next = "\\" + (url.substr(i + whole.length, 1) || can.route._querySeparator);
+ // a name without a default value HAS to have a value
+ // a name that has a default value can be empty
+ // The `\\` is for string-escaping giving single `\` for `RegExp` escaping.
+ return "([^" + next + "]" + (defaults[name] ? "*" : "+") + ")";
+ });
+
+ // Add route in a form that can be easily figured out.
+ can.route.routes[url] = {
+ // A regular expression that will match the route when variable values
+ // are present; i.e. for `:page/:type` the `RegExp` is `/([\w\.]*)/([\w\.]*)/` which
+ // will match for any value of `:page` and `:type` (word chars or period).
+ test: new RegExp("^" + test + "($|" + wrapQuote(can.route._querySeparator) + ")"),
+ // The original URL, same as the index for this entry in routes.
+ route: url,
+ // An `array` of all the variable names in this route.
+ names: names,
+ // Default values provided for the variables.
+ defaults: defaults,
+ // The number of parts in the URL separated by `/`.
+ length: url.split('/').length
+ };
+ return can.route;
+ };
+
+
+ extend(can.route, {
+
+ _querySeparator: '&',
+ _paramsMatcher: paramsMatcher,
+
+
+ param: function(data, _setRoute) {
+ // Check if the provided data keys match the names in any routes;
+ // Get the one with the most matches.
+ var route,
+ // Need to have at least 1 match.
+ matches = 0,
+ matchCount,
+ routeName = data.route,
+ propCount = 0;
+
+ delete data.route;
+
+ each(data, function() {
+ propCount++;
+ });
+ // Otherwise find route.
+ each(can.route.routes, function(temp, name) {
+ // best route is the first with all defaults matching
+
+
+ matchCount = matchesData(temp, data);
+ if (matchCount > matches) {
+ route = temp;
+ matches = matchCount;
+ }
+ if (matchCount >= propCount) {
+ return false;
+ }
+ });
+ // If we have a route name in our `can.route` data, and it's
+ // just as good as what currently matches, use that
+ if (can.route.routes[routeName] && matchesData(can.route.routes[routeName], data) === matches) {
+ route = can.route.routes[routeName];
+ }
+ // If this is match...
+ if (route) {
+ var cpy = extend({}, data),
+ // Create the url by replacing the var names with the provided data.
+ // If the default value is found an empty string is inserted.
+ res = route.route.replace(matcher, function(whole, name) {
+ delete cpy[name];
+ return data[name] === route.defaults[name] ? "" : encodeURIComponent(data[name]);
+ }),
+ after;
+ // Remove matching default values
+ each(route.defaults, function(val, name) {
+ if (cpy[name] === val) {
+ delete cpy[name];
+ }
+ });
+
+ // The remaining elements of data are added as
+ // `&` separated parameters to the url.
+ after = can.param(cpy);
+ // if we are paraming for setting the hash
+ // we also want to make sure the route value is updated
+ if (_setRoute) {
+ can.route.attr('route', route.route);
+ }
+ return res + (after ? can.route._querySeparator + after : "");
+ }
+ // If no route was found, there is no hash URL, only paramters.
+ return can.isEmptyObject(data) ? "" : can.route._querySeparator + can.param(data);
+ },
+
+ deparam: function(url) {
+ // See if the url matches any routes by testing it against the `route.test` `RegExp`.
+ // By comparing the URL length the most specialized route that matches is used.
+ var route = {
+ length: -1
+ };
+ each(can.route.routes, function(temp, name) {
+ if (temp.test.test(url) && temp.length > route.length) {
+ route = temp;
+ }
+ });
+ // If a route was matched.
+ if (route.length > -1) {
+
+ var // Since `RegExp` backreferences are used in `route.test` (parens)
+ // the parts will contain the full matched string and each variable (back-referenced) value.
+ parts = url.match(route.test),
+ // Start will contain the full matched string; parts contain the variable values.
+ start = parts.shift(),
+ // The remainder will be the `&key=value` list at the end of the URL.
+ remainder = url.substr(start.length - (parts[parts.length - 1] === can.route._querySeparator ? 1 : 0)),
+ // If there is a remainder and it contains a `&key=value` list deparam it.
+ obj = (remainder && can.route._paramsMatcher.test(remainder)) ? can.deparam(remainder.slice(1)) : {};
+
+ // Add the default values for this route.
+ obj = extend(true, {}, route.defaults, obj);
+ // Overwrite each of the default values in `obj` with those in
+ // parts if that part is not empty.
+ each(parts, function(part, i) {
+ if (part && part !== can.route._querySeparator) {
+ obj[route.names[i]] = decodeURIComponent(part);
+ }
+ });
+ obj.route = route.route;
+ return obj;
+ }
+ // If no route was matched, it is parsed as a `&key=value` list.
+ if (url.charAt(0) !== can.route._querySeparator) {
+ url = can.route._querySeparator + url;
+ }
+ return can.route._paramsMatcher.test(url) ? can.deparam(url.slice(1)) : {};
+ },
+
+ data: new can.Observe({}),
+
+ routes: {},
+
+ ready: function(val) {
+ if (val === false) {
+ onready = val;
+ }
+ if (val === true || onready === true) {
+ can.route._setup();
+ setState();
+ }
+ return can.route;
+ },
+
+ url: function(options, merge) {
+ if (merge) {
+ options = extend({}, curParams, options)
+ }
+ return "#!" + can.route.param(options);
+ },
+
+ link: function(name, options, props, merge) {
+ return "<a " + makeProps(
+ extend({
+ href: can.route.url(options, merge)
+ }, props)) + ">" + name + "</a>";
+ },
+
+ current: function(options) {
+ return location.hash == "#!" + can.route.param(options)
+ },
+ _setup: function() {
+ // If the hash changes, update the `can.route.data`.
+ can.bind.call(window, 'hashchange', setState);
+ },
+ _getHash: function() {
+ return location.href.split(/#!?/)[1] || "";
+ },
+ _setHash: function(serialized) {
+ var path = (can.route.param(serialized, true));
+ location.hash = "#!" + path;
+ return path;
+ }
+ });
+
+
+ // The functions in the following list applied to `can.route` (e.g. `can.route.attr('...')`) will
+ // instead act on the `can.route.data` observe.
+ each(['bind', 'unbind', 'delegate', 'undelegate', 'attr', 'removeAttr'], function(name) {
+ can.route[name] = function() {
+ // `delegate` and `undelegate` require
+ // the `can/observe/delegate` plugin
+ if (!can.route.data[name]) {
+ return;
+ }
+
+ return can.route.data[name].apply(can.route.data, arguments);
+ }
+ })
+
+ var // A ~~throttled~~ debounced function called multiple times will only fire once the
+ // timer runs down. Each call resets the timer.
+ timer,
+ // Intermediate storage for `can.route.data`.
+ curParams,
+ // Deparameterizes the portion of the hash of interest and assign the
+ // values to the `can.route.data` removing existing values no longer in the hash.
+ // setState is called typically by hashchange which fires asynchronously
+ // So it's possible that someone started changing the data before the
+ // hashchange event fired. For this reason, it will not set the route data
+ // if the data is changing or the hash already matches the hash that was set.
+ setState = can.route.setState = function() {
+ var hash = can.route._getHash();
+ curParams = can.route.deparam(hash);
+
+ // if the hash data is currently changing, or
+ // the hash is what we set it to anyway, do NOT change the hash
+ if (!changingData || hash !== lastHash) {
+ can.route.attr(curParams, true);
+ }
+ },
+ // The last hash caused by a data change
+ lastHash,
+ // Are data changes pending that haven't yet updated the hash
+ changingData;
+
+ // If the `can.route.data` changes, update the hash.
+ // Using `.serialize()` retrieves the raw data contained in the `observable`.
+ // This function is ~~throttled~~ debounced so it only updates once even if multiple values changed.
+ // This might be able to use batchNum and avoid this.
+ can.route.bind("change", function(ev, attr) {
+ // indicate that data is changing
+ changingData = 1;
+ clearTimeout(timer);
+ timer = setTimeout(function() {
+ // indicate that the hash is set to look like the data
+ changingData = 0;
+ var serialized = can.route.data.serialize();
+
+ lastHash = can.route._setHash(serialized);
+ }, 1);
+ });
+ // `onready` event...
+ can.bind.call(document, "ready", can.route.ready);
+
+ // Libraries other than jQuery don't execute the document `ready` listener
+ // if we are already DOM ready
+ if ((document.readyState === 'complete' || document.readyState === "interactive") && onready) {
+ can.route.ready();
+ }
+
+ // extend route to have a similar property
+ // that is often checked in mustache to determine
+ // an object's observability
+ can.route.constructor.canMakeObserve = can.Observe.canMakeObserve;
+
+ return can.route;
+ })(__m3, __m12, __m25);
+
+ // ## control/route/route.js
+ var __m26 = (function(can) {
+
+ // ## control/route.js
+ // _Controller route integration._
+
+ can.Control.processors.route = function(el, event, selector, funcName, controller) {
+ selector = selector || "";
+ can.route(selector);
+ var batchNum,
+ check = function(ev, attr, how) {
+ if (can.route.attr('route') === (selector) &&
+ (ev.batchNum === undefined || ev.batchNum !== batchNum)) {
+
+ batchNum = ev.batchNum;
+
+ var d = can.route.attr();
+ delete d.route;
+ if (can.isFunction(controller[funcName])) {
+ controller[funcName](d);
+ } else {
+ controller[controller[funcName]](d);
+ }
+
+ }
+ };
+ can.route.bind('change', check);
+ return function() {
+ can.route.unbind('change', check);
+ };
+ };
+
+ return can;
+ })(__m3, __m24, __m23);
+
+ window['can'] = __m4;
+ })();
\ No newline at end of file
Added: tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.fixture.js
===================================================================
--- tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.fixture.js (rev 0)
+++ tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.fixture.js 2013-06-17 15:45:26 UTC (rev 92)
@@ -0,0 +1,614 @@
+/*!
+ * CanJS - 1.1.6
+ * http://canjs.us/
+ * Copyright (c) 2013 Bitovi
+ * Wed, 29 May 2013 18:59:29 GMT
+ * Licensed MIT
+ * Includes: can/util/fixture
+ * Download from: http://canjs.com
+ */
+(function(can) {
+
+ // Get the URL from old Steal root, new Steal config or can.fixture.rootUrl
+ var getUrl = function(url) {
+ if (typeof steal !== 'undefined') {
+ if (can.isFunction(steal.config)) {
+ return steal.config().root.mapJoin(url).toString();
+ }
+ return steal.root.join(url).toString();
+ }
+ return (can.fixture.rootUrl || '') + url;
+ }
+
+ var updateSettings = function(settings, originalOptions) {
+ if (!can.fixture.on) {
+ return;
+ }
+
+ //simple wrapper for logging
+ var _logger = function(type, arr) {
+ if (console.log.apply) {
+ Function.prototype.call.apply(console[type], [console].concat(arr));
+ // console[type].apply(console, arr)
+ } else {
+ console[type](arr)
+ }
+ },
+ log = function() {
+ if (typeof steal !== 'undefined' && steal.dev) {
+ steal.dev.log('fixture INFO: ' + Array.prototype.slice.call(arguments).join(' '));
+ }
+ }
+
+ // We always need the type which can also be called method, default to GET
+ settings.type = settings.type || settings.method || 'GET';
+
+ // add the fixture option if programmed in
+ var data = overwrite(settings);
+
+ // if we don't have a fixture, do nothing
+ if (!settings.fixture) {
+ if (window.location.protocol === "file:") {
+ log("ajax request to " + settings.url + ", no fixture found");
+ }
+ return;
+ }
+
+ //if referencing something else, update the fixture option
+ if (typeof settings.fixture === "string" && can.fixture[settings.fixture]) {
+ settings.fixture = can.fixture[settings.fixture];
+ }
+
+ // if a string, we just point to the right url
+ if (typeof settings.fixture == "string") {
+ var url = settings.fixture;
+
+ if (/^\/\//.test(url)) {
+ // this lets us use rootUrl w/o having steal...
+ url = getUrl(settings.fixture.substr(2));
+ }
+
+ if (data) {
+ // Template static fixture URLs
+ url = can.sub(url, data);
+ }
+
+ delete settings.fixture;
+
+
+
+ settings.url = url;
+ settings.data = null;
+ settings.type = "GET";
+ if (!settings.error) {
+ settings.error = function(xhr, error, message) {
+ throw "fixtures.js Error " + error + " " + message;
+ };
+ }
+ } else {
+
+
+ //it's a function ... add the fixture datatype so our fixture transport handles it
+ // TODO: make everything go here for timing and other fun stuff
+ // add to settings data from fixture ...
+ settings.dataTypes && settings.dataTypes.splice(0, 0, "fixture");
+
+ if (data && originalOptions) {
+ can.extend(originalOptions.data, data)
+ }
+ }
+ },
+ // A helper function that takes what's called with response
+ // and moves some common args around to make it easier to call
+ extractResponse = function(status, statusText, responses, headers) {
+ // if we get response(RESPONSES, HEADERS)
+ if (typeof status != "number") {
+ headers = statusText;
+ responses = status;
+ statusText = "success"
+ status = 200;
+ }
+ // if we get response(200, RESPONSES, HEADERS)
+ if (typeof statusText != "string") {
+ headers = responses;
+ responses = statusText;
+ statusText = "success";
+ }
+ if (status >= 400 && status <= 599) {
+ this.dataType = "text"
+ }
+ return [status, statusText, extractResponses(this, responses), headers];
+ },
+ // If we get data instead of responses,
+ // make sure we provide a response type that matches the first datatype (typically json)
+ extractResponses = function(settings, responses) {
+ var next = settings.dataTypes ? settings.dataTypes[0] : (settings.dataType || 'json');
+ if (!responses || !responses[next]) {
+ var tmp = {}
+ tmp[next] = responses;
+ responses = tmp;
+ }
+ return responses;
+ };
+
+ //used to check urls
+ // check if jQuery
+ if (can.ajaxPrefilter && can.ajaxTransport) {
+
+ // the pre-filter needs to re-route the url
+ can.ajaxPrefilter(updateSettings);
+
+ can.ajaxTransport("fixture", function(s, original) {
+ // remove the fixture from the datatype
+ s.dataTypes.shift();
+
+ //we'll return the result of the next data type
+ var timeout, stopped = false;
+
+ return {
+ send: function(headers, callback) {
+ // we'll immediately wait the delay time for all fixtures
+ timeout = setTimeout(function() {
+ // if the user wants to call success on their own, we allow it ...
+ var success = function() {
+ if (stopped === false) {
+ callback.apply(null, extractResponse.apply(s, arguments));
+ }
+ },
+ // get the result form the fixture
+ result = s.fixture(original, success, headers, s);
+ if (result !== undefined) {
+ // make sure the result has the right dataType
+ callback(200, "success", extractResponses(s, result), {});
+ }
+ }, can.fixture.delay);
+ },
+ abort: function() {
+ stopped = true;
+ clearTimeout(timeout)
+ }
+ };
+ });
+ } else {
+ var AJAX = can.ajax;
+ can.ajax = function(settings) {
+ updateSettings(settings, settings);
+ if (settings.fixture) {
+ var timeout, d = new can.Deferred(),
+ stopped = false;
+
+ //TODO this should work with response
+ d.getResponseHeader = function() {}
+
+ // call success and fail
+ d.then(settings.success, settings.fail);
+
+ // abort should stop the timeout and calling success
+ d.abort = function() {
+ clearTimeout(timeout);
+ stopped = true;
+ d.reject(d)
+ }
+ // set a timeout that simulates making a request ....
+ timeout = setTimeout(function() {
+ // if the user wants to call success on their own, we allow it ...
+ var success = function() {
+ var response = extractResponse.apply(settings, arguments),
+ status = response[0];
+
+ if ((status >= 200 && status < 300 || status === 304) && stopped === false) {
+ d.resolve(response[2][settings.dataType])
+ } else {
+ // TODO probably resolve better
+ d.reject(d, 'error', response[1]);
+ }
+ },
+ // get the result form the fixture
+ result = settings.fixture(settings, success, settings.headers, settings);
+ if (result !== undefined) {
+ d.resolve(result)
+ }
+ }, can.fixture.delay);
+
+ return d;
+ } else {
+ return AJAX(settings);
+ }
+ }
+ }
+
+ var typeTest = /^(script|json|text|jsonp)$/,
+ // a list of 'overwrite' settings object
+ overwrites = [],
+ // returns the index of an overwrite function
+ find = function(settings, exact) {
+ for (var i = 0; i < overwrites.length; i++) {
+ if ($fixture._similar(settings, overwrites[i], exact)) {
+ return i;
+ }
+ }
+ return -1;
+ },
+ // overwrites the settings fixture if an overwrite matches
+ overwrite = function(settings) {
+ var index = find(settings);
+ if (index > -1) {
+ settings.fixture = overwrites[index].fixture;
+ return $fixture._getData(overwrites[index].url, settings.url)
+ }
+
+ },
+ // Makes an attempt to guess where the id is at in the url and returns it.
+ getId = function(settings) {
+ var id = settings.data.id;
+
+ if (id === undefined && typeof settings.data === "number") {
+ id = settings.data;
+ }
+
+
+
+ if (id === undefined) {
+ settings.url.replace(/\/(\d+)(\/|$|\.)/g, function(all, num) {
+ id = num;
+ });
+ }
+
+ if (id === undefined) {
+ id = settings.url.replace(/\/(\w+)(\/|$|\.)/g, function(all, num) {
+ if (num != 'update') {
+ id = num;
+ }
+ })
+ }
+
+ if (id === undefined) { // if still not set, guess a random number
+ id = Math.round(Math.random() * 1000)
+ }
+
+ return id;
+ };
+
+ var $fixture = can.fixture = function(settings, fixture) {
+ // if we provide a fixture ...
+ if (fixture !== undefined) {
+ if (typeof settings == 'string') {
+ // handle url strings
+ var matches = settings.match(/(GET|POST|PUT|DELETE) (.+)/i);
+ if (!matches) {
+ settings = {
+ url: settings
+ };
+ } else {
+ settings = {
+ url: matches[2],
+ type: matches[1]
+ };
+ }
+
+ }
+
+ //handle removing. An exact match if fixture was provided, otherwise, anything similar
+ var index = find(settings, !! fixture);
+ if (index > -1) {
+ overwrites.splice(index, 1)
+ }
+ if (fixture == null) {
+ return
+ }
+ settings.fixture = fixture;
+ overwrites.push(settings)
+ } else {
+ can.each(settings, function(fixture, url) {
+ $fixture(url, fixture);
+ })
+ }
+ };
+ var replacer = can.replacer;
+
+ can.extend(can.fixture, {
+ // given ajax settings, find an overwrite
+ _similar: function(settings, overwrite, exact) {
+ if (exact) {
+ return can.Object.same(settings, overwrite, {
+ fixture: null
+ })
+ } else {
+ return can.Object.subset(settings, overwrite, can.fixture._compare)
+ }
+ },
+ _compare: {
+ url: function(a, b) {
+ return !!$fixture._getData(b, a)
+ },
+ fixture: null,
+ type: "i"
+ },
+ // gets data from a url like "/todo/{id}" given "todo/5"
+ _getData: function(fixtureUrl, url) {
+ var order = [],
+ fixtureUrlAdjusted = fixtureUrl.replace('.', '\\.').replace('?', '\\?'),
+ res = new RegExp(fixtureUrlAdjusted.replace(replacer, function(whole, part) {
+ order.push(part)
+ return "([^\/]+)"
+ }) + "$").exec(url),
+ data = {};
+
+ if (!res) {
+ return null;
+ }
+ res.shift();
+ can.each(order, function(name) {
+ data[name] = res.shift()
+ })
+ return data;
+ },
+
+ store: function(types, count, make, filter) {
+
+ var items = [], // TODO: change this to a hash
+ currentId = 0,
+ findOne = function(id) {
+ for (var i = 0; i < items.length; i++) {
+ if (id == items[i].id) {
+ return items[i];
+ }
+ }
+ },
+ methods = {};
+
+ if (typeof types === "string") {
+ types = [types + "s", types]
+ } else if (!can.isArray(types)) {
+ filter = make;
+ make = count;
+ count = types;
+ }
+
+ // make all items
+ can.extend(methods, {
+
+ findAll: function(request) {
+ request = request || {}
+ //copy array of items
+ var retArr = items.slice(0);
+ request.data = request.data || {};
+ //sort using order
+ //order looks like ["age ASC","gender DESC"]
+ can.each((request.data.order || []).slice(0).reverse(), function(name) {
+ var split = name.split(" ");
+ retArr = retArr.sort(function(a, b) {
+ if (split[1].toUpperCase() !== "ASC") {
+ if (a[split[0]] < b[split[0]]) {
+ return 1;
+ } else if (a[split[0]] == b[split[0]]) {
+ return 0
+ } else {
+ return -1;
+ }
+ } else {
+ if (a[split[0]] < b[split[0]]) {
+ return -1;
+ } else if (a[split[0]] == b[split[0]]) {
+ return 0
+ } else {
+ return 1;
+ }
+ }
+ });
+ });
+
+ //group is just like a sort
+ can.each((request.data.group || []).slice(0).reverse(), function(name) {
+ var split = name.split(" ");
+ retArr = retArr.sort(function(a, b) {
+ return a[split[0]] > b[split[0]];
+ });
+ });
+
+ var offset = parseInt(request.data.offset, 10) || 0,
+ limit = parseInt(request.data.limit, 10) || (items.length - offset),
+ i = 0;
+
+ //filter results if someone added an attr like parentId
+ for (var param in request.data) {
+ i = 0;
+ if (request.data[param] !== undefined && // don't do this if the value of the param is null (ignore it)
+ (param.indexOf("Id") != -1 || param.indexOf("_id") != -1)) {
+ while (i < retArr.length) {
+ if (request.data[param] != retArr[i][param]) {
+ retArr.splice(i, 1);
+ } else {
+ i++;
+ }
+ }
+ }
+ }
+
+ if (filter) {
+ i = 0;
+ while (i < retArr.length) {
+ if (!filter(retArr[i], request)) {
+ retArr.splice(i, 1);
+ } else {
+ i++;
+ }
+ }
+ }
+
+ //return data spliced with limit and offset
+ return {
+ "count": retArr.length,
+ "limit": request.data.limit,
+ "offset": request.data.offset,
+ "data": retArr.slice(offset, offset + limit)
+ };
+ },
+
+ findOne: function(request, response) {
+ var item = findOne(getId(request));
+ response(item ? item : undefined);
+ },
+
+ update: function(request, response) {
+ var id = getId(request);
+
+ // TODO: make it work with non-linear ids ..
+ can.extend(findOne(id), request.data);
+ response({
+ id: getId(request)
+ }, {
+ location: request.url || "/" + getId(request)
+ });
+ },
+
+ destroy: function(request) {
+ var id = getId(request);
+ for (var i = 0; i < items.length; i++) {
+ if (items[i].id == id) {
+ items.splice(i, 1);
+ break;
+ }
+ }
+
+ // TODO: make it work with non-linear ids ..
+ can.extend(findOne(id) || {}, request.data);
+ return {};
+ },
+
+ create: function(settings, response) {
+ var item = make(items.length, items);
+
+ can.extend(item, settings.data);
+
+ if (!item.id) {
+ item.id = currentId++;
+ }
+
+ items.push(item);
+ response({
+ id: item.id
+ }, {
+ location: settings.url + "/" + item.id
+ })
+ }
+ });
+
+ var reset = function() {
+ items = [];
+ for (var i = 0; i < (count); i++) {
+ //call back provided make
+ var item = make(i, items);
+
+ if (!item.id) {
+ item.id = i;
+ }
+ currentId = Math.max(item.id + 1, currentId + 1) || items.length;
+ items.push(item);
+ }
+ if (can.isArray(types)) {
+ can.fixture["~" + types[0]] = items;
+ can.fixture["-" + types[0]] = methods.findAll;
+ can.fixture["-" + types[1]] = methods.findOne;
+ can.fixture["-" + types[1] + "Update"] = methods.update;
+ can.fixture["-" + types[1] + "Destroy"] = methods.destroy;
+ can.fixture["-" + types[1] + "Create"] = methods.create;
+ }
+
+ }
+ reset()
+ // if we have types given add them to can.fixture
+
+
+ return can.extend({
+ getId: getId,
+
+ find: function(settings) {
+ return findOne(getId(settings));
+ },
+
+ reset: reset
+ }, methods);
+ },
+
+ rand: function(arr, min, max) {
+ if (typeof arr == 'number') {
+ if (typeof min == 'number') {
+ return arr + Math.floor(Math.random() * (min - arr));
+ } else {
+ return Math.floor(Math.random() * arr);
+ }
+
+ }
+ var rand = arguments.callee;
+ // get a random set
+ if (min === undefined) {
+ return rand(arr, rand(arr.length + 1))
+ }
+ // get a random selection of arr
+ var res = [];
+ arr = arr.slice(0);
+ // set max
+ if (!max) {
+ max = min;
+ }
+ //random max
+ max = min + Math.round(rand(max - min))
+ for (var i = 0; i < max; i++) {
+ res.push(arr.splice(rand(arr.length), 1)[0])
+ }
+ return res;
+ },
+
+ xhr: function(xhr) {
+ return can.extend({}, {
+ abort: can.noop,
+ getAllResponseHeaders: function() {
+ return "";
+ },
+ getResponseHeader: function() {
+ return "";
+ },
+ open: can.noop,
+ overrideMimeType: can.noop,
+ readyState: 4,
+ responseText: "",
+ responseXML: null,
+ send: can.noop,
+ setRequestHeader: can.noop,
+ status: 200,
+ statusText: "OK"
+ }, xhr);
+ },
+
+ on: true
+ });
+
+ can.fixture.delay = 200;
+
+
+ can.fixture.rootUrl = getUrl('');
+
+ can.fixture["-handleFunction"] = function(settings) {
+ if (typeof settings.fixture === "string" && can.fixture[settings.fixture]) {
+ settings.fixture = can.fixture[settings.fixture];
+ }
+ if (typeof settings.fixture == "function") {
+ setTimeout(function() {
+ if (settings.success) {
+ settings.success.apply(null, settings.fixture(settings, "success"));
+ }
+ if (settings.complete) {
+ settings.complete.apply(null, settings.fixture(settings, "complete"));
+ }
+ }, can.fixture.delay);
+ return true;
+ }
+ return false;
+ };
+
+ //Expose this for fixture debugging
+ can.fixture.overwrites = overwrites;
+ can.fixture.make = can.fixture.store;
+ return can.fixture;
+ })(can);
\ No newline at end of file
Added: tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.jquery.js
===================================================================
--- tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.jquery.js (rev 0)
+++ tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.jquery.js 2013-06-17 15:45:26 UTC (rev 92)
@@ -0,0 +1,4363 @@
+/*!
+ * CanJS - 1.1.6
+ * http://canjs.us/
+ * Copyright (c) 2013 Bitovi
+ * Wed, 29 May 2013 18:59:27 GMT
+ * Licensed MIT
+ * Includes: can/construct,can/observe,can/observe/compute,can/model,can/view,can/view/ejs,can/control,can/route,can/control/route
+ * Download from: http://canjs.com
+ */
+(function(undefined) {
+
+ // ## util/can.js
+ var __m5 = (function() {
+ var can = window.can || {};
+ if (typeof GLOBALCAN === 'undefined' || GLOBALCAN !== false) {
+ window.can = can;
+ }
+
+ can.isDeferred = function(obj) {
+ var isFunction = this.isFunction;
+ // Returns `true` if something looks like a deferred.
+ return obj && isFunction(obj.then) && isFunction(obj.pipe);
+ };
+
+ var cid = 0;
+ can.cid = function(object, name) {
+ if (object._cid) {
+ return object._cid
+ } else {
+ return object._cid = (name || "") + (++cid)
+ }
+ }
+ can.VERSION = '@EDGE';
+ return can;
+ })();
+
+ // ## util/array/each.js
+ var __m6 = (function(can) {
+ can.each = function(elements, callback, context) {
+ var i = 0,
+ key;
+ if (elements) {
+ if (typeof elements.length === 'number' && elements.pop) {
+ if (elements.attr) {
+ elements.attr('length');
+ }
+ for (key = elements.length; i < key; i++) {
+ if (callback.call(context || elements[i], elements[i], i, elements) === false) {
+ break;
+ }
+ }
+ } else if (elements.hasOwnProperty) {
+ for (key in elements) {
+ if (elements.hasOwnProperty(key)) {
+ if (callback.call(context || elements[key], elements[key], key, elements) === false) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ return elements;
+ };
+
+ return can;
+ })(__m5);
+
+ // ## util/jquery/jquery.js
+ var __m3 = (function($, can) {
+ // _jQuery node list._
+ $.extend(can, $, {
+ trigger: function(obj, event, args) {
+ if (obj.trigger) {
+ obj.trigger(event, args);
+ } else {
+ $.event.trigger(event, args, obj, true);
+ }
+ },
+ addEvent: function(ev, cb) {
+ $([this]).bind(ev, cb);
+ return this;
+ },
+ removeEvent: function(ev, cb) {
+ $([this]).unbind(ev, cb);
+ return this;
+ },
+ // jquery caches fragments, we always needs a new one
+ buildFragment: function(elems, context) {
+ var oldFragment = $.buildFragment,
+ ret;
+
+ elems = [elems];
+ // Set context per 1.8 logic
+ context = context || document;
+ context = !context.nodeType && context[0] || context;
+ context = context.ownerDocument || context;
+
+ ret = oldFragment.call(jQuery, elems, context);
+
+ return ret.cacheable ? $.clone(ret.fragment) : ret.fragment || ret;
+ },
+ $: $,
+ each: can.each
+ });
+
+ // Wrap binding functions.
+ $.each(['bind', 'unbind', 'undelegate', 'delegate'], function(i, func) {
+ can[func] = function() {
+ var t = this[func] ? this : $([this]);
+ t[func].apply(t, arguments);
+ return this;
+ };
+ });
+
+ // Wrap modifier functions.
+ $.each(["append", "filter", "addClass", "remove", "data", "get"], function(i, name) {
+ can[name] = function(wrapped) {
+ return wrapped[name].apply(wrapped, can.makeArray(arguments).slice(1));
+ };
+ });
+
+ // Memory safe destruction.
+ var oldClean = $.cleanData;
+
+ $.cleanData = function(elems) {
+ $.each(elems, function(i, elem) {
+ if (elem) {
+ can.trigger(elem, "destroyed", [], false);
+ }
+ });
+ oldClean(elems);
+ };
+
+ return can;
+ })(jQuery, __m5, __m6);
+
+ // ## util/string/string.js
+ var __m2 = (function(can) {
+ // ##string.js
+ // _Miscellaneous string utility functions._
+
+ // Several of the methods in this plugin use code adapated from Prototype
+ // Prototype JavaScript framework, version 1.6.0.1.
+ // © 2005-2007 Sam Stephenson
+ var strUndHash = /_|-/,
+ strColons = /\=\=/,
+ strWords = /([A-Z]+)([A-Z][a-z])/g,
+ strLowUp = /([a-z\d])([A-Z])/g,
+ strDash = /([a-z\d])([A-Z])/g,
+ strReplacer = /\{([^\}]+)\}/g,
+ strQuote = /"/g,
+ strSingleQuote = /'/g,
+
+ // Returns the `prop` property from `obj`.
+ // If `add` is true and `prop` doesn't exist in `obj`, create it as an
+ // empty object.
+ getNext = function(obj, prop, add) {
+ var result = obj[prop];
+
+ if (result === undefined && add === true) {
+ result = obj[prop] = {}
+ }
+ return result
+ },
+
+ // Returns `true` if the object can have properties (no `null`s).
+ isContainer = function(current) {
+ return (/^f|^o/).test(typeof current);
+ };
+
+ can.extend(can, {
+ // Escapes strings for HTML.
+
+ esc: function(content) {
+ // Convert bad values into empty strings
+ var isInvalid = content === null || content === undefined || (isNaN(content) && ("" + content === 'NaN'));
+ return ("" + (isInvalid ? '' : content))
+ .replace(/&/g, '&')
+ .replace(/</g, '<')
+ .replace(/>/g, '>')
+ .replace(strQuote, '"')
+ .replace(strSingleQuote, "'");
+ },
+
+
+ getObject: function(name, roots, add) {
+
+ // The parts of the name we are looking up
+ // `['App','Models','Recipe']`
+ var parts = name ? name.split('.') : [],
+ length = parts.length,
+ current,
+ r = 0,
+ i, container, rootsLength;
+
+ // Make sure roots is an `array`.
+ roots = can.isArray(roots) ? roots : [roots || window];
+
+ rootsLength = roots.length
+
+ if (!length) {
+ return roots[0];
+ }
+
+ // For each root, mark it as current.
+ for (r; r < rootsLength; r++) {
+ current = roots[r];
+ container = undefined;
+
+ // Walk current to the 2nd to last object or until there
+ // is not a container.
+ for (i = 0; i < length && isContainer(current); i++) {
+ container = current;
+ current = getNext(container, parts[i]);
+ }
+
+ // If we found property break cycle
+ if (container !== undefined && current !== undefined) {
+ break
+ }
+ }
+
+ // Remove property from found container
+ if (add === false && current !== undefined) {
+ delete container[parts[i - 1]]
+ }
+
+ // When adding property add it to the first root
+ if (add === true && current === undefined) {
+ current = roots[0]
+
+ for (i = 0; i < length && isContainer(current); i++) {
+ current = getNext(current, parts[i], true);
+ }
+ }
+
+ return current;
+ },
+ // Capitalizes a string.
+
+ capitalize: function(s, cache) {
+ // Used to make newId.
+ return s.charAt(0).toUpperCase() + s.slice(1);
+ },
+
+ // Underscores a string.
+
+ underscore: function(s) {
+ return s
+ .replace(strColons, '/')
+ .replace(strWords, '$1_$2')
+ .replace(strLowUp, '$1_$2')
+ .replace(strDash, '_')
+ .toLowerCase();
+ },
+ // Micro-templating.
+
+ sub: function(str, data, remove) {
+ var obs = [];
+
+ str = str || '';
+
+ obs.push(str.replace(strReplacer, function(whole, inside) {
+
+ // Convert inside to type.
+ var ob = can.getObject(inside, data, remove === true ? false : undefined);
+
+ if (ob === undefined) {
+ obs = null;
+ return "";
+ }
+
+ // If a container, push into objs (which will return objects found).
+ if (isContainer(ob) && obs) {
+ obs.push(ob);
+ return "";
+ }
+
+ return "" + ob;
+ }));
+
+ return obs === null ? obs : (obs.length <= 1 ? obs[0] : obs);
+ },
+
+ // These regex's are used throughout the rest of can, so let's make
+ // them available.
+ replacer: strReplacer,
+ undHash: strUndHash
+ });
+ return can;
+ })(__m3);
+
+ // ## construct/construct.js
+ var __m1 = (function(can) {
+
+ // ## construct.js
+ // `can.Construct`
+ // _This is a modified version of
+ // [John Resig's class](http://ejohn.org/blog/simple-javascript-inheritance/).
+ // It provides class level inheritance and callbacks._
+
+ // A private flag used to initialize a new class instance without
+ // initializing it's bindings.
+ var initializing = 0;
+
+
+ can.Construct = function() {
+ if (arguments.length) {
+ return can.Construct.extend.apply(can.Construct, arguments);
+ }
+ };
+
+
+ can.extend(can.Construct, {
+
+ newInstance: function() {
+ // Get a raw instance object (`init` is not called).
+ var inst = this.instance(),
+ arg = arguments,
+ args;
+
+ // Call `setup` if there is a `setup`
+ if (inst.setup) {
+ args = inst.setup.apply(inst, arguments);
+ }
+
+ // Call `init` if there is an `init`
+ // If `setup` returned `args`, use those as the arguments
+ if (inst.init) {
+ inst.init.apply(inst, args || arguments);
+ }
+
+ return inst;
+ },
+ // Overwrites an object with methods. Used in the `super` plugin.
+ // `newProps` - New properties to add.
+ // `oldProps` - Where the old properties might be (used with `super`).
+ // `addTo` - What we are adding to.
+ _inherit: function(newProps, oldProps, addTo) {
+ can.extend(addTo || newProps, newProps || {})
+ },
+ // used for overwriting a single property.
+ // this should be used for patching other objects
+ // the super plugin overwrites this
+ _overwrite: function(what, oldProps, propName, val) {
+ what[propName] = val;
+ },
+ // Set `defaults` as the merger of the parent `defaults` and this
+ // object's `defaults`. If you overwrite this method, make sure to
+ // include option merging logic.
+
+ setup: function(base, fullName) {
+ this.defaults = can.extend(true, {}, base.defaults, this.defaults);
+ },
+ // Create's a new `class` instance without initializing by setting the
+ // `initializing` flag.
+ instance: function() {
+
+ // Prevents running `init`.
+ initializing = 1;
+
+ var inst = new this();
+
+ // Allow running `init`.
+ initializing = 0;
+
+ return inst;
+ },
+ // Extends classes.
+
+ extend: function(fullName, klass, proto) {
+ // Figure out what was passed and normalize it.
+ if (typeof fullName != 'string') {
+ proto = klass;
+ klass = fullName;
+ fullName = null;
+ }
+
+ if (!proto) {
+ proto = klass;
+ klass = null;
+ }
+ proto = proto || {};
+
+ var _super_class = this,
+ _super = this.prototype,
+ name, shortName, namespace, prototype;
+
+ // Instantiate a base class (but only create the instance,
+ // don't run the init constructor).
+ prototype = this.instance();
+
+ // Copy the properties over onto the new prototype.
+ can.Construct._inherit(proto, _super, prototype);
+
+ // The dummy class constructor.
+
+ function Constructor() {
+ // All construction is actually done in the init method.
+ if (!initializing) {
+ return this.constructor !== Constructor && arguments.length ?
+ // We are being called without `new` or we are extending.
+ arguments.callee.extend.apply(arguments.callee, arguments) :
+ // We are being called with `new`.
+ this.constructor.newInstance.apply(this.constructor, arguments);
+ }
+ }
+
+ // Copy old stuff onto class (can probably be merged w/ inherit)
+ for (name in _super_class) {
+ if (_super_class.hasOwnProperty(name)) {
+ Constructor[name] = _super_class[name];
+ }
+ }
+
+ // Copy new static properties on class.
+ can.Construct._inherit(klass, _super_class, Constructor);
+
+ // Setup namespaces.
+ if (fullName) {
+
+ var parts = fullName.split('.'),
+ shortName = parts.pop(),
+ current = can.getObject(parts.join('.'), window, true),
+ namespace = current,
+ _fullName = can.underscore(fullName.replace(/\./g, "_")),
+ _shortName = can.underscore(shortName);
+
+
+
+ current[shortName] = Constructor;
+ }
+
+ // Set things that shouldn't be overwritten.
+ can.extend(Constructor, {
+ constructor: Constructor,
+ prototype: prototype,
+
+ namespace: namespace,
+
+ _shortName: _shortName,
+
+ fullName: fullName,
+ _fullName: _fullName
+ });
+
+ // Dojo and YUI extend undefined
+ if (shortName !== undefined) {
+ Constructor.shortName = shortName;
+ }
+
+ // Make sure our prototype looks nice.
+ Constructor.prototype.constructor = Constructor;
+
+
+ // Call the class `setup` and `init`
+ var t = [_super_class].concat(can.makeArray(arguments)),
+ args = Constructor.setup.apply(Constructor, t);
+
+ if (Constructor.init) {
+ Constructor.init.apply(Constructor, args || t);
+ }
+
+
+ return Constructor;
+
+ }
+
+ });
+ return can.Construct;
+ })(__m2);
+
+ // ## util/bind/bind.js
+ var __m8 = (function(can) {
+
+
+ // ## Bind helpers
+ can.bindAndSetup = function() {
+ // Add the event to this object
+ can.addEvent.apply(this, arguments);
+ // If not initializing, and the first binding
+ // call bindsetup if the function exists.
+ if (!this._init) {
+ if (!this._bindings) {
+ this._bindings = 1;
+ // setup live-binding
+ this._bindsetup && this._bindsetup();
+
+ } else {
+ this._bindings++;
+ }
+
+ }
+
+ return this;
+ };
+
+ can.unbindAndTeardown = function(ev, handler) {
+ // Remove the event handler
+ can.removeEvent.apply(this, arguments);
+
+ this._bindings--;
+ // If there are no longer any bindings and
+ // there is a bindteardown method, call it.
+ if (!this._bindings) {
+ this._bindteardown && this._bindteardown();
+ }
+ return this;
+ }
+
+ return can;
+
+ })(__m3);
+
+ // ## observe/observe.js
+ var __m7 = (function(can, bind) {
+ // ## observe.js
+ // `can.Observe`
+ // _Provides the observable pattern for JavaScript Objects._
+ // Returns `true` if something is an object with properties of its own.
+ var canMakeObserve = function(obj) {
+ return obj && !can.isDeferred(obj) && (can.isArray(obj) || can.isPlainObject(obj) || (obj instanceof can.Observe));
+ },
+
+ // Removes all listeners.
+ unhookup = function(items, namespace) {
+ return can.each(items, function(item) {
+ if (item && item.unbind) {
+ item.unbind("change" + namespace);
+ }
+ });
+ },
+ // Listens to changes on `child` and "bubbles" the event up.
+ // `child` - The object to listen for changes on.
+ // `prop` - The property name is at on.
+ // `parent` - The parent object of prop.
+ // `ob` - (optional) The Observe object constructor
+ // `list` - (optional) The observable list constructor
+ hookupBubble = function(child, prop, parent, Ob, List) {
+ Ob = Ob || Observe;
+ List = List || Observe.List;
+
+ // If it's an `array` make a list, otherwise a child.
+ if (child instanceof Observe) {
+ // We have an `observe` already...
+ // Make sure it is not listening to this already
+ // It's only listening if it has bindings already.
+ parent._bindings && unhookup([child], parent._cid);
+ } else if (can.isArray(child)) {
+ child = new List(child);
+ } else {
+ child = new Ob(child);
+ }
+ // only listen if something is listening to you
+ if (parent._bindings) {
+ // Listen to all changes and `batchTrigger` upwards.
+ bindToChildAndBubbleToParent(child, prop, parent)
+ }
+
+
+ return child;
+ },
+ bindToChildAndBubbleToParent = function(child, prop, parent) {
+ child.bind("change" + parent._cid, function() {
+ // `batchTrigger` the type on this...
+ var args = can.makeArray(arguments),
+ ev = args.shift();
+ args[0] = (prop === "*" ? [parent.indexOf(child), args[0]] : [prop, args[0]]).join(".");
+
+ // track objects dispatched on this observe
+ ev.triggeredNS = ev.triggeredNS || {};
+
+ // if it has already been dispatched exit
+ if (ev.triggeredNS[parent._cid]) {
+ return;
+ }
+
+ ev.triggeredNS[parent._cid] = true;
+ // send change event with modified attr to parent
+ can.trigger(parent, ev, args);
+ // send modified attr event to parent
+ //can.trigger(parent, args[0], args);
+ });
+ }
+ // An `id` to track events for a given observe.
+ observeId = 0,
+ // A helper used to serialize an `Observe` or `Observe.List`.
+ // `observe` - The observable.
+ // `how` - To serialize with `attr` or `serialize`.
+ // `where` - To put properties, in an `{}` or `[]`.
+ serialize = function(observe, how, where) {
+ // Go through each property.
+ observe.each(function(val, name) {
+ // If the value is an `object`, and has an `attrs` or `serialize` function.
+ where[name] = canMakeObserve(val) && can.isFunction(val[how]) ?
+ // Call `attrs` or `serialize` to get the original data back.
+ val[how]() :
+ // Otherwise return the value.
+ val;
+ });
+ return where;
+ },
+ attrParts = function(attr, keepKey) {
+ if (keepKey) {
+ return [attr];
+ }
+ return can.isArray(attr) ? attr : ("" + attr).split(".");
+ },
+ // Which batch of events this is for -- might not want to send multiple
+ // messages on the same batch. This is mostly for event delegation.
+ batchNum = 1,
+ // how many times has start been called without a stop
+ transactions = 0,
+ // an array of events within a transaction
+ batchEvents = [],
+ stopCallbacks = [],
+ makeBindSetup = function(wildcard) {
+ return function() {
+ var parent = this;
+ this._each(function(child, prop) {
+ if (child && child.bind) {
+ bindToChildAndBubbleToParent(child, wildcard || prop, parent)
+ }
+ })
+ };
+ };
+
+
+ var Observe = can.Map = can.Observe = can.Construct({
+
+ // keep so it can be overwritten
+ bind: can.bindAndSetup,
+ unbind: can.unbindAndTeardown,
+ id: "id",
+ canMakeObserve: canMakeObserve,
+ // starts collecting events
+ // takes a callback for after they are updated
+ // how could you hook into after ejs
+
+ startBatch: function(batchStopHandler) {
+ transactions++;
+ batchStopHandler && stopCallbacks.push(batchStopHandler);
+ },
+
+ stopBatch: function(force, callStart) {
+ if (force) {
+ transactions = 0;
+ } else {
+ transactions--;
+ }
+
+ if (transactions == 0) {
+ var items = batchEvents.slice(0),
+ callbacks = stopCallbacks.slice(0);
+ batchEvents = [];
+ stopCallbacks = [];
+ batchNum++;
+ callStart && this.startBatch();
+ can.each(items, function(args) {
+ can.trigger.apply(can, args);
+ });
+ can.each(callbacks, function(cb) {
+ cb();
+ });
+ }
+ },
+
+ triggerBatch: function(item, event, args) {
+ // Don't send events if initalizing.
+ if (!item._init) {
+ if (transactions == 0) {
+ return can.trigger(item, event, args);
+ } else {
+ event = typeof event === "string" ? {
+ type: event
+ } :
+ event;
+ event.batchNum = batchNum;
+ batchEvents.push([
+ item,
+ event,
+ args
+ ]);
+ }
+ }
+ },
+
+ keys: function(observe) {
+ var keys = [];
+ Observe.__reading && Observe.__reading(observe, '__keys');
+ for (var keyName in observe._data) {
+ keys.push(keyName);
+ }
+ return keys;
+ }
+ },
+
+ {
+ setup: function(obj) {
+ // `_data` is where we keep the properties.
+ this._data = {};
+
+ // The namespace this `object` uses to listen to events.
+ can.cid(this, ".observe");
+ // Sets all `attrs`.
+ this._init = 1;
+ this.attr(obj);
+ this.bind('change' + this._cid, can.proxy(this._changes, this));
+ delete this._init;
+ },
+ _bindsetup: makeBindSetup(),
+ _bindteardown: function() {
+ var cid = this._cid;
+ this._each(function(child) {
+ unhookup([child], cid)
+ })
+ },
+ _changes: function(ev, attr, how, newVal, oldVal) {
+ Observe.triggerBatch(this, {
+ type: attr,
+ batchNum: ev.batchNum
+ }, [newVal, oldVal]);
+ },
+ _triggerChange: function(attr, how, newVal, oldVal) {
+ Observe.triggerBatch(this, "change", can.makeArray(arguments))
+ },
+ // no live binding iterator
+ _each: function(callback) {
+ var data = this.__get();
+ for (var prop in data) {
+ if (data.hasOwnProperty(prop)) {
+ callback(data[prop], prop)
+ }
+ }
+ },
+
+ attr: function(attr, val) {
+ // This is super obfuscated for space -- basically, we're checking
+ // if the type of the attribute is not a `number` or a `string`.
+ var type = typeof attr;
+ if (type !== "string" && type !== "number") {
+ return this._attrs(attr, val)
+ } else if (val === undefined) { // If we are getting a value.
+ // Let people know we are reading.
+ Observe.__reading && Observe.__reading(this, attr)
+ return this._get(attr)
+ } else {
+ // Otherwise we are setting.
+ this._set(attr, val);
+ return this;
+ }
+ },
+
+ each: function() {
+ Observe.__reading && Observe.__reading(this, '__keys');
+ return can.each.apply(undefined, [this.__get()].concat(can.makeArray(arguments)))
+ },
+
+ removeAttr: function(attr) {
+ // Info if this is List or not
+ var isList = this instanceof can.Observe.List,
+ // Convert the `attr` into parts (if nested).
+ parts = attrParts(attr),
+ // The actual property to remove.
+ prop = parts.shift(),
+ // The current value.
+ current = isList ? this[prop] : this._data[prop];
+
+ // If we have more parts, call `removeAttr` on that part.
+ if (parts.length) {
+ return current.removeAttr(parts)
+ } else {
+ if (isList) {
+ this.splice(prop, 1)
+ } else if (prop in this._data) {
+ // Otherwise, `delete`.
+ delete this._data[prop];
+ // Create the event.
+ if (!(prop in this.constructor.prototype)) {
+ delete this[prop]
+ }
+ // Let others know the number of keys have changed
+ Observe.triggerBatch(this, "__keys");
+ this._triggerChange(prop, "remove", undefined, current);
+
+ }
+ return current;
+ }
+ },
+ // Reads a property from the `object`.
+ _get: function(attr) {
+ var value = typeof attr === 'string' && !! ~attr.indexOf('.') && this.__get(attr);
+ if (value) {
+ return value;
+ }
+
+ // break up the attr (`"foo.bar"`) into `["foo","bar"]`
+ var parts = attrParts(attr),
+ // get the value of the first attr name (`"foo"`)
+ current = this.__get(parts.shift());
+ // if there are other attributes to read
+ return parts.length ?
+ // and current has a value
+ current ?
+ // lookup the remaining attrs on current
+ current._get(parts) :
+ // or if there's no current, return undefined
+ undefined :
+ // if there are no more parts, return current
+ current;
+ },
+ // Reads a property directly if an `attr` is provided, otherwise
+ // returns the "real" data object itself.
+ __get: function(attr) {
+ return attr ? this._data[attr] : this._data;
+ },
+ // Sets `attr` prop as value on this object where.
+ // `attr` - Is a string of properties or an array of property values.
+ // `value` - The raw value to set.
+ _set: function(attr, value, keepKey) {
+ // Convert `attr` to attr parts (if it isn't already).
+ var parts = attrParts(attr, keepKey),
+ // The immediate prop we are setting.
+ prop = parts.shift(),
+ // The current value.
+ current = this.__get(prop);
+
+ // If we have an `object` and remaining parts.
+ if (canMakeObserve(current) && parts.length) {
+ // That `object` should set it (this might need to call attr).
+ current._set(parts, value)
+ } else if (!parts.length) {
+ // We're in "real" set territory.
+ if (this.__convert) {
+ value = this.__convert(prop, value)
+ }
+ this.__set(prop, value, current)
+ } else {
+ throw "can.Observe: Object does not exist"
+ }
+ },
+ __set: function(prop, value, current) {
+
+ // Otherwise, we are setting it on this `object`.
+ // TODO: Check if value is object and transform
+ // are we changing the value.
+ if (value !== current) {
+ // Check if we are adding this for the first time --
+ // if we are, we need to create an `add` event.
+ var changeType = this.__get().hasOwnProperty(prop) ? "set" : "add";
+
+ // Set the value on data.
+ this.___set(prop,
+
+ // If we are getting an object.
+ canMakeObserve(value) ?
+
+ // Hook it up to send event.
+ hookupBubble(value, prop, this) :
+ // Value is normal.
+ value);
+
+ if (changeType == "add") {
+ // If there is no current value, let others know that
+ // the the number of keys have changed
+
+ Observe.triggerBatch(this, "__keys", undefined);
+
+ }
+ // `batchTrigger` the change event.
+ this._triggerChange(prop, changeType, value, current);
+
+ //Observe.triggerBatch(this, prop, [value, current]);
+ // If we can stop listening to our old value, do it.
+ current && unhookup([current], this._cid);
+ }
+
+ },
+ // Directly sets a property on this `object`.
+ ___set: function(prop, val) {
+ this._data[prop] = val;
+ // Add property directly for easy writing.
+ // Check if its on the `prototype` so we don't overwrite methods like `attrs`.
+ if (!(prop in this.constructor.prototype)) {
+ this[prop] = val
+ }
+ },
+
+
+ bind: can.bindAndSetup,
+
+ unbind: can.unbindAndTeardown,
+
+ serialize: function() {
+ return serialize(this, 'serialize', {});
+ },
+
+ _attrs: function(props, remove) {
+
+ if (props === undefined) {
+ return serialize(this, 'attr', {})
+ }
+
+ props = can.extend({}, props);
+ var prop,
+ self = this,
+ newVal;
+ Observe.startBatch();
+ this.each(function(curVal, prop) {
+ newVal = props[prop];
+
+ // If we are merging...
+ if (newVal === undefined) {
+ remove && self.removeAttr(prop);
+ return;
+ }
+
+ if (self.__convert) {
+ newVal = self.__convert(prop, newVal)
+ }
+
+ // if we're dealing with models, want to call _set to let converter run
+ if (newVal instanceof can.Observe) {
+ self.__set(prop, newVal, curVal)
+ // if its an object, let attr merge
+ } else if (canMakeObserve(curVal) && canMakeObserve(newVal) && curVal.attr) {
+ curVal.attr(newVal, remove)
+ // otherwise just set
+ } else if (curVal != newVal) {
+ self.__set(prop, newVal, curVal)
+ }
+
+ delete props[prop];
+ })
+ // Add remaining props.
+ for (var prop in props) {
+ newVal = props[prop];
+ this._set(prop, newVal, true)
+ }
+ Observe.stopBatch()
+ return this;
+ },
+
+
+ compute: function(prop) {
+ return can.compute(this, prop);
+ }
+ });
+ // Helpers for `observable` lists.
+ var splice = [].splice,
+
+ list = Observe(
+
+ {
+ setup: function(instances, options) {
+ this.length = 0;
+ can.cid(this, ".observe")
+ this._init = 1;
+ if (can.isDeferred(instances)) {
+ this.replace(instances)
+ } else {
+ this.push.apply(this, can.makeArray(instances || []));
+ }
+ // this change needs to be ignored
+ this.bind('change' + this._cid, can.proxy(this._changes, this));
+ can.extend(this, options);
+ delete this._init;
+ },
+ _triggerChange: function(attr, how, newVal, oldVal) {
+
+ Observe.prototype._triggerChange.apply(this, arguments)
+ // `batchTrigger` direct add and remove events...
+ if (!~attr.indexOf('.')) {
+
+ if (how === 'add') {
+ Observe.triggerBatch(this, how, [newVal, +attr]);
+ Observe.triggerBatch(this, 'length', [this.length]);
+ } else if (how === 'remove') {
+ Observe.triggerBatch(this, how, [oldVal, +attr]);
+ Observe.triggerBatch(this, 'length', [this.length]);
+ } else {
+ Observe.triggerBatch(this, how, [newVal, +attr])
+ }
+
+ }
+
+ },
+ __get: function(attr) {
+ return attr ? this[attr] : this;
+ },
+ ___set: function(attr, val) {
+ this[attr] = val;
+ if (+attr >= this.length) {
+ this.length = (+attr + 1)
+ }
+ },
+ _each: function(callback) {
+ var data = this.__get();
+ for (var i = 0; i < data.length; i++) {
+ callback(data[i], i)
+ }
+ },
+ _bindsetup: makeBindSetup("*"),
+ // Returns the serialized form of this list.
+
+ serialize: function() {
+ return serialize(this, 'serialize', []);
+ },
+
+ splice: function(index, howMany) {
+ var args = can.makeArray(arguments),
+ i;
+
+ for (i = 2; i < args.length; i++) {
+ var val = args[i];
+ if (canMakeObserve(val)) {
+ args[i] = hookupBubble(val, "*", this, this.constructor.Observe, this.constructor)
+ }
+ }
+ if (howMany === undefined) {
+ howMany = args[1] = this.length - index;
+ }
+ var removed = splice.apply(this, args);
+ can.Observe.startBatch();
+ if (howMany > 0) {
+ this._triggerChange("" + index, "remove", undefined, removed);
+ unhookup(removed, this._cid);
+ }
+ if (args.length > 2) {
+ this._triggerChange("" + index, "add", args.slice(2), removed);
+ }
+ can.Observe.stopBatch();
+ return removed;
+ },
+
+ _attrs: function(items, remove) {
+ if (items === undefined) {
+ return serialize(this, 'attr', []);
+ }
+
+ // Create a copy.
+ items = can.makeArray(items);
+
+ Observe.startBatch();
+ this._updateAttrs(items, remove);
+ Observe.stopBatch()
+ },
+
+ _updateAttrs: function(items, remove) {
+ var len = Math.min(items.length, this.length);
+
+ for (var prop = 0; prop < len; prop++) {
+ var curVal = this[prop],
+ newVal = items[prop];
+
+ if (canMakeObserve(curVal) && canMakeObserve(newVal)) {
+ curVal.attr(newVal, remove)
+ } else if (curVal != newVal) {
+ this._set(prop, newVal)
+ } else {
+
+ }
+ }
+ if (items.length > this.length) {
+ // Add in the remaining props.
+ this.push.apply(this, items.slice(this.length));
+ } else if (items.length < this.length && remove) {
+ this.splice(items.length)
+ }
+ }
+ }),
+
+ // Converts to an `array` of arguments.
+ getArgs = function(args) {
+ return args[0] && can.isArray(args[0]) ?
+ args[0] :
+ can.makeArray(args);
+ };
+ // Create `push`, `pop`, `shift`, and `unshift`
+ can.each({
+
+ push: "length",
+
+ unshift: 0
+ },
+ // Adds a method
+ // `name` - The method name.
+ // `where` - Where items in the `array` should be added.
+
+ function(where, name) {
+ var orig = [][name]
+ list.prototype[name] = function() {
+ // Get the items being added.
+ var args = [],
+ // Where we are going to add items.
+ len = where ? this.length : 0,
+ i = arguments.length,
+ res,
+ val,
+ constructor = this.constructor;
+
+ // Go through and convert anything to an `observe` that needs to be converted.
+ while (i--) {
+ val = arguments[i];
+ args[i] = canMakeObserve(val) ?
+ hookupBubble(val, "*", this, this.constructor.Observe, this.constructor) :
+ val;
+ }
+
+ // Call the original method.
+ res = orig.apply(this, args);
+
+ if (!this.comparator || args.length) {
+
+ this._triggerChange("" + len, "add", args, undefined);
+ }
+
+ return res;
+ }
+ });
+
+ can.each({
+
+ pop: "length",
+
+ shift: 0
+ },
+ // Creates a `remove` type method
+
+ function(where, name) {
+ list.prototype[name] = function() {
+
+ var args = getArgs(arguments),
+ len = where && this.length ? this.length - 1 : 0;
+
+ var res = [][name].apply(this, args)
+
+ // Create a change where the args are
+ // `len` - Where these items were removed.
+ // `remove` - Items removed.
+ // `undefined` - The new values (there are none).
+ // `res` - The old, removed values (should these be unbound).
+ this._triggerChange("" + len, "remove", undefined, [res])
+
+ if (res && res.unbind) {
+ res.unbind("change" + this._cid)
+ }
+ return res;
+ }
+ });
+
+ can.extend(list.prototype, {
+
+ indexOf: function(item) {
+ this.attr('length')
+ return can.inArray(item, this)
+ },
+
+
+ join: [].join,
+
+
+ reverse: [].reverse,
+
+
+ slice: function() {
+ var temp = Array.prototype.slice.apply(this, arguments);
+ return new this.constructor(temp);
+ },
+
+
+ concat: function() {
+ var args = [];
+ can.each(can.makeArray(arguments), function(arg, i) {
+ args[i] = arg instanceof can.Observe.List ? arg.serialize() : arg;
+ });
+ return new this.constructor(Array.prototype.concat.apply(this.serialize(), args));
+ },
+
+
+ forEach: function(cb, thisarg) {
+ can.each(this, cb, thisarg || this);
+ },
+
+
+ replace: function(newList) {
+ if (can.isDeferred(newList)) {
+ newList.then(can.proxy(this.replace, this));
+ } else {
+ this.splice.apply(this, [0, this.length].concat(can.makeArray(newList || [])));
+ }
+
+ return this;
+ }
+ });
+
+ can.List = Observe.List = list;
+ Observe.setup = function() {
+ can.Construct.setup.apply(this, arguments);
+ // I would prefer not to do it this way. It should
+ // be using the attributes plugin to do this type of conversion.
+ this.List = Observe.List({
+ Observe: this
+ }, {});
+ }
+ return Observe;
+ })(__m3, __m8, __m1);
+
+ // ## observe/compute/compute.js
+ var __m9 = (function(can, bind) {
+
+ // returns the
+ // - observes and attr methods are called by func
+ // - the value returned by func
+ // ex: `{value: 100, observed: [{obs: o, attr: "completed"}]}`
+ var getValueAndObserved = function(func, self) {
+
+ var oldReading;
+ if (can.Observe) {
+ // Set a callback on can.Observe to know
+ // when an attr is read.
+ // Keep a reference to the old reader
+ // if there is one. This is used
+ // for nested live binding.
+ oldReading = can.Observe.__reading;
+ can.Observe.__reading = function(obj, attr) {
+ // Add the observe and attr that was read
+ // to `observed`
+ observed.push({
+ obj: obj,
+ attr: attr + ""
+ });
+ };
+ }
+
+ var observed = [],
+ // Call the "wrapping" function to get the value. `observed`
+ // will have the observe/attribute pairs that were read.
+ value = func.call(self);
+
+ // Set back so we are no longer reading.
+ if (can.Observe) {
+ can.Observe.__reading = oldReading;
+ }
+ return {
+ value: value,
+ observed: observed
+ };
+ },
+ // Calls `callback(newVal, oldVal)` everytime an observed property
+ // called within `getterSetter` is changed and creates a new result of `getterSetter`.
+ // Also returns an object that can teardown all event handlers.
+ computeBinder = function(getterSetter, context, callback, computeState) {
+ // track what we are observing
+ var observing = {},
+ // a flag indicating if this observe/attr pair is already bound
+ matched = true,
+ // the data to return
+ data = {
+ // we will maintain the value while live-binding is taking place
+ value: undefined,
+ // a teardown method that stops listening
+ teardown: function() {
+ for (var name in observing) {
+ var ob = observing[name];
+ ob.observe.obj.unbind(ob.observe.attr, onchanged);
+ delete observing[name];
+ }
+ }
+ },
+ batchNum;
+
+ // when a property value is changed
+ var onchanged = function(ev) {
+ // If the compute is no longer bound (because the same change event led to an unbind)
+ // then do not call getValueAndBind, or we will leak bindings.
+ if (computeState && !computeState.bound) {
+ return;
+ }
+ if (ev.batchNum === undefined || ev.batchNum !== batchNum) {
+ // store the old value
+ var oldValue = data.value,
+ // get the new value
+ newvalue = getValueAndBind();
+
+ // update the value reference (in case someone reads)
+ data.value = newvalue;
+ // if a change happened
+ if (newvalue !== oldValue) {
+ callback(newvalue, oldValue);
+ }
+ batchNum = batchNum = ev.batchNum;
+ }
+
+
+ };
+
+ // gets the value returned by `getterSetter` and also binds to any attributes
+ // read by the call
+ var getValueAndBind = function() {
+ var info = getValueAndObserved(getterSetter, context),
+ newObserveSet = info.observed;
+
+ var value = info.value;
+ matched = !matched;
+
+ // go through every attribute read by this observe
+ can.each(newObserveSet, function(ob) {
+ // if the observe/attribute pair is being observed
+ if (observing[ob.obj._cid + "|" + ob.attr]) {
+ // mark at as observed
+ observing[ob.obj._cid + "|" + ob.attr].matched = matched;
+ } else {
+ // otherwise, set the observe/attribute on oldObserved, marking it as being observed
+ observing[ob.obj._cid + "|" + ob.attr] = {
+ matched: matched,
+ observe: ob
+ };
+ ob.obj.bind(ob.attr, onchanged);
+ }
+ });
+
+ // Iterate through oldObserved, looking for observe/attributes
+ // that are no longer being bound and unbind them
+ for (var name in observing) {
+ var ob = observing[name];
+ if (ob.matched !== matched) {
+ ob.observe.obj.unbind(ob.observe.attr, onchanged);
+ delete observing[name];
+ }
+ }
+ return value;
+ };
+ // set the initial value
+ data.value = getValueAndBind();
+
+ data.isListening = !can.isEmptyObject(observing);
+ return data;
+ }
+
+ // if no one is listening ... we can not calculate every time
+
+ can.compute = function(getterSetter, context, eventName) {
+ if (getterSetter && getterSetter.isComputed) {
+ return getterSetter;
+ }
+ // stores the result of computeBinder
+ var computedData,
+ // how many listeners to this this compute
+ bindings = 0,
+ // the computed object
+ computed,
+ // an object that keeps track if the computed is bound
+ // onchanged needs to know this. It's possible a change happens and results in
+ // something that unbinds the compute, it needs to not to try to recalculate who it
+ // is listening to
+ computeState = {
+ bound: false,
+ // true if this compute is calculated from other computes and observes
+ hasDependencies: false
+ },
+ // The following functions are overwritten depending on how compute() is called
+ // a method to setup listening
+ on = function() {},
+ // a method to teardown listening
+ off = function() {},
+ // the current cached value (only valid if bound = true)
+ value,
+ // how to read the value
+ get = function() {
+ return value
+ },
+ // sets the value
+ set = function(newVal) {
+ value = newVal;
+ },
+ // this compute can be a dependency of other computes
+ canReadForChangeEvent = true;
+
+ computed = function(newVal) {
+ // setting ...
+ if (arguments.length) {
+ // save a reference to the old value
+ var old = value;
+
+ // setter may return a value if
+ // setter is for a value maintained exclusively by this compute
+ var setVal = set.call(context, newVal, old);
+
+ // if this has dependencies return the current value
+ if (computed.hasDependencies) {
+ return get.call(context);
+ }
+
+ if (setVal === undefined) {
+ // it's possible, like with the DOM, setting does not
+ // fire a change event, so we must read
+ value = get.call(context);
+ } else {
+ value = setVal;
+ }
+ // fire the change
+ if (old !== value) {
+ can.Observe.triggerBatch(computed, "change", [value, old]);
+ }
+ return value;
+ } else {
+ // Let others know to listen to changes in this compute
+ if (can.Observe.__reading && canReadForChangeEvent) {
+ can.Observe.__reading(computed, 'change');
+ }
+ // if we are bound, use the cached value
+ if (computeState.bound) {
+ return value;
+ } else {
+ return get.call(context);
+ }
+ }
+ }
+ if (typeof getterSetter === "function") {
+ set = getterSetter;
+ get = getterSetter;
+ canReadForChangeEvent = eventName === false ? false : true;
+ computed.hasDependencies = false;
+ on = function(update) {
+ computedData = computeBinder(getterSetter, context || this, update, computeState);
+ computed.hasDependencies = computedData.isListening
+ value = computedData.value;
+ }
+ off = function() {
+ computedData.teardown();
+ }
+ } else if (context) {
+
+ if (typeof context == "string") {
+ // `can.compute(obj, "propertyName", [eventName])`
+
+ var propertyName = context,
+ isObserve = getterSetter instanceof can.Observe;
+ if (isObserve) {
+ computed.hasDependencies = true;
+ }
+ get = function() {
+ if (isObserve) {
+ return getterSetter.attr(propertyName);
+ } else {
+ return getterSetter[propertyName];
+ }
+ }
+ set = function(newValue) {
+ if (isObserve) {
+ getterSetter.attr(propertyName, newValue)
+ } else {
+ getterSetter[propertyName] = newValue;
+ }
+ }
+ var handler;
+ on = function(update) {
+ handler = function() {
+ update(get(), value)
+ };
+ can.bind.call(getterSetter, eventName || propertyName, handler)
+
+ // use getValueAndObserved because
+ // we should not be indicating that some parent
+ // reads this property if it happens to be binding on it
+ value = getValueAndObserved(get).value
+ }
+ off = function() {
+ can.unbind.call(getterSetter, eventName || propertyName, handler)
+ }
+
+ } else {
+ // `can.compute(initialValue, setter)`
+ if (typeof context === "function") {
+ value = getterSetter;
+ set = context;
+ } else {
+ // `can.compute(initialValue,{get:, set:, on:, off:})`
+ value = getterSetter;
+ var options = context;
+ get = options.get || get;
+ set = options.set || set;
+ on = options.on || on;
+ off = options.off || off;
+ }
+
+ }
+
+
+
+ } else {
+ // `can.compute(5)`
+ value = getterSetter;
+ }
+
+ computed.isComputed = true;
+
+ can.cid(computed, "compute")
+
+ var updater = function(newValue, oldValue) {
+ value = newValue;
+ // might need a way to look up new and oldVal
+ can.Observe.triggerBatch(computed, "change", [newValue, oldValue])
+ }
+
+ return can.extend(computed, {
+ _bindsetup: function() {
+ computeState.bound = true;
+ // setup live-binding
+ on.call(this, updater)
+ },
+ _bindteardown: function() {
+ off.call(this, updater)
+ computeState.bound = false;
+ },
+
+ bind: can.bindAndSetup,
+
+ unbind: can.unbindAndTeardown
+ });
+ };
+ can.compute.binder = computeBinder;
+ return can.compute;
+ })(__m3, __m8);
+
+ // ## model/model.js
+ var __m10 = (function(can) {
+
+ // ## model.js
+ // `can.Model`
+ // _A `can.Observe` that connects to a RESTful interface._
+ // Generic deferred piping function
+
+ var pipe = function(def, model, func) {
+ var d = new can.Deferred();
+ def.then(function() {
+ var args = can.makeArray(arguments);
+ args[0] = model[func](args[0]);
+ d.resolveWith(d, args);
+ }, function() {
+ d.rejectWith(this, arguments);
+ });
+
+ if (typeof def.abort === 'function') {
+ d.abort = function() {
+ return def.abort();
+ }
+ }
+
+ return d;
+ },
+ modelNum = 0,
+ ignoreHookup = /change.observe\d+/,
+ getId = function(inst) {
+ // Instead of using attr, use __get for performance.
+ // Need to set reading
+ can.Observe.__reading && can.Observe.__reading(inst, inst.constructor.id)
+ return inst.__get(inst.constructor.id);
+ },
+ // Ajax `options` generator function
+ ajax = function(ajaxOb, data, type, dataType, success, error) {
+
+ var params = {};
+
+ // If we get a string, handle it.
+ if (typeof ajaxOb == "string") {
+ // If there's a space, it's probably the type.
+ var parts = ajaxOb.split(/\s+/);
+ params.url = parts.pop();
+ if (parts.length) {
+ params.type = parts.pop();
+ }
+ } else {
+ can.extend(params, ajaxOb);
+ }
+
+ // If we are a non-array object, copy to a new attrs.
+ params.data = typeof data == "object" && !can.isArray(data) ?
+ can.extend(params.data || {}, data) : data;
+
+ // Get the url with any templated values filled out.
+ params.url = can.sub(params.url, params.data, true);
+
+ return can.ajax(can.extend({
+ type: type || "post",
+ dataType: dataType || "json",
+ success: success,
+ error: error
+ }, params));
+ },
+ makeRequest = function(self, type, success, error, method) {
+ var args;
+ // if we pass an array as `self` it it means we are coming from
+ // the queued request, and we're passing already serialized data
+ // self's signature will be: [self, serializedData]
+ if (can.isArray(self)) {
+ args = self[1];
+ self = self[0];
+ } else {
+ args = self.serialize();
+ }
+ args = [args];
+ var deferred,
+ // The model.
+ model = self.constructor,
+ jqXHR;
+
+ // `destroy` does not need data.
+ if (type == 'destroy') {
+ args.shift();
+ }
+ // `update` and `destroy` need the `id`.
+ if (type !== 'create') {
+ args.unshift(getId(self));
+ }
+
+
+ jqXHR = model[type].apply(model, args);
+
+ deferred = jqXHR.pipe(function(data) {
+ self[method || type + "d"](data, jqXHR);
+ return self;
+ });
+
+ // Hook up `abort`
+ if (jqXHR.abort) {
+ deferred.abort = function() {
+ jqXHR.abort();
+ };
+ }
+
+ deferred.then(success, error);
+ return deferred;
+ },
+
+ // This object describes how to make an ajax request for each ajax method.
+ // The available properties are:
+ // `url` - The default url to use as indicated as a property on the model.
+ // `type` - The default http request type
+ // `data` - A method that takes the `arguments` and returns `data` used for ajax.
+
+ ajaxMethods = {
+
+ create: {
+ url: "_shortName",
+ type: "post"
+ },
+
+ update: {
+ data: function(id, attrs) {
+ attrs = attrs || {};
+ var identity = this.id;
+ if (attrs[identity] && attrs[identity] !== id) {
+ attrs["new" + can.capitalize(id)] = attrs[identity];
+ delete attrs[identity];
+ }
+ attrs[identity] = id;
+ return attrs;
+ },
+ type: "put"
+ },
+
+ destroy: {
+ type: "delete",
+ data: function(id) {
+ var args = {};
+ args.id = args[this.id] = id;
+ return args;
+ }
+ },
+
+ findAll: {
+ url: "_shortName"
+ },
+
+ findOne: {}
+ },
+ // Makes an ajax request `function` from a string.
+ // `ajaxMethod` - The `ajaxMethod` object defined above.
+ // `str` - The string the user provided. Ex: `findAll: "/recipes.json"`.
+ ajaxMaker = function(ajaxMethod, str) {
+ // Return a `function` that serves as the ajax method.
+ return function(data) {
+ // If the ajax method has it's own way of getting `data`, use that.
+ data = ajaxMethod.data ?
+ ajaxMethod.data.apply(this, arguments) :
+ // Otherwise use the data passed in.
+ data;
+ // Return the ajax method with `data` and the `type` provided.
+ return ajax(str || this[ajaxMethod.url || "_url"], data, ajaxMethod.type || "get")
+ }
+ }
+
+
+
+ can.Model = can.Observe({
+ fullName: "can.Model",
+ _reqs: 0,
+ setup: function(base) {
+ // create store here if someone wants to use model without inheriting from it
+ this.store = {};
+ can.Observe.setup.apply(this, arguments);
+ // Set default list as model list
+ if (!can.Model) {
+ return;
+ }
+ this.List = ML({
+ Observe: this
+ }, {});
+ var self = this,
+ clean = can.proxy(this._clean, self);
+
+
+ // go through ajax methods and set them up
+ can.each(ajaxMethods, function(method, name) {
+ // if an ajax method is not a function, it's either
+ // a string url like findAll: "/recipes" or an
+ // ajax options object like {url: "/recipes"}
+ if (!can.isFunction(self[name])) {
+ // use ajaxMaker to convert that into a function
+ // that returns a deferred with the data
+ self[name] = ajaxMaker(method, self[name]);
+ }
+ // check if there's a make function like makeFindAll
+ // these take deferred function and can do special
+ // behavior with it (like look up data in a store)
+ if (self["make" + can.capitalize(name)]) {
+ // pass the deferred method to the make method to get back
+ // the "findAll" method.
+ var newMethod = self["make" + can.capitalize(name)](self[name]);
+ can.Construct._overwrite(self, base, name, function() {
+ // increment the numer of requests
+ can.Model._reqs++;
+ var def = newMethod.apply(this, arguments);
+ var then = def.then(clean, clean);
+ then.abort = def.abort;
+
+ // attach abort to our then and return it
+ return then;
+ })
+ }
+ });
+
+ if (self.fullName == "can.Model" || !self.fullName) {
+ self.fullName = "Model" + (++modelNum);
+ }
+ // Add ajax converters.
+ can.Model._reqs = 0;
+ this._url = this._shortName + "/{" + this.id + "}"
+ },
+ _ajax: ajaxMaker,
+ _makeRequest: makeRequest,
+ _clean: function() {
+ can.Model._reqs--;
+ if (!can.Model._reqs) {
+ for (var id in this.store) {
+ if (!this.store[id]._bindings) {
+ delete this.store[id];
+ }
+ }
+ }
+ return arguments[0];
+ },
+
+ models: function(instancesRawData, oldList) {
+ // until "end of turn", increment reqs counter so instances will be added to the store
+ can.Model._reqs++;
+ if (!instancesRawData) {
+ return;
+ }
+
+ if (instancesRawData instanceof this.List) {
+ return instancesRawData;
+ }
+
+ // Get the list type.
+ var self = this,
+ tmp = [],
+ res = oldList instanceof can.Observe.List ? oldList : new(self.List || ML),
+ // Did we get an `array`?
+ arr = can.isArray(instancesRawData),
+
+ // Did we get a model list?
+ ml = (instancesRawData instanceof ML),
+
+ // Get the raw `array` of objects.
+ raw = arr ?
+
+ // If an `array`, return the `array`.
+ instancesRawData :
+
+ // Otherwise if a model list.
+ (ml ?
+
+ // Get the raw objects from the list.
+ instancesRawData.serialize() :
+
+ // Get the object's data.
+ instancesRawData.data),
+ i = 0;
+
+
+
+ if (res.length) {
+ res.splice(0);
+ }
+
+ can.each(raw, function(rawPart) {
+ tmp.push(self.model(rawPart));
+ });
+
+ // We only want one change event so push everything at once
+ res.push.apply(res, tmp);
+
+ if (!arr) { // Push other stuff onto `array`.
+ can.each(instancesRawData, function(val, prop) {
+ if (prop !== 'data') {
+ res.attr(prop, val);
+ }
+ })
+ }
+ // at "end of turn", clean up the store
+ setTimeout(can.proxy(this._clean, this), 1);
+ return res;
+ },
+
+ model: function(attributes) {
+ if (!attributes) {
+ return;
+ }
+ if (attributes instanceof this) {
+ attributes = attributes.serialize();
+ }
+ var id = attributes[this.id],
+ model = (id || id === 0) && this.store[id] ?
+ this.store[id].attr(attributes, this.removeAttr || false) : new this(attributes);
+ if (can.Model._reqs) {
+ this.store[attributes[this.id]] = model;
+ }
+ return model;
+ }
+ },
+
+
+ {
+
+ isNew: function() {
+ var id = getId(this);
+ return !(id || id === 0); // If `null` or `undefined`
+ },
+
+ save: function(success, error) {
+ return makeRequest(this, this.isNew() ? 'create' : 'update', success, error);
+ },
+
+ destroy: function(success, error) {
+ if (this.isNew()) {
+ var self = this;
+ var def = can.Deferred();
+ def.then(success, error);
+ return def.done(function(data) {
+ self.destroyed(data)
+ }).resolve(self);
+ }
+ return makeRequest(this, 'destroy', success, error, 'destroyed');
+ },
+
+ _bindsetup: function() {
+ this.constructor.store[this.__get(this.constructor.id)] = this;
+ return can.Observe.prototype._bindsetup.apply(this, arguments);
+ },
+
+ _bindteardown: function() {
+ delete this.constructor.store[getId(this)];
+ return can.Observe.prototype._bindteardown.apply(this, arguments)
+ },
+ // Change `id`.
+ ___set: function(prop, val) {
+ can.Observe.prototype.___set.call(this, prop, val)
+ // If we add an `id`, move it to the store.
+ if (prop === this.constructor.id && this._bindings) {
+ this.constructor.store[getId(this)] = this;
+ }
+ }
+ });
+
+ can.each({
+ makeFindAll: "models",
+ makeFindOne: "model",
+ makeCreate: "model",
+ makeUpdate: "model"
+ }, function(method, name) {
+ can.Model[name] = function(oldMethod) {
+ return function() {
+ var args = can.makeArray(arguments),
+ oldArgs = can.isFunction(args[1]) ? args.splice(0, 1) : args.splice(0, 2),
+ def = pipe(oldMethod.apply(this, oldArgs), this, method);
+ def.then(args[0], args[1]);
+ // return the original promise
+ return def;
+ };
+ };
+ });
+
+ can.each([
+
+ "created",
+
+ "updated",
+
+ "destroyed"
+ ], function(funcName) {
+ can.Model.prototype[funcName] = function(attrs) {
+ var stub,
+ constructor = this.constructor;
+
+ // Update attributes if attributes have been passed
+ stub = attrs && typeof attrs == 'object' && this.attr(attrs.attr ? attrs.attr() : attrs);
+
+ // triggers change event that bubble's like
+ // handler( 'change','1.destroyed' ). This is used
+ // to remove items on destroyed from Model Lists.
+ // but there should be a better way.
+ can.trigger(this, "change", funcName)
+
+
+ // Call event on the instance's Class
+ can.trigger(constructor, funcName, this);
+ };
+ });
+
+ // Model lists are just like `Observe.List` except that when their items are
+ // destroyed, it automatically gets removed from the list.
+
+ var ML = can.Model.List = can.Observe.List({
+ setup: function(params) {
+ if (can.isPlainObject(params) && !can.isArray(params)) {
+ can.Observe.List.prototype.setup.apply(this);
+ this.replace(this.constructor.Observe.findAll(params))
+ } else {
+ can.Observe.List.prototype.setup.apply(this, arguments);
+ }
+ },
+ _changes: function(ev, attr) {
+ can.Observe.List.prototype._changes.apply(this, arguments);
+ if (/\w+\.destroyed/.test(attr)) {
+ var index = this.indexOf(ev.target);
+ if (index != -1) {
+ this.splice(index, 1);
+ }
+ }
+ }
+ })
+
+ return can.Model;
+ })(__m3, __m7);
+
+ // ## view/view.js
+ var __m11 = (function(can) {
+ // ## view.js
+ // `can.view`
+ // _Templating abstraction._
+
+ var isFunction = can.isFunction,
+ makeArray = can.makeArray,
+ // Used for hookup `id`s.
+ hookupId = 1,
+
+ $view = can.view = can.template = function(view, data, helpers, callback) {
+ // If helpers is a `function`, it is actually a callback.
+ if (isFunction(helpers)) {
+ callback = helpers;
+ helpers = undefined;
+ }
+
+ var pipe = function(result) {
+ return $view.frag(result);
+ },
+ // In case we got a callback, we need to convert the can.view.render
+ // result to a document fragment
+ wrapCallback = isFunction(callback) ? function(frag) {
+ callback(pipe(frag));
+ } : null,
+ // Get the result.
+ result = $view.render(view, data, helpers, wrapCallback),
+ deferred = can.Deferred();
+
+ if (isFunction(result)) {
+ return result;
+ }
+
+ if (can.isDeferred(result)) {
+ result.then(function(result, data) {
+ deferred.resolve.call(deferred, pipe(result), data);
+ }, function() {
+ deferred.fail.apply(deferred, arguments);
+ });
+ return deferred;
+ }
+
+ // Convert it into a dom frag.
+ return pipe(result);
+ };
+
+ can.extend($view, {
+ // creates a frag and hooks it up all at once
+ frag: function(result, parentNode) {
+ return $view.hookup($view.fragment(result), parentNode);
+ },
+
+ // simply creates a frag
+ // this is used internally to create a frag
+ // insert it
+ // then hook it up
+ fragment: function(result) {
+ var frag = can.buildFragment(result, document.body);
+ // If we have an empty frag...
+ if (!frag.childNodes.length) {
+ frag.appendChild(document.createTextNode(''));
+ }
+ return frag;
+ },
+
+ // Convert a path like string into something that's ok for an `element` ID.
+ toId: function(src) {
+ return can.map(src.toString().split(/\/|\./g), function(part) {
+ // Dont include empty strings in toId functions
+ if (part) {
+ return part;
+ }
+ }).join("_");
+ },
+
+ hookup: function(fragment, parentNode) {
+ var hookupEls = [],
+ id,
+ func;
+
+ // Get all `childNodes`.
+ can.each(fragment.childNodes ? can.makeArray(fragment.childNodes) : fragment, function(node) {
+ if (node.nodeType === 1) {
+ hookupEls.push(node);
+ hookupEls.push.apply(hookupEls, can.makeArray(node.getElementsByTagName('*')));
+ }
+ });
+
+ // Filter by `data-view-id` attribute.
+ can.each(hookupEls, function(el) {
+ if (el.getAttribute && (id = el.getAttribute('data-view-id')) && (func = $view.hookups[id])) {
+ func(el, parentNode, id);
+ delete $view.hookups[id];
+ el.removeAttribute('data-view-id');
+ }
+ });
+
+ return fragment;
+ },
+
+
+ hookups: {},
+
+
+ hook: function(cb) {
+ $view.hookups[++hookupId] = cb;
+ return " data-view-id='" + hookupId + "'";
+ },
+
+
+ cached: {},
+
+ cachedRenderers: {},
+
+
+ cache: true,
+
+
+ register: function(info) {
+ this.types["." + info.suffix] = info;
+ },
+
+ types: {},
+
+
+ ext: ".ejs",
+
+
+ registerScript: function() {},
+
+
+ preload: function() {},
+
+
+ render: function(view, data, helpers, callback) {
+ // If helpers is a `function`, it is actually a callback.
+ if (isFunction(helpers)) {
+ callback = helpers;
+ helpers = undefined;
+ }
+
+ // See if we got passed any deferreds.
+ var deferreds = getDeferreds(data);
+
+ if (deferreds.length) { // Does data contain any deferreds?
+ // The deferred that resolves into the rendered content...
+ var deferred = new can.Deferred(),
+ dataCopy = can.extend({}, data);
+
+ // Add the view request to the list of deferreds.
+ deferreds.push(get(view, true))
+
+ // Wait for the view and all deferreds to finish...
+ can.when.apply(can, deferreds).then(function(resolved) {
+ // Get all the resolved deferreds.
+ var objs = makeArray(arguments),
+ // Renderer is the last index of the data.
+ renderer = objs.pop(),
+ // The result of the template rendering with data.
+ result;
+
+ // Make data look like the resolved deferreds.
+ if (can.isDeferred(data)) {
+ dataCopy = usefulPart(resolved);
+ } else {
+ // Go through each prop in data again and
+ // replace the defferreds with what they resolved to.
+ for (var prop in data) {
+ if (can.isDeferred(data[prop])) {
+ dataCopy[prop] = usefulPart(objs.shift());
+ }
+ }
+ }
+
+ // Get the rendered result.
+ result = renderer(dataCopy, helpers);
+
+ // Resolve with the rendered view.
+ deferred.resolve(result, dataCopy);
+
+ // If there's a `callback`, call it back with the result.
+ callback && callback(result, dataCopy);
+ }, function() {
+ deferred.reject.apply(deferred, arguments)
+ });
+ // Return the deferred...
+ return deferred;
+ } else {
+ // No deferreds! Render this bad boy.
+ var response,
+ // If there's a `callback` function
+ async = isFunction(callback),
+ // Get the `view` type
+ deferred = get(view, async);
+
+ // If we are `async`...
+ if (async) {
+ // Return the deferred
+ response = deferred;
+ // And fire callback with the rendered result.
+ deferred.then(function(renderer) {
+ callback(data ? renderer(data, helpers) : renderer);
+ })
+ } else {
+ // if the deferred is resolved, call the cached renderer instead
+ // this is because it's possible, with recursive deferreds to
+ // need to render a view while its deferred is _resolving_. A _resolving_ deferred
+ // is a deferred that was just resolved and is calling back it's success callbacks.
+ // If a new success handler is called while resoliving, it does not get fired by
+ // jQuery's deferred system. So instead of adding a new callback
+ // we use the cached renderer.
+ // We also add __view_id on the deferred so we can look up it's cached renderer.
+ // In the future, we might simply store either a deferred or the cached result.
+ if (deferred.state() === "resolved" && deferred.__view_id) {
+ var currentRenderer = $view.cachedRenderers[deferred.__view_id];
+ return data ? currentRenderer(data, helpers) : currentRenderer;
+ } else {
+ // Otherwise, the deferred is complete, so
+ // set response to the result of the rendering.
+ deferred.then(function(renderer) {
+ response = data ? renderer(data, helpers) : renderer;
+ });
+ }
+ }
+
+ return response;
+ }
+ },
+
+
+ registerView: function(id, text, type, def) {
+ // Get the renderer function.
+ var func = (type || $view.types[$view.ext]).renderer(id, text);
+ def = def || new can.Deferred();
+
+ // Cache if we are caching.
+ if ($view.cache) {
+ $view.cached[id] = def;
+ def.__view_id = id;
+ $view.cachedRenderers[id] = func;
+ }
+
+ // Return the objects for the response's `dataTypes`
+ // (in this case view).
+ return def.resolve(func);
+ }
+ });
+
+ // Makes sure there's a template, if not, have `steal` provide a warning.
+ var checkText = function(text, url) {
+ if (!text.length) {
+
+ throw "can.view: No template or empty template:" + url;
+ }
+ },
+ // `Returns a `view` renderer deferred.
+ // `url` - The url to the template.
+ // `async` - If the ajax request should be asynchronous.
+ // Returns a deferred.
+ get = function(url, async) {
+ var suffix = url.match(/\.[\w\d]+$/),
+ type,
+ // If we are reading a script element for the content of the template,
+ // `el` will be set to that script element.
+ el,
+ // A unique identifier for the view (used for caching).
+ // This is typically derived from the element id or
+ // the url for the template.
+ id,
+ // The ajax request used to retrieve the template content.
+ jqXHR;
+
+ //If the url has a #, we assume we want to use an inline template
+ //from a script element and not current page's HTML
+ if (url.match(/^#/)) {
+ url = url.substr(1);
+ }
+ // If we have an inline template, derive the suffix from the `text/???` part.
+ // This only supports `<script>` tags.
+ if (el = document.getElementById(url)) {
+ suffix = "." + el.type.match(/\/(x\-)?(.+)/)[2];
+ }
+
+ // If there is no suffix, add one.
+ if (!suffix && !$view.cached[url]) {
+ url += (suffix = $view.ext);
+ }
+
+ if (can.isArray(suffix)) {
+ suffix = suffix[0]
+ }
+
+ // Convert to a unique and valid id.
+ id = $view.toId(url);
+
+ // If an absolute path, use `steal` to get it.
+ // You should only be using `//` if you are using `steal`.
+ if (url.match(/^\/\//)) {
+ var sub = url.substr(2);
+ url = !window.steal ?
+ sub :
+ steal.config().root.mapJoin("" + steal.id(sub));
+ }
+
+ // Set the template engine type.
+ type = $view.types[suffix];
+
+ // If it is cached,
+ if ($view.cached[id]) {
+ // Return the cached deferred renderer.
+ return $view.cached[id];
+
+ // Otherwise if we are getting this from a `<script>` element.
+ } else if (el) {
+ // Resolve immediately with the element's `innerHTML`.
+ return $view.registerView(id, el.innerHTML, type);
+ } else {
+ // Make an ajax request for text.
+ var d = new can.Deferred();
+ can.ajax({
+ async: async,
+ url: url,
+ dataType: "text",
+ error: function(jqXHR) {
+ checkText("", url);
+ d.reject(jqXHR);
+ },
+ success: function(text) {
+ // Make sure we got some text back.
+ checkText(text, url);
+ $view.registerView(id, text, type, d)
+ }
+ });
+ return d;
+ }
+ },
+ // Gets an `array` of deferreds from an `object`.
+ // This only goes one level deep.
+ getDeferreds = function(data) {
+ var deferreds = [];
+
+ // pull out deferreds
+ if (can.isDeferred(data)) {
+ return [data]
+ } else {
+ for (var prop in data) {
+ if (can.isDeferred(data[prop])) {
+ deferreds.push(data[prop]);
+ }
+ }
+ }
+ return deferreds;
+ },
+ // Gets the useful part of a resolved deferred.
+ // This is for `model`s and `can.ajax` that resolve to an `array`.
+ usefulPart = function(resolved) {
+ return can.isArray(resolved) && resolved[1] === 'success' ? resolved[0] : resolved
+ };
+
+ //!steal-pluginify-remove-start
+ if (window.steal) {
+ steal.type("view js", function(options, success, error) {
+ var type = $view.types["." + options.type],
+ id = $view.toId(options.id);
+
+ options.text = "steal('" + (type.plugin || "can/view/" + options.type) + "',function(can){return " + "can.view.preload('" + id + "'," + options.text + ");\n})";
+ success();
+ })
+ }
+ //!steal-pluginify-remove-end
+
+ can.extend($view, {
+ register: function(info) {
+ this.types["." + info.suffix] = info;
+
+ //!steal-pluginify-remove-start
+ if (window.steal) {
+ steal.type(info.suffix + " view js", function(options, success, error) {
+ var type = $view.types["." + options.type],
+ id = $view.toId(options.id + '');
+
+ options.text = type.script(id, options.text)
+ success();
+ })
+ };
+ //!steal-pluginify-remove-end
+
+ $view[info.suffix] = function(id, text) {
+ if (!text) {
+ // Return a nameless renderer
+ var renderer = function() {
+ return $view.frag(renderer.render.apply(this, arguments));
+ }
+ renderer.render = function() {
+ var renderer = info.renderer(null, id);
+ return renderer.apply(renderer, arguments);
+ }
+ return renderer;
+ }
+
+ $view.preload(id, info.renderer(id, text));
+ return can.view(id);
+ }
+ },
+ registerScript: function(type, id, src) {
+ return "can.view.preload('" + id + "'," + $view.types["." + type].script(id, src) + ");";
+ },
+ preload: function(id, renderer) {
+ $view.cached[id] = new can.Deferred().resolve(function(data, helpers) {
+ return renderer.call(data, data, helpers);
+ });
+
+ function frag() {
+ return $view.frag(renderer.apply(this, arguments));
+ }
+ // expose the renderer for mustache
+ frag.render = renderer;
+ return frag;
+ }
+
+ });
+
+ return can;
+ })(__m3);
+
+ // ## view/elements.js
+ var __m14 = (function() {
+
+ var elements = {
+ tagToContentPropMap: {
+ option: "textContent" in document.createElement("option") ? "textContent" : "innerText",
+ textarea: "value"
+ },
+
+ attrMap: {
+ "class": "className",
+ "value": "value",
+ "innerText": "innerText",
+ "textContent": "textContent",
+ "checked": true,
+ "disabled": true,
+ "readonly": true,
+ "required": true
+ },
+ // elements whos default value we should set
+ defaultValue: ["input", "textarea"],
+ // a map of parent element to child elements
+ tagMap: {
+ "": "span",
+ table: "tbody",
+ tr: "td",
+ ol: "li",
+ ul: "li",
+ tbody: "tr",
+ thead: "tr",
+ tfoot: "tr",
+ select: "option",
+ optgroup: "option"
+ },
+ // a tag's parent element
+ reverseTagMap: {
+ tr: "tbody",
+ option: "select",
+ td: "tr",
+ th: "tr",
+ li: "ul"
+ },
+
+ getParentNode: function(el, defaultParentNode) {
+ return defaultParentNode && el.parentNode.nodeType === 11 ? defaultParentNode : el.parentNode;
+ },
+ // set an attribute on an element
+ setAttr: function(el, attrName, val) {
+ var tagName = el.nodeName.toString().toLowerCase(),
+ prop = elements.attrMap[attrName];
+ // if this is a special property
+ if (prop === true) {
+ el[attrName] = true;
+ } else if (prop) {
+ // set the value as true / false
+ el[prop] = val;
+ if (prop === "value" && can.inArray(tagName, elements.defaultValue) >= 0) {
+ el.defaultValue = val;
+ }
+ } else {
+ el.setAttribute(attrName, val);
+ }
+ },
+ // gets the value of an attribute
+ getAttr: function(el, attrName) {
+ // Default to a blank string for IE7/8
+ return (elements.attrMap[attrName] && el[elements.attrMap[attrName]] ?
+ el[elements.attrMap[attrName]] :
+ el.getAttribute(attrName)) || '';
+ },
+ // removes the attribute
+ removeAttr: function(el, attrName) {
+ if (elements.attrMap[attrName] === true) {
+ el[attrName] = false;
+ } else {
+ el.removeAttribute(attrName);
+ }
+ },
+ contentText: function(text) {
+ if (typeof text == 'string') {
+ return text;
+ }
+ // If has no value, return an empty string.
+ if (!text && text !== 0) {
+ return '';
+ }
+ return "" + text;
+ }
+ };
+
+ return elements;
+ })();
+
+ // ## view/scanner.js
+ var __m13 = (function(can, elements) {
+
+ var newLine = /(\r|\n)+/g,
+ // Escapes characters starting with `\`.
+ clean = function(content) {
+ return content
+ .split('\\').join("\\\\")
+ .split("\n").join("\\n")
+ .split('"').join('\\"')
+ .split("\t").join("\\t");
+ },
+ // Returns a tagName to use as a temporary placeholder for live content
+ // looks forward ... could be slow, but we only do it when necessary
+ getTag = function(tagName, tokens, i) {
+ // if a tagName is provided, use that
+ if (tagName) {
+ return tagName;
+ } else {
+ // otherwise go searching for the next two tokens like "<",TAG
+ while (i < tokens.length) {
+ if (tokens[i] == "<" && elements.reverseTagMap[tokens[i + 1]]) {
+ return elements.reverseTagMap[tokens[i + 1]];
+ }
+ i++;
+ }
+ }
+ return '';
+ },
+ bracketNum = function(content) {
+ return (--content.split("{").length) - (--content.split("}").length);
+ },
+ myEval = function(script) {
+ eval(script);
+ },
+ attrReg = /([^\s]+)[\s]*=[\s]*$/,
+ // Commands for caching.
+ startTxt = 'var ___v1ew = [];',
+ finishTxt = "return ___v1ew.join('')",
+ put_cmd = "___v1ew.push(",
+ insert_cmd = put_cmd,
+ // Global controls (used by other functions to know where we are).
+ // Are we inside a tag?
+ htmlTag = null,
+ // Are we within a quote within a tag?
+ quote = null,
+ // What was the text before the current quote? (used to get the `attr` name)
+ beforeQuote = null,
+ // Whether a rescan is in progress
+ rescan = null,
+ // Used to mark where the element is.
+ status = function() {
+ // `t` - `1`.
+ // `h` - `0`.
+ // `q` - String `beforeQuote`.
+ return quote ? "'" + beforeQuote.match(attrReg)[1] + "'" : (htmlTag ? 1 : 0);
+ };
+
+ can.view.Scanner = Scanner = function(options) {
+ // Set options on self
+ can.extend(this, {
+ text: {},
+ tokens: []
+ }, options);
+
+ // Cache a token lookup
+ this.tokenReg = [];
+ this.tokenSimple = {
+ "<": "<",
+ ">": ">",
+ '"': '"',
+ "'": "'"
+ };
+ this.tokenComplex = [];
+ this.tokenMap = {};
+ for (var i = 0, token; token = this.tokens[i]; i++) {
+
+
+ // Save complex mappings (custom regexp)
+ if (token[2]) {
+ this.tokenReg.push(token[2]);
+ this.tokenComplex.push({
+ abbr: token[1],
+ re: new RegExp(token[2]),
+ rescan: token[3]
+ });
+ }
+ // Save simple mappings (string only, no regexp)
+ else {
+ this.tokenReg.push(token[1]);
+ this.tokenSimple[token[1]] = token[0];
+ }
+ this.tokenMap[token[0]] = token[1];
+ }
+
+ // Cache the token registry.
+ this.tokenReg = new RegExp("(" + this.tokenReg.slice(0).concat(["<", ">", '"', "'"]).join("|") + ")", "g");
+ };
+
+ Scanner.prototype = {
+
+ helpers: [
+
+ {
+ name: /\s*\(([\$\w]+)\)\s*->([^\n]*)/,
+ fn: function(content) {
+ var quickFunc = /\s*\(([\$\w]+)\)\s*->([^\n]*)/,
+ parts = content.match(quickFunc);
+
+ return "can.proxy(function(__){var " + parts[1] + "=can.$(__);" + parts[2] + "}, this);";
+ }
+ }
+ ],
+
+ scan: function(source, name) {
+ var tokens = [],
+ last = 0,
+ simple = this.tokenSimple,
+ complex = this.tokenComplex;
+
+ source = source.replace(newLine, "\n");
+ if (this.transform) {
+ source = this.transform(source);
+ }
+ source.replace(this.tokenReg, function(whole, part) {
+ // offset is the second to last argument
+ var offset = arguments[arguments.length - 2];
+
+ // if the next token starts after the last token ends
+ // push what's in between
+ if (offset > last) {
+ tokens.push(source.substring(last, offset));
+ }
+
+ // push the simple token (if there is one)
+ if (simple[whole]) {
+ tokens.push(whole);
+ }
+ // otherwise lookup complex tokens
+ else {
+ for (var i = 0, token; token = complex[i]; i++) {
+ if (token.re.test(whole)) {
+ tokens.push(token.abbr);
+ // Push a rescan function if one exists
+ if (token.rescan) {
+ tokens.push(token.rescan(part));
+ }
+ break;
+ }
+ }
+ }
+
+ // update the position of the last part of the last token
+ last = offset + part.length;
+ });
+
+ // if there's something at the end, add it
+ if (last < source.length) {
+ tokens.push(source.substr(last));
+ }
+
+ var content = '',
+ buff = [startTxt + (this.text.start || '')],
+ // Helper `function` for putting stuff in the view concat.
+ put = function(content, bonus) {
+ buff.push(put_cmd, '"', clean(content), '"' + (bonus || '') + ');');
+ },
+ // A stack used to keep track of how we should end a bracket
+ // `}`.
+ // Once we have a `<%= %>` with a `leftBracket`,
+ // we store how the file should end here (either `))` or `;`).
+ endStack = [],
+ // The last token, used to remember which tag we are in.
+ lastToken,
+ // The corresponding magic tag.
+ startTag = null,
+ // Was there a magic tag inside an html tag?
+ magicInTag = false,
+ // The current tag name.
+ tagName = '',
+ // stack of tagNames
+ tagNames = [],
+ // Pop from tagNames?
+ popTagName = false,
+ // Declared here.
+ bracketCount,
+ i = 0,
+ token,
+ tmap = this.tokenMap;
+
+ // Reinitialize the tag state goodness.
+ htmlTag = quote = beforeQuote = null;
+
+ for (;
+ (token = tokens[i++]) !== undefined;) {
+ if (startTag === null) {
+ switch (token) {
+ case tmap.left:
+ case tmap.escapeLeft:
+ case tmap.returnLeft:
+ magicInTag = htmlTag && 1;
+ case tmap.commentLeft:
+ // A new line -- just add whatever content within a clean.
+ // Reset everything.
+ startTag = token;
+ if (content.length) {
+ put(content);
+ }
+ content = '';
+ break;
+ case tmap.escapeFull:
+ // This is a full line escape (a line that contains only whitespace and escaped logic)
+ // Break it up into escape left and right
+ magicInTag = htmlTag && 1;
+ rescan = 1;
+ startTag = tmap.escapeLeft;
+ if (content.length) {
+ put(content);
+ }
+ rescan = tokens[i++];
+ content = rescan.content || rescan;
+ if (rescan.before) {
+ put(rescan.before);
+ }
+ tokens.splice(i, 0, tmap.right);
+ break;
+ case tmap.commentFull:
+ // Ignore full line comments.
+ break;
+ case tmap.templateLeft:
+ content += tmap.left;
+ break;
+ case '<':
+ // Make sure we are not in a comment.
+ if (tokens[i].indexOf("!--") !== 0) {
+ htmlTag = 1;
+ magicInTag = 0;
+ }
+ content += token;
+ break;
+ case '>':
+ htmlTag = 0;
+ // content.substr(-1) doesn't work in IE7/8
+ var emptyElement = content.substr(content.length - 1) == "/" || content.substr(content.length - 2) == "--";
+ // if there was a magic tag
+ // or it's an element that has text content between its tags,
+ // but content is not other tags add a hookup
+ // TODO: we should only add `can.EJS.pending()` if there's a magic tag
+ // within the html tags.
+ if (magicInTag || !popTagName && elements.tagToContentPropMap[tagNames[tagNames.length - 1]]) {
+ // make sure / of /> is on the left of pending
+ if (emptyElement) {
+ put(content.substr(0, content.length - 1), ",can.view.pending(),\"/>\"");
+ } else {
+ put(content, ",can.view.pending(),\">\"");
+ }
+ content = '';
+ magicInTag = 0;
+ } else {
+ content += token;
+ }
+ // if it's a tag like <input/>
+ if (emptyElement || popTagName) {
+ // remove the current tag in the stack
+ tagNames.pop();
+ // set the current tag to the previous parent
+ tagName = tagNames[tagNames.length - 1];
+ // Don't pop next time
+ popTagName = false;
+ }
+ break;
+ case "'":
+ case '"':
+ // If we are in an html tag, finding matching quotes.
+ if (htmlTag) {
+ // We have a quote and it matches.
+ if (quote && quote === token) {
+ // We are exiting the quote.
+ quote = null;
+ // Otherwise we are creating a quote.
+ // TODO: does this handle `\`?
+ } else if (quote === null) {
+ quote = token;
+ beforeQuote = lastToken;
+ }
+ }
+ default:
+ // Track the current tag
+ if (lastToken === '<') {
+ tagName = token.split(/\s/)[0];
+ if (tagName.indexOf("/") === 0 && tagNames[tagNames.length - 1] === tagName.substr(1)) {
+ // set tagName to the last tagName
+ // if there are no more tagNames, we'll rely on getTag.
+ tagName = tagNames[tagNames.length - 1];
+ popTagName = true;
+ } else {
+ tagNames.push(tagName);
+ }
+ }
+ content += token;
+ break;
+ }
+ } else {
+ // We have a start tag.
+ switch (token) {
+ case tmap.right:
+ case tmap.returnRight:
+ switch (startTag) {
+ case tmap.left:
+ // Get the number of `{ minus }`
+ bracketCount = bracketNum(content);
+
+ // We are ending a block.
+ if (bracketCount == 1) {
+
+ // We are starting on.
+ buff.push(insert_cmd, "can.view.txt(0,'" + getTag(tagName, tokens, i) + "'," + status() + ",this,function(){", startTxt, content);
+
+ endStack.push({
+ before: "",
+ after: finishTxt + "}));\n"
+ });
+ } else {
+
+ // How are we ending this statement?
+ last = // If the stack has value and we are ending a block...
+ endStack.length && bracketCount == -1 ? // Use the last item in the block stack.
+ endStack.pop() : // Or use the default ending.
+ {
+ after: ";"
+ };
+
+ // If we are ending a returning block,
+ // add the finish text which returns the result of the
+ // block.
+ if (last.before) {
+ buff.push(last.before);
+ }
+ // Add the remaining content.
+ buff.push(content, ";", last.after);
+ }
+ break;
+ case tmap.escapeLeft:
+ case tmap.returnLeft:
+ // We have an extra `{` -> `block`.
+ // Get the number of `{ minus }`.
+ bracketCount = bracketNum(content);
+ // If we have more `{`, it means there is a block.
+ if (bracketCount) {
+ // When we return to the same # of `{` vs `}` end with a `doubleParent`.
+ endStack.push({
+ before: finishTxt,
+ after: "}));"
+ });
+ }
+
+ var escaped = startTag === tmap.escapeLeft ? 1 : 0,
+ commands = {
+ insert: insert_cmd,
+ tagName: getTag(tagName, tokens, i),
+ status: status()
+ };
+
+ for (var ii = 0; ii < this.helpers.length; ii++) {
+ // Match the helper based on helper
+ // regex name value
+ var helper = this.helpers[ii];
+ if (helper.name.test(content)) {
+ content = helper.fn(content, commands);
+
+ // dont escape partials
+ if (helper.name.source == /^>[\s]*\w*/.source) {
+ escaped = 0;
+ }
+ break;
+ }
+ }
+
+ // Handle special cases
+ if (typeof content == 'object') {
+ if (content.raw) {
+ buff.push(content.raw);
+ }
+ } else {
+ // If we have `<%== a(function(){ %>` then we want
+ // `can.EJS.text(0,this, function(){ return a(function(){ var _v1ew = [];`.
+ buff.push(insert_cmd, "can.view.txt(" + escaped + ",'" + tagName + "'," + status() + ",this,function(){ " + (this.text.escape || '') + "return ", content,
+ // If we have a block.
+ bracketCount ?
+ // Start with startTxt `"var _v1ew = [];"`.
+ startTxt :
+ // If not, add `doubleParent` to close push and text.
+ "}));");
+ }
+
+ if (rescan && rescan.after && rescan.after.length) {
+ put(rescan.after.length);
+ rescan = null;
+ }
+ break;
+ }
+ startTag = null;
+ content = '';
+ break;
+ case tmap.templateLeft:
+ content += tmap.left;
+ break;
+ default:
+ content += token;
+ break;
+ }
+ }
+ lastToken = token;
+ }
+
+ // Put it together...
+ if (content.length) {
+ // Should be `content.dump` in Ruby.
+ put(content);
+ }
+ buff.push(";");
+
+ var template = buff.join(''),
+ out = {
+ out: 'with(_VIEW) { with (_CONTEXT) {' + template + " " + finishTxt + "}}"
+ };
+ // Use `eval` instead of creating a function, because it is easier to debug.
+ myEval.call(out, 'this.fn = (function(_CONTEXT,_VIEW){' + out.out + '});\r\n//@ sourceURL=' + name + ".js");
+
+ return out;
+ }
+ };
+
+ return Scanner;
+ })(__m11, __m14);
+
+ // ## view/node_lists.js
+ var __m17 = (function(can) {
+
+ // text node expando test
+ var canExpando = true;
+ try {
+ document.createTextNode('')._ = 0;
+ } catch (ex) {
+ canExpando = false;
+ }
+
+ // a mapping of element ids to nodeList ids
+ var nodeMap = {},
+ // a mapping of ids to text nodes
+ textNodeMap = {},
+ // a mapping of nodeList ids to nodeList
+ nodeListMap = {},
+ expando = "ejs_" + Math.random(),
+ _id = 0,
+ id = function(node) {
+ if (canExpando || node.nodeType !== 3) {
+ if (node[expando]) {
+ return node[expando];
+ } else {
+ return node[expando] = (node.nodeName ? "element_" : "obj_") + (++_id);
+ }
+ } else {
+ for (var textNodeID in textNodeMap) {
+ if (textNodeMap[textNodeID] === node) {
+ return textNodeID;
+ }
+ }
+
+ textNodeMap["text_" + (++_id)] = node;
+ return "text_" + _id;
+ }
+ },
+ // removes a nodeListId from a node's nodeListIds
+ removeNodeListId = function(node, nodeListId) {
+ var nodeListIds = nodeMap[id(node)];
+ if (nodeListIds) {
+ var index = can.inArray(nodeListId, nodeListIds);
+
+ if (index >= 0) {
+ nodeListIds.splice(index, 1);
+ }
+ if (!nodeListIds.length) {
+ delete nodeMap[id(node)];
+ }
+ }
+ },
+ addNodeListId = function(node, nodeListId) {
+ var nodeListIds = nodeMap[id(node)];
+ if (!nodeListIds) {
+ nodeListIds = nodeMap[id(node)] = [];
+ }
+ nodeListIds.push(nodeListId);
+ };
+
+ var nodeLists = {
+ id: id,
+ // replaces the contents of one node list with the nodes in another list
+ replace: function(oldNodeList, newNodes) {
+ // for each node in the node list
+ oldNodeList = can.makeArray(oldNodeList);
+
+ // try every set
+ //can.each( oldNodeList, function(node){
+ var node = oldNodeList[0]
+ // for each nodeList the node is in
+ can.each(can.makeArray(nodeMap[id(node)]), function(nodeListId) {
+
+ // if startNode to endNode is
+ // within list, replace that list
+ // I think the problem is not the WHOLE part is being
+ // matched
+ var nodeList = nodeListMap[nodeListId],
+ startIndex = can.inArray(node, nodeList),
+ endIndex = can.inArray(oldNodeList[oldNodeList.length - 1], nodeList);
+
+
+ // remove this nodeListId from each node
+ if (startIndex >= 0 && endIndex >= 0) {
+ for (var i = startIndex; i <= endIndex; i++) {
+ var n = nodeList[i];
+ removeNodeListId(n, nodeListId);
+ }
+ // swap in new nodes into the nodeLIst
+ nodeList.splice.apply(nodeList, [startIndex, endIndex - startIndex + 1].concat(newNodes));
+
+ // tell these new nodes they belong to the nodeList
+ can.each(newNodes, function(node) {
+ addNodeListId(node, nodeListId);
+ });
+ } else {
+ nodeLists.unregister(nodeList);
+ }
+ });
+ //});
+ },
+ // registers a list of nodes
+ register: function(nodeList) {
+ var nLId = id(nodeList);
+ nodeListMap[nLId] = nodeList;
+
+ can.each(nodeList, function(node) {
+ addNodeListId(node, nLId);
+ });
+
+ },
+ // removes mappings
+ unregister: function(nodeList) {
+ var nLId = id(nodeList);
+ can.each(nodeList, function(node) {
+ removeNodeListId(node, nLId);
+ });
+ delete nodeListMap[nLId];
+ },
+ nodeMap: nodeMap,
+ nodeListMap: nodeListMap
+ }
+ var ids = function(nodeList) {
+ return nodeList.map(function(n) {
+ return id(n) + ":" + (n.innerHTML || n.nodeValue)
+ })
+ }
+ return nodeLists;
+
+ })(__m3);
+
+ // ## view/live.js
+ var __m16 = (function(can, elements, view, nodeLists) {
+ // ## live.js
+ // The live module provides live binding for computes
+ // and can.Observe.List.
+ // Currently, it's API is designed for `can/view/render`, but
+ // it could easily be used for other purposes.
+
+ // ### Helper methods
+ // #### setup
+ // `setup(HTMLElement, bind(data), unbind(data)) -> data`
+ // Calls bind right away, but will call unbind
+ // if the element is "destroyed" (removed from the DOM).
+ var setup = function(el, bind, unbind) {
+ var teardown = function() {
+ unbind(data)
+ can.unbind.call(el, 'destroyed', teardown);
+ },
+ data = {
+ teardownCheck: function(parent) {
+ if (!parent) {
+ teardown();
+ }
+ }
+ }
+
+ can.bind.call(el, 'destroyed', teardown);
+ bind(data)
+ return data;
+ },
+ // #### listen
+ // Calls setup, but presets bind and unbind to
+ // operate on a compute
+ listen = function(el, compute, change) {
+ return setup(el, function() {
+ compute.bind("change", change);
+ }, function(data) {
+ compute.unbind("change", change);
+ if (data.nodeList) {
+ nodeLists.unregister(data.nodeList);
+ }
+ });
+ },
+ // #### getAttributeParts
+ // Breaks up a string like foo='bar' into ["foo","'bar'""]
+ getAttributeParts = function(newVal) {
+ return (newVal || "").replace(/['"]/g, '').split('=')
+ }
+ // #### insertElementsAfter
+ // Appends elements after the last item in oldElements.
+ insertElementsAfter = function(oldElements, newFrag) {
+ var last = oldElements[oldElements.length - 1];
+
+ // Insert it in the `document` or `documentFragment`
+ if (last.nextSibling) {
+ last.parentNode.insertBefore(newFrag, last.nextSibling);
+ } else {
+ last.parentNode.appendChild(newFrag);
+ }
+ };
+
+ var live = {
+ nodeLists: nodeLists,
+ list: function(el, list, func, context, parentNode) {
+ // A mapping of the index to an array
+ // of elements that represent the item.
+ // Each array is registered so child or parent
+ // live structures can update the elements
+ var nodesMap = [],
+
+ add = function(ev, items, index) {
+
+ // Collect new html and mappings
+ var frag = document.createDocumentFragment(),
+ newMappings = [];
+ can.each(items, function(item) {
+ var itemHTML = func.call(context, item),
+ itemFrag = can.view.frag(itemHTML, parentNode);
+
+ newMappings.push(can.makeArray(itemFrag.childNodes));
+ frag.appendChild(itemFrag);
+ })
+
+ // Inserting at the end of the list
+ if (!nodesMap[index]) {
+ insertElementsAfter(
+ index == 0 ? [text] :
+ nodesMap[index - 1], frag)
+ } else {
+ var el = nodesMap[index][0];
+ el.parentNode.insertBefore(frag, el)
+ }
+ // register each item
+ can.each(newMappings, function(nodeList) {
+ nodeLists.register(nodeList)
+ });
+ [].splice.apply(nodesMap, [index, 0].concat(newMappings));
+ },
+ remove = function(ev, items, index) {
+ var removedMappings = nodesMap.splice(index, items.length),
+ itemsToRemove = [];
+
+ can.each(removedMappings, function(nodeList) {
+ // add items that we will remove all at once
+ [].push.apply(itemsToRemove, nodeList)
+ // Update any parent lists to remove these items
+ nodeLists.replace(nodeList, []);
+ // unregister the list
+ nodeLists.unregister(nodeList);
+
+ });
+ can.remove(can.$(itemsToRemove));
+ },
+ parentNode = elements.getParentNode(el, parentNode),
+ text = document.createTextNode("");
+
+ // Setup binding and teardown to add and remove events
+ setup(parentNode, function() {
+ list.bind("add", add).bind("remove", remove)
+ }, function() {
+ list.unbind("add", add).unbind("remove", remove);
+ can.each(nodesMap, function(nodeList) {
+ nodeLists.unregister(nodeList);
+ })
+ })
+
+ insertElementsAfter([el], text);
+ can.remove(can.$(el));
+ add({}, list, 0);
+
+ },
+ html: function(el, compute, parentNode) {
+ var parentNode = elements.getParentNode(el, parentNode),
+
+ data = listen(parentNode, compute, function(ev, newVal, oldVal) {
+ var attached = nodes[0].parentNode;
+ // update the nodes in the DOM with the new rendered value
+ if (attached) {
+ makeAndPut(newVal);
+ }
+ data.teardownCheck(nodes[0].parentNode);
+ });
+
+ var nodes,
+ makeAndPut = function(val) {
+ // create the fragment, but don't hook it up
+ // we need to insert it into the document first
+ var frag = can.view.frag(val, parentNode),
+ // keep a reference to each node
+ newNodes = can.makeArray(frag.childNodes);
+ // Insert it in the `document` or `documentFragment`
+ insertElementsAfter(nodes || [el], frag)
+ // nodes hasn't been set yet
+ if (!nodes) {
+ can.remove(can.$(el));
+ nodes = newNodes;
+ // set the teardown nodeList
+ data.nodeList = nodes;
+ nodeLists.register(nodes);
+ } else {
+ // Update node Array's to point to new nodes
+ // and then remove the old nodes.
+ // It has to be in this order for Mootools
+ // and IE because somehow, after an element
+ // is removed from the DOM, it loses its
+ // expando values.
+ var nodesToRemove = can.makeArray(nodes);
+ nodeLists.replace(nodes, newNodes);
+ can.remove(can.$(nodesToRemove));
+ }
+ };
+ makeAndPut(compute(), [el]);
+
+ },
+ text: function(el, compute, parentNode) {
+ var parent = elements.getParentNode(el, parentNode);
+
+ // setup listening right away so we don't have to re-calculate value
+ var data = listen(el.parentNode !== parent ? el.parentNode : parent, compute, function(ev, newVal, oldVal) {
+ // Sometimes this is 'unknown' in IE and will throw an exception if it is
+ if (typeof node.nodeValue != 'unknown') {
+ node.nodeValue = "" + newVal;
+ }
+ data.teardownCheck(node.parentNode);
+ });
+
+ var node = document.createTextNode(compute());
+
+ if (el.parentNode !== parent) {
+ parent = el.parentNode;
+ parent.insertBefore(node, el);
+ parent.removeChild(el);
+ } else {
+ parent.insertBefore(node, el);
+ parent.removeChild(el);
+ }
+ },
+ attributes: function(el, compute, currentValue) {
+ var setAttrs = function(newVal) {
+ var parts = getAttributeParts(newVal),
+ newAttrName = parts.shift();
+
+ // Remove if we have a change and used to have an `attrName`.
+ if ((newAttrName != attrName) && attrName) {
+ elements.removeAttr(el, attrName);
+ }
+ // Set if we have a new `attrName`.
+ if (newAttrName) {
+ elements.setAttr(el, newAttrName, parts.join('='));
+ attrName = newAttrName;
+ }
+ }
+
+ listen(el, compute, function(ev, newVal) {
+ setAttrs(newVal)
+ })
+ // current value has been set
+ if (arguments.length >= 3) {
+ var attrName = getAttributeParts(currentValue)[0]
+ } else {
+ setAttrs(compute())
+ }
+ },
+ attributePlaceholder: '__!!__',
+ attributeReplace: /__!!__/g,
+ attribute: function(el, attributeName, compute) {
+ listen(el, compute, function(ev, newVal) {
+ elements.setAttr(el, attributeName, hook.render());
+ })
+
+ var wrapped = can.$(el),
+ hooks;
+
+ // Get the list of hookups or create one for this element.
+ // Hooks is a map of attribute names to hookup `data`s.
+ // Each hookup data has:
+ // `render` - A `function` to render the value of the attribute.
+ // `funcs` - A list of hookup `function`s on that attribute.
+ // `batchNum` - The last event `batchNum`, used for performance.
+ hooks = can.data(wrapped, 'hooks');
+ if (!hooks) {
+ can.data(wrapped, 'hooks', hooks = {});
+ }
+
+ // Get the attribute value.
+ var attr = elements.getAttr(el, attributeName),
+ // Split the attribute value by the template.
+ // Only split out the first __!!__ so if we have multiple hookups in the same attribute,
+ // they will be put in the right spot on first render
+ parts = attr.split(live.attributePlaceholder),
+ goodParts = [],
+ hook;
+ goodParts.push(parts.shift(),
+ parts.join(live.attributePlaceholder));
+
+ // If we already had a hookup for this attribute...
+ if (hooks[attributeName]) {
+ // Just add to that attribute's list of `function`s.
+ hooks[attributeName].computes.push(compute);
+ } else {
+ // Create the hookup data.
+ hooks[attributeName] = {
+ render: function() {
+ var i = 0,
+ // attr doesn't have a value in IE
+ newAttr = attr ? attr.replace(live.attributeReplace, function() {
+ return elements.contentText(hook.computes[i++]());
+ }) : elements.contentText(hook.computes[i++]());
+ return newAttr;
+ },
+ computes: [compute],
+ batchNum: undefined
+ };
+ }
+
+ // Save the hook for slightly faster performance.
+ hook = hooks[attributeName];
+
+ // Insert the value in parts.
+ goodParts.splice(1, 0, compute());
+
+ // Set the attribute.
+ elements.setAttr(el, attributeName, goodParts.join(""));
+
+ }
+ }
+ return live;
+
+ })(__m3, __m14, __m11, __m17);
+
+ // ## view/render.js
+ var __m15 = (function(can, elements, live) {
+
+ var pendingHookups = [],
+ tagChildren = function(tagName) {
+ var newTag = elements.tagMap[tagName] || "span";
+ if (newTag === "span") {
+ //innerHTML in IE doesn't honor leading whitespace after empty elements
+ return "@@!!@@";
+ }
+ return "<" + newTag + ">" + tagChildren(newTag) + "</" + newTag + ">";
+ },
+ contentText = function(input, tag) {
+
+ // If it's a string, return.
+ if (typeof input == 'string') {
+ return input;
+ }
+ // If has no value, return an empty string.
+ if (!input && input !== 0) {
+ return '';
+ }
+
+ // If it's an object, and it has a hookup method.
+ var hook = (input.hookup &&
+
+ // Make a function call the hookup method.
+
+ function(el, id) {
+ input.hookup.call(input, el, id);
+ }) ||
+
+ // Or if it's a `function`, just use the input.
+ (typeof input == 'function' && input);
+
+ // Finally, if there is a `function` to hookup on some dom,
+ // add it to pending hookups.
+ if (hook) {
+ if (tag) {
+ return "<" + tag + " " + can.view.hook(hook) + "></" + tag + ">"
+ } else {
+ pendingHookups.push(hook);
+ }
+
+ return '';
+ }
+
+ // Finally, if all else is `false`, `toString()` it.
+ return "" + input;
+ },
+ // Returns escaped/sanatized content for anything other than a live-binding
+ contentEscape = function(txt) {
+ return (typeof txt == 'string' || typeof txt == 'number') ?
+ can.esc(txt) :
+ contentText(txt);
+ };
+
+ var current;
+
+ can.extend(can.view, {
+ live: live,
+ setupLists: function() {
+
+ var old = can.view.lists,
+ data;
+
+ can.view.lists = function(list, renderer) {
+ data = {
+ list: list,
+ renderer: renderer
+ }
+ }
+ return function() {
+ can.view.lists = old;
+ return data;
+ }
+ },
+ pending: function() {
+ // TODO, make this only run for the right tagName
+ var hooks = pendingHookups.slice(0);
+ lastHookups = hooks;
+ pendingHookups = [];
+ return can.view.hook(function(el) {
+ can.each(hooks, function(fn) {
+ fn(el);
+ });
+ });
+ },
+
+
+ txt: function(escape, tagName, status, self, func) {
+ var listTeardown = can.view.setupLists(),
+ emptyHandler = function() {},
+ unbind = function() {
+ compute.unbind("change", emptyHandler)
+ };
+
+ var compute = can.compute(func, self, false);
+ // bind to get and temporarily cache the value
+ compute.bind("change", emptyHandler);
+ // call the "wrapping" function and get the binding information
+ var tag = (elements.tagMap[tagName] || "span"),
+ listData = listTeardown(),
+ value = compute();
+
+
+ if (listData) {
+ return "<" + tag + can.view.hook(function(el, parentNode) {
+ live.list(el, listData.list, listData.renderer, self, parentNode);
+ }) + "></" + tag + ">";
+ }
+
+ // If we had no observes just return the value returned by func.
+ if (!compute.hasDependencies) {
+ unbind();
+ return (escape || status !== 0 ? contentEscape : contentText)(value, status === 0 && tag);
+ }
+
+ // the property (instead of innerHTML elements) to adjust. For
+ // example options should use textContent
+ var contentProp = elements.tagToContentPropMap[tagName];
+
+
+ // The magic tag is outside or between tags.
+ if (status === 0 && !contentProp) {
+ // Return an element tag with a hookup in place of the content
+ return "<" + tag + can.view.hook(
+ escape ?
+ // If we are escaping, replace the parentNode with
+ // a text node who's value is `func`'s return value.
+
+ function(el, parentNode) {
+ live.text(el, compute, parentNode);
+ unbind();
+ } :
+ // If we are not escaping, replace the parentNode with a
+ // documentFragment created as with `func`'s return value.
+
+ function(el, parentNode) {
+ live.html(el, compute, parentNode);
+ unbind();
+ //children have to be properly nested HTML for buildFragment to work properly
+ }) + ">" + tagChildren(tag) + "</" + tag + ">";
+ // In a tag, but not in an attribute
+ } else if (status === 1) {
+ // remember the old attr name
+ pendingHookups.push(function(el) {
+ live.attributes(el, compute, compute());
+ unbind();
+ });
+ return compute();
+ } else { // In an attribute...
+ var attributeName = status === 0 ? contentProp : status;
+ // if the magic tag is inside the element, like `<option><% TAG %></option>`,
+ // we add this hookup to the last element (ex: `option`'s) hookups.
+ // Otherwise, the magic tag is in an attribute, just add to the current element's
+ // hookups.
+ (status === 0 ? lastHookups : pendingHookups).push(function(el) {
+ live.attribute(el, attributeName, compute);
+ unbind();
+ });
+ return live.attributePlaceholder;
+ }
+ }
+ });
+
+ return can;
+ })(__m11, __m14, __m16, __m2);
+
+ // ## view/ejs/ejs.js
+ var __m12 = (function(can) {
+ // ## ejs.js
+ // `can.EJS`
+ // _Embedded JavaScript Templates._
+
+ // Helper methods.
+ var extend = can.extend,
+ EJS = function(options) {
+ // Supports calling EJS without the constructor
+ // This returns a function that renders the template.
+ if (this.constructor != EJS) {
+ var ejs = new EJS(options);
+ return function(data, helpers) {
+ return ejs.render(data, helpers);
+ };
+ }
+ // If we get a `function` directly, it probably is coming from
+ // a `steal`-packaged view.
+ if (typeof options == "function") {
+ this.template = {
+ fn: options
+ };
+ return;
+ }
+ // Set options on self.
+ extend(this, options);
+ this.template = this.scanner.scan(this.text, this.name);
+ };
+
+ can.EJS = EJS;
+
+
+ EJS.prototype.
+
+ render = function(object, extraHelpers) {
+ object = object || {};
+ return this.template.fn.call(object, object, new EJS.Helpers(object, extraHelpers || {}));
+ };
+
+ extend(EJS.prototype, {
+
+ scanner: new can.view.Scanner({
+
+ tokens: [
+ ["templateLeft", "<%%"], // Template
+ ["templateRight", "%>"], // Right Template
+ ["returnLeft", "<%=="], // Return Unescaped
+ ["escapeLeft", "<%="], // Return Escaped
+ ["commentLeft", "<%#"], // Comment
+ ["left", "<%"], // Run --- this is hack for now
+ ["right", "%>"], // Right -> All have same FOR Mustache ...
+ ["returnRight", "%>"]
+ ],
+
+
+ transform: function(source) {
+ return source.replace(/<%([\s\S]+?)%>/gm, function(whole, part) {
+ var brackets = [],
+ foundBracketPair,
+ i;
+
+ // Look for brackets (for removing self-contained blocks)
+ part.replace(/[{}]/gm, function(bracket, offset) {
+ brackets.push([bracket, offset]);
+ });
+
+ // Remove bracket pairs from the list of replacements
+ do {
+ foundBracketPair = false;
+ for (i = brackets.length - 2; i >= 0; i--) {
+ if (brackets[i][0] == '{' && brackets[i + 1][0] == '}') {
+ brackets.splice(i, 2);
+ foundBracketPair = true;
+ break;
+ }
+ }
+ } while (foundBracketPair);
+
+ // Unmatched brackets found, inject EJS tags
+ if (brackets.length >= 2) {
+ var result = ['<%'],
+ bracket,
+ last = 0;
+ for (i = 0; bracket = brackets[i]; i++) {
+ result.push(part.substring(last, last = bracket[1]));
+ if ((bracket[0] == '{' && i < brackets.length - 1) || (bracket[0] == '}' && i > 0)) {
+ result.push(bracket[0] == '{' ? '{ %><% ' : ' %><% }');
+ } else {
+ result.push(bracket[0]);
+ }
+ ++last;
+ }
+ result.push(part.substring(last), '%>');
+ return result.join('');
+ }
+ // Otherwise return the original
+ else {
+ return '<%' + part + '%>';
+ }
+ });
+ }
+ })
+ });
+
+ EJS.Helpers = function(data, extras) {
+ this._data = data;
+ this._extras = extras;
+ extend(this, extras);
+ };
+
+
+ EJS.Helpers.prototype = {
+ // TODO Deprecated!!
+ list: function(list, cb) {
+
+ can.each(list, function(item, i) {
+ cb(item, i, list)
+ })
+ },
+ each: function(list, cb) {
+ // Normal arrays don't get live updated
+ if (can.isArray(list)) {
+ this.list(list, cb);
+ } else {
+ can.view.lists(list, cb);
+ }
+ }
+ };
+
+ // Options for `steal`'s build.
+ can.view.register({
+ suffix: "ejs",
+ // returns a `function` that renders the view.
+ script: function(id, src) {
+ return "can.EJS(function(_CONTEXT,_VIEW) { " + new EJS({
+ text: src,
+ name: id
+ }).template.out + " })";
+ },
+ renderer: function(id, text) {
+ return EJS({
+ text: text,
+ name: id
+ });
+ }
+ });
+
+ return can;
+ })(__m3, __m11, __m2, __m9, __m13, __m15);
+
+ // ## control/control.js
+ var __m18 = (function(can) {
+ // ## control.js
+ // `can.Control`
+ // _Controller_
+
+ // Binds an element, returns a function that unbinds.
+ var bind = function(el, ev, callback) {
+
+ can.bind.call(el, ev, callback);
+
+ return function() {
+ can.unbind.call(el, ev, callback);
+ };
+ },
+ isFunction = can.isFunction,
+ extend = can.extend,
+ each = can.each,
+ slice = [].slice,
+ paramReplacer = /\{([^\}]+)\}/g,
+ special = can.getObject("$.event.special", [can]) || {},
+
+ // Binds an element, returns a function that unbinds.
+ delegate = function(el, selector, ev, callback) {
+ can.delegate.call(el, selector, ev, callback);
+ return function() {
+ can.undelegate.call(el, selector, ev, callback);
+ };
+ },
+
+ // Calls bind or unbind depending if there is a selector.
+ binder = function(el, ev, callback, selector) {
+ return selector ?
+ delegate(el, can.trim(selector), ev, callback) :
+ bind(el, ev, callback);
+ },
+
+ basicProcessor;
+
+ var Control = can.Control = can.Construct(
+
+ {
+ // Setup pre-processes which methods are event listeners.
+
+ setup: function() {
+
+ // Allow contollers to inherit "defaults" from super-classes as it
+ // done in `can.Construct`
+ can.Construct.setup.apply(this, arguments);
+
+ // If you didn't provide a name, or are `control`, don't do anything.
+ if (can.Control) {
+
+ // Cache the underscored names.
+ var control = this,
+ funcName;
+
+ // Calculate and cache actions.
+ control.actions = {};
+ for (funcName in control.prototype) {
+ if (control._isAction(funcName)) {
+ control.actions[funcName] = control._action(funcName);
+ }
+ }
+ }
+ },
+
+ // Moves `this` to the first argument, wraps it with `jQuery` if it's an element
+ _shifter: function(context, name) {
+
+ var method = typeof name == "string" ? context[name] : name;
+
+ if (!isFunction(method)) {
+ method = context[method];
+ }
+
+ return function() {
+ context.called = name;
+ return method.apply(context, [this.nodeName ? can.$(this) : this].concat(slice.call(arguments, 0)));
+ };
+ },
+
+ // Return `true` if is an action.
+
+ _isAction: function(methodName) {
+
+ var val = this.prototype[methodName],
+ type = typeof val;
+ // if not the constructor
+ return (methodName !== 'constructor') &&
+ // and is a function or links to a function
+ (type == "function" || (type == "string" && isFunction(this.prototype[val]))) &&
+ // and is in special, a processor, or has a funny character
+ !! (special[methodName] || processors[methodName] || /[^\w]/.test(methodName));
+ },
+ // Takes a method name and the options passed to a control
+ // and tries to return the data necessary to pass to a processor
+ // (something that binds things).
+
+ _action: function(methodName, options) {
+
+ // If we don't have options (a `control` instance), we'll run this
+ // later.
+ paramReplacer.lastIndex = 0;
+ if (options || !paramReplacer.test(methodName)) {
+ // If we have options, run sub to replace templates `{}` with a
+ // value from the options or the window
+ var convertedName = options ? can.sub(methodName, [options, window]) : methodName;
+ if (!convertedName) {
+ return null;
+ }
+ // If a `{}` template resolves to an object, `convertedName` will be
+ // an array
+ var arr = can.isArray(convertedName),
+
+ // Get the name
+ name = arr ? convertedName[1] : convertedName,
+
+ // Grab the event off the end
+ parts = name.split(/\s+/g),
+ event = parts.pop();
+
+ return {
+ processor: processors[event] || basicProcessor,
+ parts: [name, parts.join(" "), event],
+ delegate: arr ? convertedName[0] : undefined
+ };
+ }
+ },
+ // An object of `{eventName : function}` pairs that Control uses to
+ // hook up events auto-magically.
+
+ processors: {},
+ // A object of name-value pairs that act as default values for a
+ // control instance
+ defaults: {}
+
+ }, {
+
+ // Sets `this.element`, saves the control in `data, binds event
+ // handlers.
+
+ setup: function(element, options) {
+
+ var cls = this.constructor,
+ pluginname = cls.pluginName || cls._fullName,
+ arr;
+
+ // Want the raw element here.
+ this.element = can.$(element)
+
+ if (pluginname && pluginname !== 'can_control') {
+ // Set element and `className` on element.
+ this.element.addClass(pluginname);
+ }
+
+ (arr = can.data(this.element, "controls")) || can.data(this.element, "controls", arr = []);
+ arr.push(this);
+
+ // Option merging.
+
+ this.options = extend({}, cls.defaults, options);
+
+ // Bind all event handlers.
+ this.on();
+
+ // Gets passed into `init`.
+
+ return [this.element, this.options];
+ },
+
+ on: function(el, selector, eventName, func) {
+ if (!el) {
+
+ // Adds bindings.
+ this.off();
+
+ // Go through the cached list of actions and use the processor
+ // to bind
+ var cls = this.constructor,
+ bindings = this._bindings,
+ actions = cls.actions,
+ element = this.element,
+ destroyCB = can.Control._shifter(this, "destroy"),
+ funcName, ready;
+
+ for (funcName in actions) {
+ // Only push if we have the action and no option is `undefined`
+ if (actions.hasOwnProperty(funcName) &&
+ (ready = actions[funcName] || cls._action(funcName, this.options))) {
+ bindings.push(ready.processor(ready.delegate || element,
+ ready.parts[2], ready.parts[1], funcName, this));
+ }
+ }
+
+
+ // Setup to be destroyed...
+ // don't bind because we don't want to remove it.
+ can.bind.call(element, "destroyed", destroyCB);
+ bindings.push(function(el) {
+ can.unbind.call(el, "destroyed", destroyCB);
+ });
+ return bindings.length;
+ }
+
+ if (typeof el == 'string') {
+ func = eventName;
+ eventName = selector;
+ selector = el;
+ el = this.element;
+ }
+
+ if (func === undefined) {
+ func = eventName;
+ eventName = selector;
+ selector = null;
+ }
+
+ if (typeof func == 'string') {
+ func = can.Control._shifter(this, func);
+ }
+
+ this._bindings.push(binder(el, eventName, func, selector));
+
+ return this._bindings.length;
+ },
+ // Unbinds all event handlers on the controller.
+
+ off: function() {
+ var el = this.element[0]
+ each(this._bindings || [], function(value) {
+ value(el);
+ });
+ // Adds bindings.
+ this._bindings = [];
+ },
+ // Prepares a `control` for garbage collection
+
+ destroy: function() {
+ //Control already destroyed
+ if (this.element === null) {
+
+ return;
+ }
+ var Class = this.constructor,
+ pluginName = Class.pluginName || Class._fullName,
+ controls;
+
+ // Unbind bindings.
+ this.off();
+
+ if (pluginName && pluginName !== 'can_control') {
+ // Remove the `className`.
+ this.element.removeClass(pluginName);
+ }
+
+ // Remove from `data`.
+ controls = can.data(this.element, "controls");
+ controls.splice(can.inArray(this, controls), 1);
+
+ can.trigger(this, "destroyed"); // In case we want to know if the `control` is removed.
+
+ this.element = null;
+ }
+ });
+
+ var processors = can.Control.processors,
+ // Processors do the binding.
+ // They return a function that unbinds when called.
+ // The basic processor that binds events.
+ basicProcessor = function(el, event, selector, methodName, control) {
+ return binder(el, event, can.Control._shifter(control, methodName), selector);
+ };
+
+ // Set common events to be processed as a `basicProcessor`
+ each(["change", "click", "contextmenu", "dblclick", "keydown", "keyup",
+ "keypress", "mousedown", "mousemove", "mouseout", "mouseover",
+ "mouseup", "reset", "resize", "scroll", "select", "submit", "focusin",
+ "focusout", "mouseenter", "mouseleave",
+ // #104 - Add touch events as default processors
+ // TOOD feature detect?
+ "touchstart", "touchmove", "touchcancel", "touchend", "touchleave"
+ ], function(v) {
+ processors[v] = basicProcessor;
+ });
+
+ return Control;
+ })(__m3, __m1);
+
+ // ## util/string/deparam/deparam.js
+ var __m20 = (function(can) {
+
+ // ## deparam.js
+ // `can.deparam`
+ // _Takes a string of name value pairs and returns a Object literal that represents those params._
+ var digitTest = /^\d+$/,
+ keyBreaker = /([^\[\]]+)|(\[\])/g,
+ paramTest = /([^?#]*)(#.*)?$/,
+ prep = function(str) {
+ return decodeURIComponent(str.replace(/\+/g, " "));
+ };
+
+
+ can.extend(can, {
+
+ deparam: function(params) {
+
+ var data = {},
+ pairs, lastPart;
+
+ if (params && paramTest.test(params)) {
+
+ pairs = params.split('&'),
+
+ can.each(pairs, function(pair) {
+
+ var parts = pair.split('='),
+ key = prep(parts.shift()),
+ value = prep(parts.join("=")),
+ current = data;
+
+ if (key) {
+ parts = key.match(keyBreaker);
+
+ for (var j = 0, l = parts.length - 1; j < l; j++) {
+ if (!current[parts[j]]) {
+ // If what we are pointing to looks like an `array`
+ current[parts[j]] = digitTest.test(parts[j + 1]) || parts[j + 1] == "[]" ? [] : {};
+ }
+ current = current[parts[j]];
+ }
+ lastPart = parts.pop();
+ if (lastPart == "[]") {
+ current.push(value);
+ } else {
+ current[lastPart] = value;
+ }
+ }
+ });
+ }
+ return data;
+ }
+ });
+ return can;
+ })(__m3, __m2);
+
+ // ## route/route.js
+ var __m19 = (function(can) {
+
+ // ## route.js
+ // `can.route`
+ // _Helps manage browser history (and client state) by synchronizing the
+ // `window.location.hash` with a `can.Observe`._
+ // Helper methods used for matching routes.
+ var
+ // `RegExp` used to match route variables of the type ':name'.
+ // Any word character or a period is matched.
+ matcher = /\:([\w\.]+)/g,
+ // Regular expression for identifying &key=value lists.
+ paramsMatcher = /^(?:&[^=]+=[^&]*)+/,
+ // Converts a JS Object into a list of parameters that can be
+ // inserted into an html element tag.
+ makeProps = function(props) {
+ var tags = [];
+ can.each(props, function(val, name) {
+ tags.push((name === 'className' ? 'class' : name) + '="' +
+ (name === "href" ? val : can.esc(val)) + '"');
+ });
+ return tags.join(" ");
+ },
+ // Checks if a route matches the data provided. If any route variable
+ // is not present in the data, the route does not match. If all route
+ // variables are present in the data, the number of matches is returned
+ // to allow discerning between general and more specific routes.
+ matchesData = function(route, data) {
+ var count = 0,
+ i = 0,
+ defaults = {};
+ // look at default values, if they match ...
+ for (var name in route.defaults) {
+ if (route.defaults[name] === data[name]) {
+ // mark as matched
+ defaults[name] = 1;
+ count++;
+ }
+ }
+ for (; i < route.names.length; i++) {
+ if (!data.hasOwnProperty(route.names[i])) {
+ return -1;
+ }
+ if (!defaults[route.names[i]]) {
+ count++;
+ }
+
+ }
+
+ return count;
+ },
+ onready = !0,
+ location = window.location,
+ wrapQuote = function(str) {
+ return (str + '').replace(/([.?*+\^$\[\]\\(){}|\-])/g, "\\$1");
+ },
+ each = can.each,
+ extend = can.extend;
+
+ can.route = function(url, defaults) {
+ defaults = defaults || {};
+ // Extract the variable names and replace with `RegExp` that will match
+ // an atual URL with values.
+ var names = [],
+ test = url.replace(matcher, function(whole, name, i) {
+ names.push(name);
+ var next = "\\" + (url.substr(i + whole.length, 1) || can.route._querySeparator);
+ // a name without a default value HAS to have a value
+ // a name that has a default value can be empty
+ // The `\\` is for string-escaping giving single `\` for `RegExp` escaping.
+ return "([^" + next + "]" + (defaults[name] ? "*" : "+") + ")";
+ });
+
+ // Add route in a form that can be easily figured out.
+ can.route.routes[url] = {
+ // A regular expression that will match the route when variable values
+ // are present; i.e. for `:page/:type` the `RegExp` is `/([\w\.]*)/([\w\.]*)/` which
+ // will match for any value of `:page` and `:type` (word chars or period).
+ test: new RegExp("^" + test + "($|" + wrapQuote(can.route._querySeparator) + ")"),
+ // The original URL, same as the index for this entry in routes.
+ route: url,
+ // An `array` of all the variable names in this route.
+ names: names,
+ // Default values provided for the variables.
+ defaults: defaults,
+ // The number of parts in the URL separated by `/`.
+ length: url.split('/').length
+ };
+ return can.route;
+ };
+
+
+ extend(can.route, {
+
+ _querySeparator: '&',
+ _paramsMatcher: paramsMatcher,
+
+
+ param: function(data, _setRoute) {
+ // Check if the provided data keys match the names in any routes;
+ // Get the one with the most matches.
+ var route,
+ // Need to have at least 1 match.
+ matches = 0,
+ matchCount,
+ routeName = data.route,
+ propCount = 0;
+
+ delete data.route;
+
+ each(data, function() {
+ propCount++;
+ });
+ // Otherwise find route.
+ each(can.route.routes, function(temp, name) {
+ // best route is the first with all defaults matching
+
+
+ matchCount = matchesData(temp, data);
+ if (matchCount > matches) {
+ route = temp;
+ matches = matchCount;
+ }
+ if (matchCount >= propCount) {
+ return false;
+ }
+ });
+ // If we have a route name in our `can.route` data, and it's
+ // just as good as what currently matches, use that
+ if (can.route.routes[routeName] && matchesData(can.route.routes[routeName], data) === matches) {
+ route = can.route.routes[routeName];
+ }
+ // If this is match...
+ if (route) {
+ var cpy = extend({}, data),
+ // Create the url by replacing the var names with the provided data.
+ // If the default value is found an empty string is inserted.
+ res = route.route.replace(matcher, function(whole, name) {
+ delete cpy[name];
+ return data[name] === route.defaults[name] ? "" : encodeURIComponent(data[name]);
+ }),
+ after;
+ // Remove matching default values
+ each(route.defaults, function(val, name) {
+ if (cpy[name] === val) {
+ delete cpy[name];
+ }
+ });
+
+ // The remaining elements of data are added as
+ // `&` separated parameters to the url.
+ after = can.param(cpy);
+ // if we are paraming for setting the hash
+ // we also want to make sure the route value is updated
+ if (_setRoute) {
+ can.route.attr('route', route.route);
+ }
+ return res + (after ? can.route._querySeparator + after : "");
+ }
+ // If no route was found, there is no hash URL, only paramters.
+ return can.isEmptyObject(data) ? "" : can.route._querySeparator + can.param(data);
+ },
+
+ deparam: function(url) {
+ // See if the url matches any routes by testing it against the `route.test` `RegExp`.
+ // By comparing the URL length the most specialized route that matches is used.
+ var route = {
+ length: -1
+ };
+ each(can.route.routes, function(temp, name) {
+ if (temp.test.test(url) && temp.length > route.length) {
+ route = temp;
+ }
+ });
+ // If a route was matched.
+ if (route.length > -1) {
+
+ var // Since `RegExp` backreferences are used in `route.test` (parens)
+ // the parts will contain the full matched string and each variable (back-referenced) value.
+ parts = url.match(route.test),
+ // Start will contain the full matched string; parts contain the variable values.
+ start = parts.shift(),
+ // The remainder will be the `&key=value` list at the end of the URL.
+ remainder = url.substr(start.length - (parts[parts.length - 1] === can.route._querySeparator ? 1 : 0)),
+ // If there is a remainder and it contains a `&key=value` list deparam it.
+ obj = (remainder && can.route._paramsMatcher.test(remainder)) ? can.deparam(remainder.slice(1)) : {};
+
+ // Add the default values for this route.
+ obj = extend(true, {}, route.defaults, obj);
+ // Overwrite each of the default values in `obj` with those in
+ // parts if that part is not empty.
+ each(parts, function(part, i) {
+ if (part && part !== can.route._querySeparator) {
+ obj[route.names[i]] = decodeURIComponent(part);
+ }
+ });
+ obj.route = route.route;
+ return obj;
+ }
+ // If no route was matched, it is parsed as a `&key=value` list.
+ if (url.charAt(0) !== can.route._querySeparator) {
+ url = can.route._querySeparator + url;
+ }
+ return can.route._paramsMatcher.test(url) ? can.deparam(url.slice(1)) : {};
+ },
+
+ data: new can.Observe({}),
+
+ routes: {},
+
+ ready: function(val) {
+ if (val === false) {
+ onready = val;
+ }
+ if (val === true || onready === true) {
+ can.route._setup();
+ setState();
+ }
+ return can.route;
+ },
+
+ url: function(options, merge) {
+ if (merge) {
+ options = extend({}, curParams, options)
+ }
+ return "#!" + can.route.param(options);
+ },
+
+ link: function(name, options, props, merge) {
+ return "<a " + makeProps(
+ extend({
+ href: can.route.url(options, merge)
+ }, props)) + ">" + name + "</a>";
+ },
+
+ current: function(options) {
+ return location.hash == "#!" + can.route.param(options)
+ },
+ _setup: function() {
+ // If the hash changes, update the `can.route.data`.
+ can.bind.call(window, 'hashchange', setState);
+ },
+ _getHash: function() {
+ return location.href.split(/#!?/)[1] || "";
+ },
+ _setHash: function(serialized) {
+ var path = (can.route.param(serialized, true));
+ location.hash = "#!" + path;
+ return path;
+ }
+ });
+
+
+ // The functions in the following list applied to `can.route` (e.g. `can.route.attr('...')`) will
+ // instead act on the `can.route.data` observe.
+ each(['bind', 'unbind', 'delegate', 'undelegate', 'attr', 'removeAttr'], function(name) {
+ can.route[name] = function() {
+ // `delegate` and `undelegate` require
+ // the `can/observe/delegate` plugin
+ if (!can.route.data[name]) {
+ return;
+ }
+
+ return can.route.data[name].apply(can.route.data, arguments);
+ }
+ })
+
+ var // A ~~throttled~~ debounced function called multiple times will only fire once the
+ // timer runs down. Each call resets the timer.
+ timer,
+ // Intermediate storage for `can.route.data`.
+ curParams,
+ // Deparameterizes the portion of the hash of interest and assign the
+ // values to the `can.route.data` removing existing values no longer in the hash.
+ // setState is called typically by hashchange which fires asynchronously
+ // So it's possible that someone started changing the data before the
+ // hashchange event fired. For this reason, it will not set the route data
+ // if the data is changing or the hash already matches the hash that was set.
+ setState = can.route.setState = function() {
+ var hash = can.route._getHash();
+ curParams = can.route.deparam(hash);
+
+ // if the hash data is currently changing, or
+ // the hash is what we set it to anyway, do NOT change the hash
+ if (!changingData || hash !== lastHash) {
+ can.route.attr(curParams, true);
+ }
+ },
+ // The last hash caused by a data change
+ lastHash,
+ // Are data changes pending that haven't yet updated the hash
+ changingData;
+
+ // If the `can.route.data` changes, update the hash.
+ // Using `.serialize()` retrieves the raw data contained in the `observable`.
+ // This function is ~~throttled~~ debounced so it only updates once even if multiple values changed.
+ // This might be able to use batchNum and avoid this.
+ can.route.bind("change", function(ev, attr) {
+ // indicate that data is changing
+ changingData = 1;
+ clearTimeout(timer);
+ timer = setTimeout(function() {
+ // indicate that the hash is set to look like the data
+ changingData = 0;
+ var serialized = can.route.data.serialize();
+
+ lastHash = can.route._setHash(serialized);
+ }, 1);
+ });
+ // `onready` event...
+ can.bind.call(document, "ready", can.route.ready);
+
+ // Libraries other than jQuery don't execute the document `ready` listener
+ // if we are already DOM ready
+ if ((document.readyState === 'complete' || document.readyState === "interactive") && onready) {
+ can.route.ready();
+ }
+
+ // extend route to have a similar property
+ // that is often checked in mustache to determine
+ // an object's observability
+ can.route.constructor.canMakeObserve = can.Observe.canMakeObserve;
+
+ return can.route;
+ })(__m3, __m7, __m20);
+
+ // ## control/route/route.js
+ var __m21 = (function(can) {
+
+ // ## control/route.js
+ // _Controller route integration._
+
+ can.Control.processors.route = function(el, event, selector, funcName, controller) {
+ selector = selector || "";
+ can.route(selector);
+ var batchNum,
+ check = function(ev, attr, how) {
+ if (can.route.attr('route') === (selector) &&
+ (ev.batchNum === undefined || ev.batchNum !== batchNum)) {
+
+ batchNum = ev.batchNum;
+
+ var d = can.route.attr();
+ delete d.route;
+ if (can.isFunction(controller[funcName])) {
+ controller[funcName](d);
+ } else {
+ controller[controller[funcName]](d);
+ }
+
+ }
+ };
+ can.route.bind('change', check);
+ return function() {
+ can.route.unbind('change', check);
+ };
+ };
+
+ return can;
+ })(__m3, __m19, __m18);
+
+ window['can'] = __m5;
+ })();
\ No newline at end of file
Added: tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.model.queue.js
===================================================================
--- tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.model.queue.js (rev 0)
+++ tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.model.queue.js 2013-06-17 15:45:26 UTC (rev 92)
@@ -0,0 +1,153 @@
+/*!
+ * CanJS - 1.1.6
+ * http://canjs.us/
+ * Copyright (c) 2013 Bitovi
+ * Wed, 29 May 2013 18:59:28 GMT
+ * Licensed MIT
+ * Includes: can/model/queue
+ * Download from: http://canjs.com
+ */
+(function(can) {
+
+ var cleanAttrs = function(changedAttrs, attrs) {
+ var newAttrs = can.extend(true, {}, attrs),
+ attr, current, path;
+ if (changedAttrs) {
+ // go through the attributes returned from the server
+ // and remove those that were changed during the current
+ // request batch
+ for (var i = 0; i < changedAttrs.length; i++) {
+ current = newAttrs;
+ path = changedAttrs[i].split('.');
+ while (path.length > 1) {
+ current = current && current[path.shift()];
+ }
+ current && delete current[path.shift()];
+ }
+ }
+ return newAttrs;
+ },
+ queueRequests = function(success, error, method, callback) {
+ this._changedAttrs = this._changedAttrs || [];
+
+ var def = new can.Deferred,
+ self = this,
+ attrs = this.attr(),
+ queue = this._requestQueue,
+ changedAttrs = this._changedAttrs,
+ reqFn, index;
+
+ reqFn = (function(self, type, success, error) {
+ // Function that performs actual request
+ return function() {
+ // pass already serialized attributes because we want to
+ // save model in state it was when request was queued, not
+ // when request is ran
+ return self.constructor._makeRequest([self, attrs], type || (self.isNew() ? 'create' : 'update'), success, error, callback)
+ }
+ })(this, method, function() {
+ // resolve deferred with results from the request
+ def.resolveWith(self, arguments);
+ // remove current deferred from the queue
+ queue.splice(0, 1);
+ if (queue.length > 0) {
+ // replace queued wrapper function with deferred
+ // returned from the makeRequest function so we
+ // can access it's `abort` function
+ queue[0] = queue[0]();
+ } else {
+ // clean up changed attrs since there is no more requests in the queue
+ changedAttrs.splice(0);
+ }
+
+ }, function() {
+ // reject deferred with results from the request
+ def.rejectWith(self, arguments);
+ // since we failed remove all pending requests from the queue
+ queue.splice(0);
+ // clean up changed attrs since there is no more requests in the queue
+ changedAttrs.splice(0);
+ })
+
+ // Add our fn to the queue
+ index = queue.push(reqFn) - 1;
+
+ // If there is only one request in the queue, run
+ // it immediately.
+ if (queue.length === 1) {
+ // replace queued wrapper function with deferred
+ // returned from the makeRequest function so we
+ // can access it's `abort` function
+ queue[0] = queue[0]();
+ }
+
+ def.abort = function() {
+ var abort;
+ // check if this request is running, if it's not
+ // just remove it from the queue
+ // also all subsequent requests should be removed too
+ abort = queue[index].abort && queue[index].abort();
+ // remove aborted request and any requests after it
+ queue.splice(index);
+ // if there is no more requests in the queue clean up
+ // the changed attributes array
+ if (queue.length === 0) {
+ changedAttrs.splice(0);
+ }
+ return abort;
+ }
+ // deferred will be resolved with original success and
+ // error functions
+ def.then(success, error);
+
+ return def;
+ },
+ _changes = can.Model.prototype._changes,
+ destroyFn = can.Model.prototype.destroy,
+ setupFn = can.Model.prototype.setup;
+
+ can.each(["created", "updated", "destroyed"], function(fn) {
+ var prototypeFn = can.Model.prototype[fn];
+
+ can.Model.prototype[fn] = function(attrs) {
+ if (attrs && typeof attrs == 'object') {
+ attrs = attrs.attr ? attrs.attr() : attrs;
+ // Create backup of last good known state returned
+ // from the server. This allows users to restore it
+ // if API returns error
+ this._backupStore = attrs;
+ attrs = cleanAttrs(this._changedAttrs || [], attrs);
+ }
+ // call the original function with the cleaned up attributes
+ prototypeFn.call(this, attrs);
+ }
+ })
+
+ can.extend(can.Model.prototype, {
+ setup: function() {
+ setupFn.apply(this, arguments);
+ this._requestQueue = new can.Observe.List;
+ },
+ _changes: function(ev, attr, how, newVal, oldVal) {
+ // record changes if there is a request running
+ this._changedAttrs && this._changedAttrs.push(attr);
+ _changes.apply(this, arguments);
+ },
+ hasQueuedRequests: function() {
+ return this._requestQueue.attr('length') > 1;
+ },
+ // call queued save request
+ save: function() {
+ return queueRequests.apply(this, arguments);
+ },
+ destroy: function(success, error) {
+ if (this.isNew()) {
+ // if it's a new instance, call default destroy method
+ return destroyFn.call(this, success, error);
+ }
+ return queueRequests.call(this, success, error, 'destroy', 'destroyed');
+ }
+ })
+
+ return can;
+ })(can);
\ No newline at end of file
Added: tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.mootools.js
===================================================================
--- tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.mootools.js (rev 0)
+++ tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.mootools.js 2013-06-17 15:45:26 UTC (rev 92)
@@ -0,0 +1,4991 @@
+/*!
+ * CanJS - 1.1.6
+ * http://canjs.us/
+ * Copyright (c) 2013 Bitovi
+ * Wed, 29 May 2013 18:59:27 GMT
+ * Licensed MIT
+ * Includes: can/construct,can/observe,can/observe/compute,can/model,can/view,can/view/ejs,can/control,can/route,can/control/route
+ * Download from: http://canjs.com
+ */
+(function(undefined) {
+
+ // ## util/can.js
+ var __m4 = (function() {
+ var can = window.can || {};
+ if (typeof GLOBALCAN === 'undefined' || GLOBALCAN !== false) {
+ window.can = can;
+ }
+
+ can.isDeferred = function(obj) {
+ var isFunction = this.isFunction;
+ // Returns `true` if something looks like a deferred.
+ return obj && isFunction(obj.then) && isFunction(obj.pipe);
+ };
+
+ var cid = 0;
+ can.cid = function(object, name) {
+ if (object._cid) {
+ return object._cid
+ } else {
+ return object._cid = (name || "") + (++cid)
+ }
+ }
+ can.VERSION = '@EDGE';
+ return can;
+ })();
+
+ // ## util/event.js
+ var __m6 = (function(can) {
+
+ // event.js
+ // ---------
+ // _Basic event wrapper._
+ can.addEvent = function(event, fn) {
+ if (!this.__bindEvents) {
+ this.__bindEvents = {};
+ }
+ var eventName = event.split(".")[0];
+
+ if (!this.__bindEvents[eventName]) {
+ this.__bindEvents[eventName] = [];
+ }
+ this.__bindEvents[eventName].push({
+ handler: fn,
+ name: event
+ });
+ return this;
+ };
+ can.removeEvent = function(event, fn) {
+ if (!this.__bindEvents) {
+ return;
+ }
+ var i = 0,
+ events = this.__bindEvents[event.split(".")[0]],
+ ev;
+ while (i < events.length) {
+ ev = events[i]
+ if ((fn && ev.handler === fn) || (!fn && ev.name === event)) {
+ events.splice(i, 1);
+ } else {
+ i++;
+ }
+ }
+ return this;
+ };
+ can.dispatch = function(event) {
+ if (!this.__bindEvents) {
+ return;
+ }
+
+ var eventName = event.type.split(".")[0],
+ handlers = (this.__bindEvents[eventName] || []).slice(0),
+ self = this,
+ args = [event].concat(event.data || []);
+
+ can.each(handlers, function(ev) {
+ event.data = args.slice(1);
+ ev.handler.apply(self, args);
+ });
+ }
+
+ return can;
+
+ })(__m4);
+
+ // ## util/fragment.js
+ var __m7 = (function(can) {
+
+ // fragment.js
+ // ---------
+ // _DOM Fragment support._
+ var fragmentRE = /^\s*<(\w+)[^>]*>/,
+ fragment = function(html, name) {
+ if (name === undefined) {
+ name = fragmentRE.test(html) && RegExp.$1;
+ }
+
+ if (html && can.isFunction(html.replace)) {
+ // Fix "XHTML"-style tags in all browsers
+ html = html.replace(/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, "<$1></$2>");
+ }
+
+ var container = document.createElement('div'),
+ temp = document.createElement('div')
+
+ // IE's parser will strip any `<tr><td>` tags when `innerHTML`
+ // is called on a `tbody`. To get around this, we construct a
+ // valid table with a `tbody` that has the `innerHTML` we want.
+ // Then the container is the `firstChild` of the `tbody`.
+ // [source](http://www.ericvasilik.com/2006/07/code-karma.html).
+ if (name === "tbody" || name === "tfoot" || name === "thead") {
+ temp.innerHTML = "<table>" + html + "</table>";
+ container = temp.firstChild.nodeType === 3 ? temp.lastChild : temp.firstChild;
+ } else if (name === "tr") {
+ temp.innerHTML = "<table><tbody>" + html + "</tbody></table>";
+ container = temp.firstChild.nodeType === 3 ? temp.lastChild : temp.firstChild.firstChild;
+ } else if (name === "td" || name === "th") {
+ temp.innerHTML = "<table><tbody><tr>" + html + "</tr></tbody></table>";
+ container = temp.firstChild.nodeType === 3 ? temp.lastChild : temp.firstChild.firstChild.firstChild;
+ } else if (name === 'option') {
+ temp.innerHTML = "<select>" + html + "</select>";
+ container = temp.firstChild.nodeType === 3 ? temp.lastChild : temp.firstChild;
+ } else {
+ container.innerHTML = '' + html;
+ }
+
+ // IE8 barfs if you pass slice a `childNodes` object, so make a copy.
+ var tmp = {},
+ children = container.childNodes;
+ tmp.length = children.length;
+ for (var i = 0; i < children.length; i++) {
+ tmp[i] = children[i];
+ }
+ return [].slice.call(tmp);
+ }
+
+ can.buildFragment = function(html, nodes) {
+ var parts = fragment(html),
+ frag = document.createDocumentFragment();
+
+ can.each(parts, function(part) {
+ frag.appendChild(part);
+ })
+ return frag;
+ };
+
+ return can;
+ })(__m4);
+
+ // ## util/deferred.js
+ var __m8 = (function(can) {
+
+ // deferred.js
+ // ---------
+ // _Lightweight, jQuery style deferreds._
+ // extend is usually provided by the wrapper but to avoid steal.then calls
+ // we define a simple extend here as well
+ var extend = function(target, src) {
+ for (var key in src) {
+ if (src.hasOwnProperty(key)) {
+ target[key] = src[key];
+ }
+ }
+ },
+ Deferred = function(func) {
+ if (!(this instanceof Deferred))
+ return new Deferred();
+
+ this._doneFuncs = [];
+ this._failFuncs = [];
+ this._resultArgs = null;
+ this._status = "";
+
+ // Check for option `function` -- call it with this as context and as first
+ // parameter, as specified in jQuery API.
+ func && func.call(this, this);
+ };
+
+ can.Deferred = Deferred;
+ can.when = Deferred.when = function() {
+ var args = can.makeArray(arguments);
+ if (args.length < 2) {
+ var obj = args[0];
+ if (obj && (can.isFunction(obj.isResolved) && can.isFunction(obj.isRejected))) {
+ return obj;
+ } else {
+ return Deferred().resolve(obj);
+ }
+ } else {
+
+ var df = Deferred(),
+ done = 0,
+ // Resolve params -- params of each resolve, we need to track them down
+ // to be able to pass them in the correct order if the master
+ // needs to be resolved.
+ rp = [];
+
+ can.each(args, function(arg, j) {
+ arg.done(function() {
+ rp[j] = (arguments.length < 2) ? arguments[0] : arguments;
+ if (++done == args.length) {
+ df.resolve.apply(df, rp);
+ }
+ }).fail(function() {
+ df.reject((arguments.length === 1) ? arguments[0] : arguments);
+ });
+ });
+
+ return df;
+
+ }
+ }
+
+ var resolveFunc = function(type, _status) {
+ return function(context) {
+ var args = this._resultArgs = (arguments.length > 1) ? arguments[1] : [];
+ return this.exec(context, this[type], args, _status);
+ }
+ },
+ doneFunc = function(type, _status) {
+ return function() {
+ var self = this;
+ // In Safari, the properties of the `arguments` object are not enumerable,
+ // so we have to convert arguments to an `Array` that allows `can.each` to loop over them.
+ can.each(Array.prototype.slice.call(arguments), function(v, i, args) {
+ if (!v)
+ return;
+ if (v.constructor === Array) {
+ args.callee.apply(self, v)
+ } else {
+ // Immediately call the `function` if the deferred has been resolved.
+ if (self._status === _status)
+ v.apply(self, self._resultArgs || []);
+
+ self[type].push(v);
+ }
+ });
+ return this;
+ }
+ };
+
+ extend(Deferred.prototype, {
+ pipe: function(done, fail) {
+ var d = can.Deferred();
+ this.done(function() {
+ d.resolve(done.apply(this, arguments));
+ });
+
+ this.fail(function() {
+ if (fail) {
+ d.reject(fail.apply(this, arguments));
+ } else {
+ d.reject.apply(d, arguments);
+ }
+ });
+ return d;
+ },
+ resolveWith: resolveFunc("_doneFuncs", "rs"),
+ rejectWith: resolveFunc("_failFuncs", "rj"),
+ done: doneFunc("_doneFuncs", "rs"),
+ fail: doneFunc("_failFuncs", "rj"),
+ always: function() {
+ var args = can.makeArray(arguments);
+ if (args.length && args[0])
+ this.done(args[0]).fail(args[0]);
+
+ return this;
+ },
+
+ then: function() {
+ var args = can.makeArray(arguments);
+ // Fail `function`(s)
+ if (args.length > 1 && args[1])
+ this.fail(args[1]);
+
+ // Done `function`(s)
+ if (args.length && args[0])
+ this.done(args[0]);
+
+ return this;
+ },
+
+ state: function() {
+ switch (this._status) {
+ case 'rs':
+ return 'resolved';
+ case 'rj':
+ return 'rejected';
+ default:
+ return 'pending';
+ }
+ },
+
+ isResolved: function() {
+ return this._status === "rs";
+ },
+
+ isRejected: function() {
+ return this._status === "rj";
+ },
+
+ reject: function() {
+ return this.rejectWith(this, arguments);
+ },
+
+ resolve: function() {
+ return this.resolveWith(this, arguments);
+ },
+
+ exec: function(context, dst, args, st) {
+ if (this._status !== "")
+ return this;
+
+ this._status = st;
+
+ can.each(dst, function(d) {
+ d.apply(context, args);
+ });
+
+ return this;
+ }
+ });
+
+ return can;
+ })(__m4);
+
+ // ## util/array/each.js
+ var __m9 = (function(can) {
+ can.each = function(elements, callback, context) {
+ var i = 0,
+ key;
+ if (elements) {
+ if (typeof elements.length === 'number' && elements.pop) {
+ if (elements.attr) {
+ elements.attr('length');
+ }
+ for (key = elements.length; i < key; i++) {
+ if (callback.call(context || elements[i], elements[i], i, elements) === false) {
+ break;
+ }
+ }
+ } else if (elements.hasOwnProperty) {
+ for (key in elements) {
+ if (elements.hasOwnProperty(key)) {
+ if (callback.call(context || elements[key], elements[key], key, elements) === false) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ return elements;
+ };
+
+ return can;
+ })(__m4);
+
+ // ## util/object/isplain/isplain.js
+ var __m10 = (function(can) {
+ var core_hasOwn = Object.prototype.hasOwnProperty,
+ isWindow = function(obj) {
+ return obj != null && obj == obj.window;
+ },
+ isPlainObject = function(obj) {
+ // Must be an Object.
+ // Because of IE, we also have to check the presence of the constructor property.
+ // Make sure that DOM nodes and window objects don't pass through, as well
+ if (!obj || (typeof obj !== "object") || obj.nodeType || isWindow(obj)) {
+ return false;
+ }
+
+ try {
+ // Not own constructor property must be Object
+ if (obj.constructor && !core_hasOwn.call(obj, "constructor") && !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) {
+ return false;
+ }
+ } catch (e) {
+ // IE8,9 Will throw exceptions on certain host objects #9897
+ return false;
+ }
+
+ // Own properties are enumerated firstly, so to speed up,
+ // if last one is own, then all properties are own.
+
+ var key;
+ for (key in obj) {}
+
+ return key === undefined || core_hasOwn.call(obj, key);
+ }
+
+ can.isPlainObject = isPlainObject;
+ return can;
+ })(__m4);
+
+ // ## util/mootools/mootools.js
+ var __m3 = (function(can) {
+ // mootools.js
+ // ---------
+ // _MooTools node list._
+ // Map string helpers.
+ can.trim = function(s) {
+ return s && s.trim()
+ }
+
+ // This extend() function is ruthlessly and shamelessly stolen from
+ // jQuery 1.8.2:, lines 291-353.
+ var extend = function() {
+ var options, name, src, copy, copyIsArray, clone,
+ target = arguments[0] || {},
+ i = 1,
+ length = arguments.length,
+ deep = false;
+
+ // Handle a deep copy situation
+ if (typeof target === "boolean") {
+ deep = target;
+ target = arguments[1] || {};
+ // skip the boolean and the target
+ i = 2;
+ }
+
+ // Handle case when target is a string or something (possible in deep copy)
+ if (typeof target !== "object" && !can.isFunction(target)) {
+ target = {};
+ }
+
+ // extend jQuery itself if only one argument is passed
+ if (length === i) {
+ target = this;
+ --i;
+ }
+
+ for (; i < length; i++) {
+ // Only deal with non-null/undefined values
+ if ((options = arguments[i]) != null) {
+ // Extend the base object
+ for (name in options) {
+ src = target[name];
+ copy = options[name];
+
+ // Prevent never-ending loop
+ if (target === copy) {
+ continue;
+ }
+
+ // Recurse if we're merging plain objects or arrays
+ if (deep && copy && (can.isPlainObject(copy) || (copyIsArray = can.isArray(copy)))) {
+ if (copyIsArray) {
+ copyIsArray = false;
+ clone = src && can.isArray(src) ? src : [];
+
+ } else {
+ clone = src && can.isPlainObject(src) ? src : {};
+ }
+
+ // Never move original objects, clone them
+ target[name] = can.extend(deep, clone, copy);
+
+ // Don't bring in undefined values
+ } else if (copy !== undefined) {
+ target[name] = copy;
+ }
+ }
+ }
+ }
+
+ // Return the modified object
+ return target;
+ };
+
+ can.extend = extend;
+
+ // Map array helpers.
+ can.makeArray = function(item) {
+ // All other libraries return a copy if item is an array.
+ // The original Mootools Array.from returned the same item so we need to slightly modify it
+ if (item == null) return [];
+ try {
+ return (Type.isEnumerable(item) && typeof item != 'string') ? Array.prototype.slice.call(item) : [item];
+ } catch (ex) {
+ // some things like DOMNodeChildCollections don't slice so good.
+ // This pains me, but it has to be done.
+ var arr = [],
+ i;
+ for (i = 0; i < item.length; ++i) {
+ arr.push(item[i]);
+ }
+ return arr;
+ }
+ }
+
+ can.isArray = function(arr) {
+ return typeOf(arr) === 'array'
+ };
+ can.inArray = function(item, arr) {
+ if (!arr) {
+ return -1;
+ }
+ return Array.prototype.indexOf.call(arr, item);
+ }
+ can.map = function(arr, fn) {
+ return Array.from(arr || []).map(fn);
+ }
+
+ // Map object helpers.
+ can.param = function(object) {
+ return Object.toQueryString(object)
+ }
+ can.isEmptyObject = function(object) {
+ return Object.keys(object).length === 0;
+ }
+ // Map function helpers.
+ can.proxy = function(func) {
+ var args = can.makeArray(arguments),
+ func = args.shift();
+
+ return func.bind.apply(func, args)
+ }
+ can.isFunction = function(f) {
+ return typeOf(f) == 'function'
+ }
+
+ // Make this object so you can bind on it.
+ can.bind = function(ev, cb) {
+
+ // If we can bind to it...
+ if (this.bind && this.bind !== can.bind) {
+ this.bind(ev, cb)
+ } else if (this.addEvent) {
+ this.addEvent(ev, cb);
+ } else if (this.nodeName && this.nodeType == 1) {
+ $(this).addEvent(ev, cb)
+ } else {
+ // Make it bind-able...
+ can.addEvent.call(this, ev, cb)
+ }
+ return this;
+ }
+ can.unbind = function(ev, cb) {
+ // If we can bind to it...
+ if (this.unbind && this.unbind !== can.unbind) {
+ this.unbind(ev, cb)
+ } else if (this.removeEvent) {
+ this.removeEvent(ev, cb)
+ }
+ if (this.nodeName && this.nodeType == 1) {
+ $(this).removeEvent(ev, cb)
+ } else {
+ // Make it bind-able...
+ can.removeEvent.call(this, ev, cb)
+ }
+ return this;
+ }
+ can.trigger = function(item, event, args, bubble) {
+ // Defaults to `true`.
+ bubble = (bubble === undefined ? true : bubble);
+ args = args || []
+ if (item.fireEvent) {
+ item = item[0] || item;
+ // walk up parents to simulate bubbling .
+ while (item) {
+ // Handle walking yourself.
+ if (!event.type) {
+ event = {
+ type: event,
+ target: item
+ }
+ }
+ var events = (item !== window ?
+ can.$(item).retrieve('events')[0] :
+ item.retrieve('events'));
+ if (events && events[event.type]) {
+ events[event.type].keys.each(function(fn) {
+ fn.apply(this, [event].concat(args));
+ }, this);
+ }
+ // If we are bubbling, get parent node.
+ if (bubble && item.parentNode) {
+ item = item.parentNode
+ } else {
+ item = null;
+ }
+
+ }
+
+ } else {
+ if (typeof event === 'string') {
+ event = {
+ type: event
+ }
+ }
+ event.target = event.target || item;
+ event.data = args
+ can.dispatch.call(item, event)
+ }
+ }
+ can.delegate = function(selector, ev, cb) {
+ if (this.delegate) {
+ this.delegate(selector, ev, cb)
+ } else if (this.addEvent) {
+ this.addEvent(ev + ":relay(" + selector + ")", cb)
+ } else {
+ // make it bind-able ...
+ }
+ return this;
+ }
+ can.undelegate = function(selector, ev, cb) {
+ if (this.undelegate) {
+ this.undelegate(selector, ev, cb)
+ } else if (this.removeEvent) {
+ this.removeEvent(ev + ":relay(" + selector + ")", cb)
+ } else {
+ // make it bind-able ...
+
+ }
+ return this;
+ }
+ var optionsMap = {
+ type: "method",
+ success: undefined,
+ error: undefined
+ }
+ var updateDeferred = function(xhr, d) {
+ for (var prop in xhr) {
+ if (typeof d[prop] == 'function') {
+ d[prop] = function() {
+ xhr[prop].apply(xhr, arguments)
+ }
+ } else {
+ d[prop] = prop[xhr]
+ }
+ }
+ }
+ can.ajax = function(options) {
+ var d = can.Deferred(),
+ requestOptions = can.extend({}, options),
+ request;
+ // Map jQuery options to MooTools options.
+
+ for (var option in optionsMap) {
+ if (requestOptions[option] !== undefined) {
+ requestOptions[optionsMap[option]] = requestOptions[option];
+ delete requestOptions[option]
+ }
+ }
+ // Mootools defaults to 'post', but Can expects a default of 'get'
+ requestOptions.method = requestOptions.method || 'get';
+ requestOptions.url = requestOptions.url.toString();
+
+ var success = options.onSuccess || options.success,
+ error = options.onFailure || options.error;
+
+ requestOptions.onSuccess = function(response, xml) {
+ var data = response;
+ updateDeferred(request.xhr, d);
+ d.resolve(data, "success", request.xhr);
+ success && success(data, "success", request.xhr);
+ }
+ requestOptions.onFailure = function() {
+ updateDeferred(request.xhr, d);
+ d.reject(request.xhr, "error");
+ error(request.xhr, "error");
+ }
+
+ if (options.dataType === 'json') {
+ request = new Request.JSON(requestOptions);
+ } else {
+ request = new Request(requestOptions);
+ }
+ request.send();
+ updateDeferred(request.xhr, d);
+ return d;
+
+ }
+ // Element -- get the wrapped helper.
+ can.$ = function(selector) {
+ if (selector === window) {
+ return window;
+ }
+ return $$(selector)
+ }
+
+ // Add `document` fragment support.
+ var old = document.id;
+ document.id = function(el) {
+ if (el && el.nodeType === 11) {
+ return el
+ } else {
+ return old.apply(document, arguments);
+ }
+ };
+ can.append = function(wrapped, html) {
+ if (typeof html === 'string') {
+ html = can.buildFragment(html)
+ }
+ return wrapped.grab(html)
+ }
+ can.filter = function(wrapped, filter) {
+ return wrapped.filter(filter);
+ }
+ can.data = function(wrapped, key, value) {
+ if (value === undefined) {
+ return wrapped[0].retrieve(key)
+ } else {
+ return wrapped.store(key, value)
+ }
+ }
+ can.addClass = function(wrapped, className) {
+ return wrapped.addClass(className);
+ }
+ can.remove = function(wrapped) {
+ // We need to remove text nodes ourselves.
+ var filtered = wrapped.filter(function(node) {
+ if (node.nodeType !== 1) {
+ node.parentNode.removeChild(node);
+ } else {
+ return true;
+ }
+ })
+ filtered.destroy();
+ return filtered;
+ }
+
+ // Destroyed method.
+ var destroy = Element.prototype.destroy;
+ Element.implement({
+ destroy: function() {
+ can.trigger(this, "destroyed", [], false)
+ var elems = this.getElementsByTagName("*");
+ for (var i = 0, elem;
+ (elem = elems[i]) !== undefined; i++) {
+ can.trigger(elem, "destroyed", [], false);
+ }
+ destroy.apply(this, arguments)
+ }
+ });
+ can.get = function(wrapped, index) {
+ return wrapped[index];
+ }
+
+ // Overwrite to handle IE not having an id.
+ // IE barfs if text node.
+ var idOf = Slick.uidOf;
+ Slick.uidOf = function(node) {
+ // for some reason, in IE8, node will be the window but not equal it.
+ if (node.nodeType === 1 || node === window || node.document === document) {
+ return idOf(node);
+ } else {
+ return Math.random();
+ }
+ }
+
+ return can;
+ })(__m4, {}, __m6, __m7, __m8, __m9, __m10);
+
+ // ## util/string/string.js
+ var __m2 = (function(can) {
+ // ##string.js
+ // _Miscellaneous string utility functions._
+
+ // Several of the methods in this plugin use code adapated from Prototype
+ // Prototype JavaScript framework, version 1.6.0.1.
+ // © 2005-2007 Sam Stephenson
+ var strUndHash = /_|-/,
+ strColons = /\=\=/,
+ strWords = /([A-Z]+)([A-Z][a-z])/g,
+ strLowUp = /([a-z\d])([A-Z])/g,
+ strDash = /([a-z\d])([A-Z])/g,
+ strReplacer = /\{([^\}]+)\}/g,
+ strQuote = /"/g,
+ strSingleQuote = /'/g,
+
+ // Returns the `prop` property from `obj`.
+ // If `add` is true and `prop` doesn't exist in `obj`, create it as an
+ // empty object.
+ getNext = function(obj, prop, add) {
+ var result = obj[prop];
+
+ if (result === undefined && add === true) {
+ result = obj[prop] = {}
+ }
+ return result
+ },
+
+ // Returns `true` if the object can have properties (no `null`s).
+ isContainer = function(current) {
+ return (/^f|^o/).test(typeof current);
+ };
+
+ can.extend(can, {
+ // Escapes strings for HTML.
+
+ esc: function(content) {
+ // Convert bad values into empty strings
+ var isInvalid = content === null || content === undefined || (isNaN(content) && ("" + content === 'NaN'));
+ return ("" + (isInvalid ? '' : content))
+ .replace(/&/g, '&')
+ .replace(/</g, '<')
+ .replace(/>/g, '>')
+ .replace(strQuote, '"')
+ .replace(strSingleQuote, "'");
+ },
+
+
+ getObject: function(name, roots, add) {
+
+ // The parts of the name we are looking up
+ // `['App','Models','Recipe']`
+ var parts = name ? name.split('.') : [],
+ length = parts.length,
+ current,
+ r = 0,
+ i, container, rootsLength;
+
+ // Make sure roots is an `array`.
+ roots = can.isArray(roots) ? roots : [roots || window];
+
+ rootsLength = roots.length
+
+ if (!length) {
+ return roots[0];
+ }
+
+ // For each root, mark it as current.
+ for (r; r < rootsLength; r++) {
+ current = roots[r];
+ container = undefined;
+
+ // Walk current to the 2nd to last object or until there
+ // is not a container.
+ for (i = 0; i < length && isContainer(current); i++) {
+ container = current;
+ current = getNext(container, parts[i]);
+ }
+
+ // If we found property break cycle
+ if (container !== undefined && current !== undefined) {
+ break
+ }
+ }
+
+ // Remove property from found container
+ if (add === false && current !== undefined) {
+ delete container[parts[i - 1]]
+ }
+
+ // When adding property add it to the first root
+ if (add === true && current === undefined) {
+ current = roots[0]
+
+ for (i = 0; i < length && isContainer(current); i++) {
+ current = getNext(current, parts[i], true);
+ }
+ }
+
+ return current;
+ },
+ // Capitalizes a string.
+
+ capitalize: function(s, cache) {
+ // Used to make newId.
+ return s.charAt(0).toUpperCase() + s.slice(1);
+ },
+
+ // Underscores a string.
+
+ underscore: function(s) {
+ return s
+ .replace(strColons, '/')
+ .replace(strWords, '$1_$2')
+ .replace(strLowUp, '$1_$2')
+ .replace(strDash, '_')
+ .toLowerCase();
+ },
+ // Micro-templating.
+
+ sub: function(str, data, remove) {
+ var obs = [];
+
+ str = str || '';
+
+ obs.push(str.replace(strReplacer, function(whole, inside) {
+
+ // Convert inside to type.
+ var ob = can.getObject(inside, data, remove === true ? false : undefined);
+
+ if (ob === undefined) {
+ obs = null;
+ return "";
+ }
+
+ // If a container, push into objs (which will return objects found).
+ if (isContainer(ob) && obs) {
+ obs.push(ob);
+ return "";
+ }
+
+ return "" + ob;
+ }));
+
+ return obs === null ? obs : (obs.length <= 1 ? obs[0] : obs);
+ },
+
+ // These regex's are used throughout the rest of can, so let's make
+ // them available.
+ replacer: strReplacer,
+ undHash: strUndHash
+ });
+ return can;
+ })(__m3);
+
+ // ## construct/construct.js
+ var __m1 = (function(can) {
+
+ // ## construct.js
+ // `can.Construct`
+ // _This is a modified version of
+ // [John Resig's class](http://ejohn.org/blog/simple-javascript-inheritance/).
+ // It provides class level inheritance and callbacks._
+
+ // A private flag used to initialize a new class instance without
+ // initializing it's bindings.
+ var initializing = 0;
+
+
+ can.Construct = function() {
+ if (arguments.length) {
+ return can.Construct.extend.apply(can.Construct, arguments);
+ }
+ };
+
+
+ can.extend(can.Construct, {
+
+ newInstance: function() {
+ // Get a raw instance object (`init` is not called).
+ var inst = this.instance(),
+ arg = arguments,
+ args;
+
+ // Call `setup` if there is a `setup`
+ if (inst.setup) {
+ args = inst.setup.apply(inst, arguments);
+ }
+
+ // Call `init` if there is an `init`
+ // If `setup` returned `args`, use those as the arguments
+ if (inst.init) {
+ inst.init.apply(inst, args || arguments);
+ }
+
+ return inst;
+ },
+ // Overwrites an object with methods. Used in the `super` plugin.
+ // `newProps` - New properties to add.
+ // `oldProps` - Where the old properties might be (used with `super`).
+ // `addTo` - What we are adding to.
+ _inherit: function(newProps, oldProps, addTo) {
+ can.extend(addTo || newProps, newProps || {})
+ },
+ // used for overwriting a single property.
+ // this should be used for patching other objects
+ // the super plugin overwrites this
+ _overwrite: function(what, oldProps, propName, val) {
+ what[propName] = val;
+ },
+ // Set `defaults` as the merger of the parent `defaults` and this
+ // object's `defaults`. If you overwrite this method, make sure to
+ // include option merging logic.
+
+ setup: function(base, fullName) {
+ this.defaults = can.extend(true, {}, base.defaults, this.defaults);
+ },
+ // Create's a new `class` instance without initializing by setting the
+ // `initializing` flag.
+ instance: function() {
+
+ // Prevents running `init`.
+ initializing = 1;
+
+ var inst = new this();
+
+ // Allow running `init`.
+ initializing = 0;
+
+ return inst;
+ },
+ // Extends classes.
+
+ extend: function(fullName, klass, proto) {
+ // Figure out what was passed and normalize it.
+ if (typeof fullName != 'string') {
+ proto = klass;
+ klass = fullName;
+ fullName = null;
+ }
+
+ if (!proto) {
+ proto = klass;
+ klass = null;
+ }
+ proto = proto || {};
+
+ var _super_class = this,
+ _super = this.prototype,
+ name, shortName, namespace, prototype;
+
+ // Instantiate a base class (but only create the instance,
+ // don't run the init constructor).
+ prototype = this.instance();
+
+ // Copy the properties over onto the new prototype.
+ can.Construct._inherit(proto, _super, prototype);
+
+ // The dummy class constructor.
+
+ function Constructor() {
+ // All construction is actually done in the init method.
+ if (!initializing) {
+ return this.constructor !== Constructor && arguments.length ?
+ // We are being called without `new` or we are extending.
+ arguments.callee.extend.apply(arguments.callee, arguments) :
+ // We are being called with `new`.
+ this.constructor.newInstance.apply(this.constructor, arguments);
+ }
+ }
+
+ // Copy old stuff onto class (can probably be merged w/ inherit)
+ for (name in _super_class) {
+ if (_super_class.hasOwnProperty(name)) {
+ Constructor[name] = _super_class[name];
+ }
+ }
+
+ // Copy new static properties on class.
+ can.Construct._inherit(klass, _super_class, Constructor);
+
+ // Setup namespaces.
+ if (fullName) {
+
+ var parts = fullName.split('.'),
+ shortName = parts.pop(),
+ current = can.getObject(parts.join('.'), window, true),
+ namespace = current,
+ _fullName = can.underscore(fullName.replace(/\./g, "_")),
+ _shortName = can.underscore(shortName);
+
+
+
+ current[shortName] = Constructor;
+ }
+
+ // Set things that shouldn't be overwritten.
+ can.extend(Constructor, {
+ constructor: Constructor,
+ prototype: prototype,
+
+ namespace: namespace,
+
+ _shortName: _shortName,
+
+ fullName: fullName,
+ _fullName: _fullName
+ });
+
+ // Dojo and YUI extend undefined
+ if (shortName !== undefined) {
+ Constructor.shortName = shortName;
+ }
+
+ // Make sure our prototype looks nice.
+ Constructor.prototype.constructor = Constructor;
+
+
+ // Call the class `setup` and `init`
+ var t = [_super_class].concat(can.makeArray(arguments)),
+ args = Constructor.setup.apply(Constructor, t);
+
+ if (Constructor.init) {
+ Constructor.init.apply(Constructor, args || t);
+ }
+
+
+ return Constructor;
+
+ }
+
+ });
+ return can.Construct;
+ })(__m2);
+
+ // ## util/bind/bind.js
+ var __m12 = (function(can) {
+
+
+ // ## Bind helpers
+ can.bindAndSetup = function() {
+ // Add the event to this object
+ can.addEvent.apply(this, arguments);
+ // If not initializing, and the first binding
+ // call bindsetup if the function exists.
+ if (!this._init) {
+ if (!this._bindings) {
+ this._bindings = 1;
+ // setup live-binding
+ this._bindsetup && this._bindsetup();
+
+ } else {
+ this._bindings++;
+ }
+
+ }
+
+ return this;
+ };
+
+ can.unbindAndTeardown = function(ev, handler) {
+ // Remove the event handler
+ can.removeEvent.apply(this, arguments);
+
+ this._bindings--;
+ // If there are no longer any bindings and
+ // there is a bindteardown method, call it.
+ if (!this._bindings) {
+ this._bindteardown && this._bindteardown();
+ }
+ return this;
+ }
+
+ return can;
+
+ })(__m3);
+
+ // ## observe/observe.js
+ var __m11 = (function(can, bind) {
+ // ## observe.js
+ // `can.Observe`
+ // _Provides the observable pattern for JavaScript Objects._
+ // Returns `true` if something is an object with properties of its own.
+ var canMakeObserve = function(obj) {
+ return obj && !can.isDeferred(obj) && (can.isArray(obj) || can.isPlainObject(obj) || (obj instanceof can.Observe));
+ },
+
+ // Removes all listeners.
+ unhookup = function(items, namespace) {
+ return can.each(items, function(item) {
+ if (item && item.unbind) {
+ item.unbind("change" + namespace);
+ }
+ });
+ },
+ // Listens to changes on `child` and "bubbles" the event up.
+ // `child` - The object to listen for changes on.
+ // `prop` - The property name is at on.
+ // `parent` - The parent object of prop.
+ // `ob` - (optional) The Observe object constructor
+ // `list` - (optional) The observable list constructor
+ hookupBubble = function(child, prop, parent, Ob, List) {
+ Ob = Ob || Observe;
+ List = List || Observe.List;
+
+ // If it's an `array` make a list, otherwise a child.
+ if (child instanceof Observe) {
+ // We have an `observe` already...
+ // Make sure it is not listening to this already
+ // It's only listening if it has bindings already.
+ parent._bindings && unhookup([child], parent._cid);
+ } else if (can.isArray(child)) {
+ child = new List(child);
+ } else {
+ child = new Ob(child);
+ }
+ // only listen if something is listening to you
+ if (parent._bindings) {
+ // Listen to all changes and `batchTrigger` upwards.
+ bindToChildAndBubbleToParent(child, prop, parent)
+ }
+
+
+ return child;
+ },
+ bindToChildAndBubbleToParent = function(child, prop, parent) {
+ child.bind("change" + parent._cid, function() {
+ // `batchTrigger` the type on this...
+ var args = can.makeArray(arguments),
+ ev = args.shift();
+ args[0] = (prop === "*" ? [parent.indexOf(child), args[0]] : [prop, args[0]]).join(".");
+
+ // track objects dispatched on this observe
+ ev.triggeredNS = ev.triggeredNS || {};
+
+ // if it has already been dispatched exit
+ if (ev.triggeredNS[parent._cid]) {
+ return;
+ }
+
+ ev.triggeredNS[parent._cid] = true;
+ // send change event with modified attr to parent
+ can.trigger(parent, ev, args);
+ // send modified attr event to parent
+ //can.trigger(parent, args[0], args);
+ });
+ }
+ // An `id` to track events for a given observe.
+ observeId = 0,
+ // A helper used to serialize an `Observe` or `Observe.List`.
+ // `observe` - The observable.
+ // `how` - To serialize with `attr` or `serialize`.
+ // `where` - To put properties, in an `{}` or `[]`.
+ serialize = function(observe, how, where) {
+ // Go through each property.
+ observe.each(function(val, name) {
+ // If the value is an `object`, and has an `attrs` or `serialize` function.
+ where[name] = canMakeObserve(val) && can.isFunction(val[how]) ?
+ // Call `attrs` or `serialize` to get the original data back.
+ val[how]() :
+ // Otherwise return the value.
+ val;
+ });
+ return where;
+ },
+ attrParts = function(attr, keepKey) {
+ if (keepKey) {
+ return [attr];
+ }
+ return can.isArray(attr) ? attr : ("" + attr).split(".");
+ },
+ // Which batch of events this is for -- might not want to send multiple
+ // messages on the same batch. This is mostly for event delegation.
+ batchNum = 1,
+ // how many times has start been called without a stop
+ transactions = 0,
+ // an array of events within a transaction
+ batchEvents = [],
+ stopCallbacks = [],
+ makeBindSetup = function(wildcard) {
+ return function() {
+ var parent = this;
+ this._each(function(child, prop) {
+ if (child && child.bind) {
+ bindToChildAndBubbleToParent(child, wildcard || prop, parent)
+ }
+ })
+ };
+ };
+
+
+ var Observe = can.Map = can.Observe = can.Construct({
+
+ // keep so it can be overwritten
+ bind: can.bindAndSetup,
+ unbind: can.unbindAndTeardown,
+ id: "id",
+ canMakeObserve: canMakeObserve,
+ // starts collecting events
+ // takes a callback for after they are updated
+ // how could you hook into after ejs
+
+ startBatch: function(batchStopHandler) {
+ transactions++;
+ batchStopHandler && stopCallbacks.push(batchStopHandler);
+ },
+
+ stopBatch: function(force, callStart) {
+ if (force) {
+ transactions = 0;
+ } else {
+ transactions--;
+ }
+
+ if (transactions == 0) {
+ var items = batchEvents.slice(0),
+ callbacks = stopCallbacks.slice(0);
+ batchEvents = [];
+ stopCallbacks = [];
+ batchNum++;
+ callStart && this.startBatch();
+ can.each(items, function(args) {
+ can.trigger.apply(can, args);
+ });
+ can.each(callbacks, function(cb) {
+ cb();
+ });
+ }
+ },
+
+ triggerBatch: function(item, event, args) {
+ // Don't send events if initalizing.
+ if (!item._init) {
+ if (transactions == 0) {
+ return can.trigger(item, event, args);
+ } else {
+ event = typeof event === "string" ? {
+ type: event
+ } :
+ event;
+ event.batchNum = batchNum;
+ batchEvents.push([
+ item,
+ event,
+ args
+ ]);
+ }
+ }
+ },
+
+ keys: function(observe) {
+ var keys = [];
+ Observe.__reading && Observe.__reading(observe, '__keys');
+ for (var keyName in observe._data) {
+ keys.push(keyName);
+ }
+ return keys;
+ }
+ },
+
+ {
+ setup: function(obj) {
+ // `_data` is where we keep the properties.
+ this._data = {};
+
+ // The namespace this `object` uses to listen to events.
+ can.cid(this, ".observe");
+ // Sets all `attrs`.
+ this._init = 1;
+ this.attr(obj);
+ this.bind('change' + this._cid, can.proxy(this._changes, this));
+ delete this._init;
+ },
+ _bindsetup: makeBindSetup(),
+ _bindteardown: function() {
+ var cid = this._cid;
+ this._each(function(child) {
+ unhookup([child], cid)
+ })
+ },
+ _changes: function(ev, attr, how, newVal, oldVal) {
+ Observe.triggerBatch(this, {
+ type: attr,
+ batchNum: ev.batchNum
+ }, [newVal, oldVal]);
+ },
+ _triggerChange: function(attr, how, newVal, oldVal) {
+ Observe.triggerBatch(this, "change", can.makeArray(arguments))
+ },
+ // no live binding iterator
+ _each: function(callback) {
+ var data = this.__get();
+ for (var prop in data) {
+ if (data.hasOwnProperty(prop)) {
+ callback(data[prop], prop)
+ }
+ }
+ },
+
+ attr: function(attr, val) {
+ // This is super obfuscated for space -- basically, we're checking
+ // if the type of the attribute is not a `number` or a `string`.
+ var type = typeof attr;
+ if (type !== "string" && type !== "number") {
+ return this._attrs(attr, val)
+ } else if (val === undefined) { // If we are getting a value.
+ // Let people know we are reading.
+ Observe.__reading && Observe.__reading(this, attr)
+ return this._get(attr)
+ } else {
+ // Otherwise we are setting.
+ this._set(attr, val);
+ return this;
+ }
+ },
+
+ each: function() {
+ Observe.__reading && Observe.__reading(this, '__keys');
+ return can.each.apply(undefined, [this.__get()].concat(can.makeArray(arguments)))
+ },
+
+ removeAttr: function(attr) {
+ // Info if this is List or not
+ var isList = this instanceof can.Observe.List,
+ // Convert the `attr` into parts (if nested).
+ parts = attrParts(attr),
+ // The actual property to remove.
+ prop = parts.shift(),
+ // The current value.
+ current = isList ? this[prop] : this._data[prop];
+
+ // If we have more parts, call `removeAttr` on that part.
+ if (parts.length) {
+ return current.removeAttr(parts)
+ } else {
+ if (isList) {
+ this.splice(prop, 1)
+ } else if (prop in this._data) {
+ // Otherwise, `delete`.
+ delete this._data[prop];
+ // Create the event.
+ if (!(prop in this.constructor.prototype)) {
+ delete this[prop]
+ }
+ // Let others know the number of keys have changed
+ Observe.triggerBatch(this, "__keys");
+ this._triggerChange(prop, "remove", undefined, current);
+
+ }
+ return current;
+ }
+ },
+ // Reads a property from the `object`.
+ _get: function(attr) {
+ var value = typeof attr === 'string' && !! ~attr.indexOf('.') && this.__get(attr);
+ if (value) {
+ return value;
+ }
+
+ // break up the attr (`"foo.bar"`) into `["foo","bar"]`
+ var parts = attrParts(attr),
+ // get the value of the first attr name (`"foo"`)
+ current = this.__get(parts.shift());
+ // if there are other attributes to read
+ return parts.length ?
+ // and current has a value
+ current ?
+ // lookup the remaining attrs on current
+ current._get(parts) :
+ // or if there's no current, return undefined
+ undefined :
+ // if there are no more parts, return current
+ current;
+ },
+ // Reads a property directly if an `attr` is provided, otherwise
+ // returns the "real" data object itself.
+ __get: function(attr) {
+ return attr ? this._data[attr] : this._data;
+ },
+ // Sets `attr` prop as value on this object where.
+ // `attr` - Is a string of properties or an array of property values.
+ // `value` - The raw value to set.
+ _set: function(attr, value, keepKey) {
+ // Convert `attr` to attr parts (if it isn't already).
+ var parts = attrParts(attr, keepKey),
+ // The immediate prop we are setting.
+ prop = parts.shift(),
+ // The current value.
+ current = this.__get(prop);
+
+ // If we have an `object` and remaining parts.
+ if (canMakeObserve(current) && parts.length) {
+ // That `object` should set it (this might need to call attr).
+ current._set(parts, value)
+ } else if (!parts.length) {
+ // We're in "real" set territory.
+ if (this.__convert) {
+ value = this.__convert(prop, value)
+ }
+ this.__set(prop, value, current)
+ } else {
+ throw "can.Observe: Object does not exist"
+ }
+ },
+ __set: function(prop, value, current) {
+
+ // Otherwise, we are setting it on this `object`.
+ // TODO: Check if value is object and transform
+ // are we changing the value.
+ if (value !== current) {
+ // Check if we are adding this for the first time --
+ // if we are, we need to create an `add` event.
+ var changeType = this.__get().hasOwnProperty(prop) ? "set" : "add";
+
+ // Set the value on data.
+ this.___set(prop,
+
+ // If we are getting an object.
+ canMakeObserve(value) ?
+
+ // Hook it up to send event.
+ hookupBubble(value, prop, this) :
+ // Value is normal.
+ value);
+
+ if (changeType == "add") {
+ // If there is no current value, let others know that
+ // the the number of keys have changed
+
+ Observe.triggerBatch(this, "__keys", undefined);
+
+ }
+ // `batchTrigger` the change event.
+ this._triggerChange(prop, changeType, value, current);
+
+ //Observe.triggerBatch(this, prop, [value, current]);
+ // If we can stop listening to our old value, do it.
+ current && unhookup([current], this._cid);
+ }
+
+ },
+ // Directly sets a property on this `object`.
+ ___set: function(prop, val) {
+ this._data[prop] = val;
+ // Add property directly for easy writing.
+ // Check if its on the `prototype` so we don't overwrite methods like `attrs`.
+ if (!(prop in this.constructor.prototype)) {
+ this[prop] = val
+ }
+ },
+
+
+ bind: can.bindAndSetup,
+
+ unbind: can.unbindAndTeardown,
+
+ serialize: function() {
+ return serialize(this, 'serialize', {});
+ },
+
+ _attrs: function(props, remove) {
+
+ if (props === undefined) {
+ return serialize(this, 'attr', {})
+ }
+
+ props = can.extend({}, props);
+ var prop,
+ self = this,
+ newVal;
+ Observe.startBatch();
+ this.each(function(curVal, prop) {
+ newVal = props[prop];
+
+ // If we are merging...
+ if (newVal === undefined) {
+ remove && self.removeAttr(prop);
+ return;
+ }
+
+ if (self.__convert) {
+ newVal = self.__convert(prop, newVal)
+ }
+
+ // if we're dealing with models, want to call _set to let converter run
+ if (newVal instanceof can.Observe) {
+ self.__set(prop, newVal, curVal)
+ // if its an object, let attr merge
+ } else if (canMakeObserve(curVal) && canMakeObserve(newVal) && curVal.attr) {
+ curVal.attr(newVal, remove)
+ // otherwise just set
+ } else if (curVal != newVal) {
+ self.__set(prop, newVal, curVal)
+ }
+
+ delete props[prop];
+ })
+ // Add remaining props.
+ for (var prop in props) {
+ newVal = props[prop];
+ this._set(prop, newVal, true)
+ }
+ Observe.stopBatch()
+ return this;
+ },
+
+
+ compute: function(prop) {
+ return can.compute(this, prop);
+ }
+ });
+ // Helpers for `observable` lists.
+ var splice = [].splice,
+
+ list = Observe(
+
+ {
+ setup: function(instances, options) {
+ this.length = 0;
+ can.cid(this, ".observe")
+ this._init = 1;
+ if (can.isDeferred(instances)) {
+ this.replace(instances)
+ } else {
+ this.push.apply(this, can.makeArray(instances || []));
+ }
+ // this change needs to be ignored
+ this.bind('change' + this._cid, can.proxy(this._changes, this));
+ can.extend(this, options);
+ delete this._init;
+ },
+ _triggerChange: function(attr, how, newVal, oldVal) {
+
+ Observe.prototype._triggerChange.apply(this, arguments)
+ // `batchTrigger` direct add and remove events...
+ if (!~attr.indexOf('.')) {
+
+ if (how === 'add') {
+ Observe.triggerBatch(this, how, [newVal, +attr]);
+ Observe.triggerBatch(this, 'length', [this.length]);
+ } else if (how === 'remove') {
+ Observe.triggerBatch(this, how, [oldVal, +attr]);
+ Observe.triggerBatch(this, 'length', [this.length]);
+ } else {
+ Observe.triggerBatch(this, how, [newVal, +attr])
+ }
+
+ }
+
+ },
+ __get: function(attr) {
+ return attr ? this[attr] : this;
+ },
+ ___set: function(attr, val) {
+ this[attr] = val;
+ if (+attr >= this.length) {
+ this.length = (+attr + 1)
+ }
+ },
+ _each: function(callback) {
+ var data = this.__get();
+ for (var i = 0; i < data.length; i++) {
+ callback(data[i], i)
+ }
+ },
+ _bindsetup: makeBindSetup("*"),
+ // Returns the serialized form of this list.
+
+ serialize: function() {
+ return serialize(this, 'serialize', []);
+ },
+
+ splice: function(index, howMany) {
+ var args = can.makeArray(arguments),
+ i;
+
+ for (i = 2; i < args.length; i++) {
+ var val = args[i];
+ if (canMakeObserve(val)) {
+ args[i] = hookupBubble(val, "*", this, this.constructor.Observe, this.constructor)
+ }
+ }
+ if (howMany === undefined) {
+ howMany = args[1] = this.length - index;
+ }
+ var removed = splice.apply(this, args);
+ can.Observe.startBatch();
+ if (howMany > 0) {
+ this._triggerChange("" + index, "remove", undefined, removed);
+ unhookup(removed, this._cid);
+ }
+ if (args.length > 2) {
+ this._triggerChange("" + index, "add", args.slice(2), removed);
+ }
+ can.Observe.stopBatch();
+ return removed;
+ },
+
+ _attrs: function(items, remove) {
+ if (items === undefined) {
+ return serialize(this, 'attr', []);
+ }
+
+ // Create a copy.
+ items = can.makeArray(items);
+
+ Observe.startBatch();
+ this._updateAttrs(items, remove);
+ Observe.stopBatch()
+ },
+
+ _updateAttrs: function(items, remove) {
+ var len = Math.min(items.length, this.length);
+
+ for (var prop = 0; prop < len; prop++) {
+ var curVal = this[prop],
+ newVal = items[prop];
+
+ if (canMakeObserve(curVal) && canMakeObserve(newVal)) {
+ curVal.attr(newVal, remove)
+ } else if (curVal != newVal) {
+ this._set(prop, newVal)
+ } else {
+
+ }
+ }
+ if (items.length > this.length) {
+ // Add in the remaining props.
+ this.push.apply(this, items.slice(this.length));
+ } else if (items.length < this.length && remove) {
+ this.splice(items.length)
+ }
+ }
+ }),
+
+ // Converts to an `array` of arguments.
+ getArgs = function(args) {
+ return args[0] && can.isArray(args[0]) ?
+ args[0] :
+ can.makeArray(args);
+ };
+ // Create `push`, `pop`, `shift`, and `unshift`
+ can.each({
+
+ push: "length",
+
+ unshift: 0
+ },
+ // Adds a method
+ // `name` - The method name.
+ // `where` - Where items in the `array` should be added.
+
+ function(where, name) {
+ var orig = [][name]
+ list.prototype[name] = function() {
+ // Get the items being added.
+ var args = [],
+ // Where we are going to add items.
+ len = where ? this.length : 0,
+ i = arguments.length,
+ res,
+ val,
+ constructor = this.constructor;
+
+ // Go through and convert anything to an `observe` that needs to be converted.
+ while (i--) {
+ val = arguments[i];
+ args[i] = canMakeObserve(val) ?
+ hookupBubble(val, "*", this, this.constructor.Observe, this.constructor) :
+ val;
+ }
+
+ // Call the original method.
+ res = orig.apply(this, args);
+
+ if (!this.comparator || args.length) {
+
+ this._triggerChange("" + len, "add", args, undefined);
+ }
+
+ return res;
+ }
+ });
+
+ can.each({
+
+ pop: "length",
+
+ shift: 0
+ },
+ // Creates a `remove` type method
+
+ function(where, name) {
+ list.prototype[name] = function() {
+
+ var args = getArgs(arguments),
+ len = where && this.length ? this.length - 1 : 0;
+
+ var res = [][name].apply(this, args)
+
+ // Create a change where the args are
+ // `len` - Where these items were removed.
+ // `remove` - Items removed.
+ // `undefined` - The new values (there are none).
+ // `res` - The old, removed values (should these be unbound).
+ this._triggerChange("" + len, "remove", undefined, [res])
+
+ if (res && res.unbind) {
+ res.unbind("change" + this._cid)
+ }
+ return res;
+ }
+ });
+
+ can.extend(list.prototype, {
+
+ indexOf: function(item) {
+ this.attr('length')
+ return can.inArray(item, this)
+ },
+
+
+ join: [].join,
+
+
+ reverse: [].reverse,
+
+
+ slice: function() {
+ var temp = Array.prototype.slice.apply(this, arguments);
+ return new this.constructor(temp);
+ },
+
+
+ concat: function() {
+ var args = [];
+ can.each(can.makeArray(arguments), function(arg, i) {
+ args[i] = arg instanceof can.Observe.List ? arg.serialize() : arg;
+ });
+ return new this.constructor(Array.prototype.concat.apply(this.serialize(), args));
+ },
+
+
+ forEach: function(cb, thisarg) {
+ can.each(this, cb, thisarg || this);
+ },
+
+
+ replace: function(newList) {
+ if (can.isDeferred(newList)) {
+ newList.then(can.proxy(this.replace, this));
+ } else {
+ this.splice.apply(this, [0, this.length].concat(can.makeArray(newList || [])));
+ }
+
+ return this;
+ }
+ });
+
+ can.List = Observe.List = list;
+ Observe.setup = function() {
+ can.Construct.setup.apply(this, arguments);
+ // I would prefer not to do it this way. It should
+ // be using the attributes plugin to do this type of conversion.
+ this.List = Observe.List({
+ Observe: this
+ }, {});
+ }
+ return Observe;
+ })(__m3, __m12, __m1);
+
+ // ## observe/compute/compute.js
+ var __m13 = (function(can, bind) {
+
+ // returns the
+ // - observes and attr methods are called by func
+ // - the value returned by func
+ // ex: `{value: 100, observed: [{obs: o, attr: "completed"}]}`
+ var getValueAndObserved = function(func, self) {
+
+ var oldReading;
+ if (can.Observe) {
+ // Set a callback on can.Observe to know
+ // when an attr is read.
+ // Keep a reference to the old reader
+ // if there is one. This is used
+ // for nested live binding.
+ oldReading = can.Observe.__reading;
+ can.Observe.__reading = function(obj, attr) {
+ // Add the observe and attr that was read
+ // to `observed`
+ observed.push({
+ obj: obj,
+ attr: attr + ""
+ });
+ };
+ }
+
+ var observed = [],
+ // Call the "wrapping" function to get the value. `observed`
+ // will have the observe/attribute pairs that were read.
+ value = func.call(self);
+
+ // Set back so we are no longer reading.
+ if (can.Observe) {
+ can.Observe.__reading = oldReading;
+ }
+ return {
+ value: value,
+ observed: observed
+ };
+ },
+ // Calls `callback(newVal, oldVal)` everytime an observed property
+ // called within `getterSetter` is changed and creates a new result of `getterSetter`.
+ // Also returns an object that can teardown all event handlers.
+ computeBinder = function(getterSetter, context, callback, computeState) {
+ // track what we are observing
+ var observing = {},
+ // a flag indicating if this observe/attr pair is already bound
+ matched = true,
+ // the data to return
+ data = {
+ // we will maintain the value while live-binding is taking place
+ value: undefined,
+ // a teardown method that stops listening
+ teardown: function() {
+ for (var name in observing) {
+ var ob = observing[name];
+ ob.observe.obj.unbind(ob.observe.attr, onchanged);
+ delete observing[name];
+ }
+ }
+ },
+ batchNum;
+
+ // when a property value is changed
+ var onchanged = function(ev) {
+ // If the compute is no longer bound (because the same change event led to an unbind)
+ // then do not call getValueAndBind, or we will leak bindings.
+ if (computeState && !computeState.bound) {
+ return;
+ }
+ if (ev.batchNum === undefined || ev.batchNum !== batchNum) {
+ // store the old value
+ var oldValue = data.value,
+ // get the new value
+ newvalue = getValueAndBind();
+
+ // update the value reference (in case someone reads)
+ data.value = newvalue;
+ // if a change happened
+ if (newvalue !== oldValue) {
+ callback(newvalue, oldValue);
+ }
+ batchNum = batchNum = ev.batchNum;
+ }
+
+
+ };
+
+ // gets the value returned by `getterSetter` and also binds to any attributes
+ // read by the call
+ var getValueAndBind = function() {
+ var info = getValueAndObserved(getterSetter, context),
+ newObserveSet = info.observed;
+
+ var value = info.value;
+ matched = !matched;
+
+ // go through every attribute read by this observe
+ can.each(newObserveSet, function(ob) {
+ // if the observe/attribute pair is being observed
+ if (observing[ob.obj._cid + "|" + ob.attr]) {
+ // mark at as observed
+ observing[ob.obj._cid + "|" + ob.attr].matched = matched;
+ } else {
+ // otherwise, set the observe/attribute on oldObserved, marking it as being observed
+ observing[ob.obj._cid + "|" + ob.attr] = {
+ matched: matched,
+ observe: ob
+ };
+ ob.obj.bind(ob.attr, onchanged);
+ }
+ });
+
+ // Iterate through oldObserved, looking for observe/attributes
+ // that are no longer being bound and unbind them
+ for (var name in observing) {
+ var ob = observing[name];
+ if (ob.matched !== matched) {
+ ob.observe.obj.unbind(ob.observe.attr, onchanged);
+ delete observing[name];
+ }
+ }
+ return value;
+ };
+ // set the initial value
+ data.value = getValueAndBind();
+
+ data.isListening = !can.isEmptyObject(observing);
+ return data;
+ }
+
+ // if no one is listening ... we can not calculate every time
+
+ can.compute = function(getterSetter, context, eventName) {
+ if (getterSetter && getterSetter.isComputed) {
+ return getterSetter;
+ }
+ // stores the result of computeBinder
+ var computedData,
+ // how many listeners to this this compute
+ bindings = 0,
+ // the computed object
+ computed,
+ // an object that keeps track if the computed is bound
+ // onchanged needs to know this. It's possible a change happens and results in
+ // something that unbinds the compute, it needs to not to try to recalculate who it
+ // is listening to
+ computeState = {
+ bound: false,
+ // true if this compute is calculated from other computes and observes
+ hasDependencies: false
+ },
+ // The following functions are overwritten depending on how compute() is called
+ // a method to setup listening
+ on = function() {},
+ // a method to teardown listening
+ off = function() {},
+ // the current cached value (only valid if bound = true)
+ value,
+ // how to read the value
+ get = function() {
+ return value
+ },
+ // sets the value
+ set = function(newVal) {
+ value = newVal;
+ },
+ // this compute can be a dependency of other computes
+ canReadForChangeEvent = true;
+
+ computed = function(newVal) {
+ // setting ...
+ if (arguments.length) {
+ // save a reference to the old value
+ var old = value;
+
+ // setter may return a value if
+ // setter is for a value maintained exclusively by this compute
+ var setVal = set.call(context, newVal, old);
+
+ // if this has dependencies return the current value
+ if (computed.hasDependencies) {
+ return get.call(context);
+ }
+
+ if (setVal === undefined) {
+ // it's possible, like with the DOM, setting does not
+ // fire a change event, so we must read
+ value = get.call(context);
+ } else {
+ value = setVal;
+ }
+ // fire the change
+ if (old !== value) {
+ can.Observe.triggerBatch(computed, "change", [value, old]);
+ }
+ return value;
+ } else {
+ // Let others know to listen to changes in this compute
+ if (can.Observe.__reading && canReadForChangeEvent) {
+ can.Observe.__reading(computed, 'change');
+ }
+ // if we are bound, use the cached value
+ if (computeState.bound) {
+ return value;
+ } else {
+ return get.call(context);
+ }
+ }
+ }
+ if (typeof getterSetter === "function") {
+ set = getterSetter;
+ get = getterSetter;
+ canReadForChangeEvent = eventName === false ? false : true;
+ computed.hasDependencies = false;
+ on = function(update) {
+ computedData = computeBinder(getterSetter, context || this, update, computeState);
+ computed.hasDependencies = computedData.isListening
+ value = computedData.value;
+ }
+ off = function() {
+ computedData.teardown();
+ }
+ } else if (context) {
+
+ if (typeof context == "string") {
+ // `can.compute(obj, "propertyName", [eventName])`
+
+ var propertyName = context,
+ isObserve = getterSetter instanceof can.Observe;
+ if (isObserve) {
+ computed.hasDependencies = true;
+ }
+ get = function() {
+ if (isObserve) {
+ return getterSetter.attr(propertyName);
+ } else {
+ return getterSetter[propertyName];
+ }
+ }
+ set = function(newValue) {
+ if (isObserve) {
+ getterSetter.attr(propertyName, newValue)
+ } else {
+ getterSetter[propertyName] = newValue;
+ }
+ }
+ var handler;
+ on = function(update) {
+ handler = function() {
+ update(get(), value)
+ };
+ can.bind.call(getterSetter, eventName || propertyName, handler)
+
+ // use getValueAndObserved because
+ // we should not be indicating that some parent
+ // reads this property if it happens to be binding on it
+ value = getValueAndObserved(get).value
+ }
+ off = function() {
+ can.unbind.call(getterSetter, eventName || propertyName, handler)
+ }
+
+ } else {
+ // `can.compute(initialValue, setter)`
+ if (typeof context === "function") {
+ value = getterSetter;
+ set = context;
+ } else {
+ // `can.compute(initialValue,{get:, set:, on:, off:})`
+ value = getterSetter;
+ var options = context;
+ get = options.get || get;
+ set = options.set || set;
+ on = options.on || on;
+ off = options.off || off;
+ }
+
+ }
+
+
+
+ } else {
+ // `can.compute(5)`
+ value = getterSetter;
+ }
+
+ computed.isComputed = true;
+
+ can.cid(computed, "compute")
+
+ var updater = function(newValue, oldValue) {
+ value = newValue;
+ // might need a way to look up new and oldVal
+ can.Observe.triggerBatch(computed, "change", [newValue, oldValue])
+ }
+
+ return can.extend(computed, {
+ _bindsetup: function() {
+ computeState.bound = true;
+ // setup live-binding
+ on.call(this, updater)
+ },
+ _bindteardown: function() {
+ off.call(this, updater)
+ computeState.bound = false;
+ },
+
+ bind: can.bindAndSetup,
+
+ unbind: can.unbindAndTeardown
+ });
+ };
+ can.compute.binder = computeBinder;
+ return can.compute;
+ })(__m3, __m12);
+
+ // ## model/model.js
+ var __m14 = (function(can) {
+
+ // ## model.js
+ // `can.Model`
+ // _A `can.Observe` that connects to a RESTful interface._
+ // Generic deferred piping function
+
+ var pipe = function(def, model, func) {
+ var d = new can.Deferred();
+ def.then(function() {
+ var args = can.makeArray(arguments);
+ args[0] = model[func](args[0]);
+ d.resolveWith(d, args);
+ }, function() {
+ d.rejectWith(this, arguments);
+ });
+
+ if (typeof def.abort === 'function') {
+ d.abort = function() {
+ return def.abort();
+ }
+ }
+
+ return d;
+ },
+ modelNum = 0,
+ ignoreHookup = /change.observe\d+/,
+ getId = function(inst) {
+ // Instead of using attr, use __get for performance.
+ // Need to set reading
+ can.Observe.__reading && can.Observe.__reading(inst, inst.constructor.id)
+ return inst.__get(inst.constructor.id);
+ },
+ // Ajax `options` generator function
+ ajax = function(ajaxOb, data, type, dataType, success, error) {
+
+ var params = {};
+
+ // If we get a string, handle it.
+ if (typeof ajaxOb == "string") {
+ // If there's a space, it's probably the type.
+ var parts = ajaxOb.split(/\s+/);
+ params.url = parts.pop();
+ if (parts.length) {
+ params.type = parts.pop();
+ }
+ } else {
+ can.extend(params, ajaxOb);
+ }
+
+ // If we are a non-array object, copy to a new attrs.
+ params.data = typeof data == "object" && !can.isArray(data) ?
+ can.extend(params.data || {}, data) : data;
+
+ // Get the url with any templated values filled out.
+ params.url = can.sub(params.url, params.data, true);
+
+ return can.ajax(can.extend({
+ type: type || "post",
+ dataType: dataType || "json",
+ success: success,
+ error: error
+ }, params));
+ },
+ makeRequest = function(self, type, success, error, method) {
+ var args;
+ // if we pass an array as `self` it it means we are coming from
+ // the queued request, and we're passing already serialized data
+ // self's signature will be: [self, serializedData]
+ if (can.isArray(self)) {
+ args = self[1];
+ self = self[0];
+ } else {
+ args = self.serialize();
+ }
+ args = [args];
+ var deferred,
+ // The model.
+ model = self.constructor,
+ jqXHR;
+
+ // `destroy` does not need data.
+ if (type == 'destroy') {
+ args.shift();
+ }
+ // `update` and `destroy` need the `id`.
+ if (type !== 'create') {
+ args.unshift(getId(self));
+ }
+
+
+ jqXHR = model[type].apply(model, args);
+
+ deferred = jqXHR.pipe(function(data) {
+ self[method || type + "d"](data, jqXHR);
+ return self;
+ });
+
+ // Hook up `abort`
+ if (jqXHR.abort) {
+ deferred.abort = function() {
+ jqXHR.abort();
+ };
+ }
+
+ deferred.then(success, error);
+ return deferred;
+ },
+
+ // This object describes how to make an ajax request for each ajax method.
+ // The available properties are:
+ // `url` - The default url to use as indicated as a property on the model.
+ // `type` - The default http request type
+ // `data` - A method that takes the `arguments` and returns `data` used for ajax.
+
+ ajaxMethods = {
+
+ create: {
+ url: "_shortName",
+ type: "post"
+ },
+
+ update: {
+ data: function(id, attrs) {
+ attrs = attrs || {};
+ var identity = this.id;
+ if (attrs[identity] && attrs[identity] !== id) {
+ attrs["new" + can.capitalize(id)] = attrs[identity];
+ delete attrs[identity];
+ }
+ attrs[identity] = id;
+ return attrs;
+ },
+ type: "put"
+ },
+
+ destroy: {
+ type: "delete",
+ data: function(id) {
+ var args = {};
+ args.id = args[this.id] = id;
+ return args;
+ }
+ },
+
+ findAll: {
+ url: "_shortName"
+ },
+
+ findOne: {}
+ },
+ // Makes an ajax request `function` from a string.
+ // `ajaxMethod` - The `ajaxMethod` object defined above.
+ // `str` - The string the user provided. Ex: `findAll: "/recipes.json"`.
+ ajaxMaker = function(ajaxMethod, str) {
+ // Return a `function` that serves as the ajax method.
+ return function(data) {
+ // If the ajax method has it's own way of getting `data`, use that.
+ data = ajaxMethod.data ?
+ ajaxMethod.data.apply(this, arguments) :
+ // Otherwise use the data passed in.
+ data;
+ // Return the ajax method with `data` and the `type` provided.
+ return ajax(str || this[ajaxMethod.url || "_url"], data, ajaxMethod.type || "get")
+ }
+ }
+
+
+
+ can.Model = can.Observe({
+ fullName: "can.Model",
+ _reqs: 0,
+ setup: function(base) {
+ // create store here if someone wants to use model without inheriting from it
+ this.store = {};
+ can.Observe.setup.apply(this, arguments);
+ // Set default list as model list
+ if (!can.Model) {
+ return;
+ }
+ this.List = ML({
+ Observe: this
+ }, {});
+ var self = this,
+ clean = can.proxy(this._clean, self);
+
+
+ // go through ajax methods and set them up
+ can.each(ajaxMethods, function(method, name) {
+ // if an ajax method is not a function, it's either
+ // a string url like findAll: "/recipes" or an
+ // ajax options object like {url: "/recipes"}
+ if (!can.isFunction(self[name])) {
+ // use ajaxMaker to convert that into a function
+ // that returns a deferred with the data
+ self[name] = ajaxMaker(method, self[name]);
+ }
+ // check if there's a make function like makeFindAll
+ // these take deferred function and can do special
+ // behavior with it (like look up data in a store)
+ if (self["make" + can.capitalize(name)]) {
+ // pass the deferred method to the make method to get back
+ // the "findAll" method.
+ var newMethod = self["make" + can.capitalize(name)](self[name]);
+ can.Construct._overwrite(self, base, name, function() {
+ // increment the numer of requests
+ can.Model._reqs++;
+ var def = newMethod.apply(this, arguments);
+ var then = def.then(clean, clean);
+ then.abort = def.abort;
+
+ // attach abort to our then and return it
+ return then;
+ })
+ }
+ });
+
+ if (self.fullName == "can.Model" || !self.fullName) {
+ self.fullName = "Model" + (++modelNum);
+ }
+ // Add ajax converters.
+ can.Model._reqs = 0;
+ this._url = this._shortName + "/{" + this.id + "}"
+ },
+ _ajax: ajaxMaker,
+ _makeRequest: makeRequest,
+ _clean: function() {
+ can.Model._reqs--;
+ if (!can.Model._reqs) {
+ for (var id in this.store) {
+ if (!this.store[id]._bindings) {
+ delete this.store[id];
+ }
+ }
+ }
+ return arguments[0];
+ },
+
+ models: function(instancesRawData, oldList) {
+ // until "end of turn", increment reqs counter so instances will be added to the store
+ can.Model._reqs++;
+ if (!instancesRawData) {
+ return;
+ }
+
+ if (instancesRawData instanceof this.List) {
+ return instancesRawData;
+ }
+
+ // Get the list type.
+ var self = this,
+ tmp = [],
+ res = oldList instanceof can.Observe.List ? oldList : new(self.List || ML),
+ // Did we get an `array`?
+ arr = can.isArray(instancesRawData),
+
+ // Did we get a model list?
+ ml = (instancesRawData instanceof ML),
+
+ // Get the raw `array` of objects.
+ raw = arr ?
+
+ // If an `array`, return the `array`.
+ instancesRawData :
+
+ // Otherwise if a model list.
+ (ml ?
+
+ // Get the raw objects from the list.
+ instancesRawData.serialize() :
+
+ // Get the object's data.
+ instancesRawData.data),
+ i = 0;
+
+
+
+ if (res.length) {
+ res.splice(0);
+ }
+
+ can.each(raw, function(rawPart) {
+ tmp.push(self.model(rawPart));
+ });
+
+ // We only want one change event so push everything at once
+ res.push.apply(res, tmp);
+
+ if (!arr) { // Push other stuff onto `array`.
+ can.each(instancesRawData, function(val, prop) {
+ if (prop !== 'data') {
+ res.attr(prop, val);
+ }
+ })
+ }
+ // at "end of turn", clean up the store
+ setTimeout(can.proxy(this._clean, this), 1);
+ return res;
+ },
+
+ model: function(attributes) {
+ if (!attributes) {
+ return;
+ }
+ if (attributes instanceof this) {
+ attributes = attributes.serialize();
+ }
+ var id = attributes[this.id],
+ model = (id || id === 0) && this.store[id] ?
+ this.store[id].attr(attributes, this.removeAttr || false) : new this(attributes);
+ if (can.Model._reqs) {
+ this.store[attributes[this.id]] = model;
+ }
+ return model;
+ }
+ },
+
+
+ {
+
+ isNew: function() {
+ var id = getId(this);
+ return !(id || id === 0); // If `null` or `undefined`
+ },
+
+ save: function(success, error) {
+ return makeRequest(this, this.isNew() ? 'create' : 'update', success, error);
+ },
+
+ destroy: function(success, error) {
+ if (this.isNew()) {
+ var self = this;
+ var def = can.Deferred();
+ def.then(success, error);
+ return def.done(function(data) {
+ self.destroyed(data)
+ }).resolve(self);
+ }
+ return makeRequest(this, 'destroy', success, error, 'destroyed');
+ },
+
+ _bindsetup: function() {
+ this.constructor.store[this.__get(this.constructor.id)] = this;
+ return can.Observe.prototype._bindsetup.apply(this, arguments);
+ },
+
+ _bindteardown: function() {
+ delete this.constructor.store[getId(this)];
+ return can.Observe.prototype._bindteardown.apply(this, arguments)
+ },
+ // Change `id`.
+ ___set: function(prop, val) {
+ can.Observe.prototype.___set.call(this, prop, val)
+ // If we add an `id`, move it to the store.
+ if (prop === this.constructor.id && this._bindings) {
+ this.constructor.store[getId(this)] = this;
+ }
+ }
+ });
+
+ can.each({
+ makeFindAll: "models",
+ makeFindOne: "model",
+ makeCreate: "model",
+ makeUpdate: "model"
+ }, function(method, name) {
+ can.Model[name] = function(oldMethod) {
+ return function() {
+ var args = can.makeArray(arguments),
+ oldArgs = can.isFunction(args[1]) ? args.splice(0, 1) : args.splice(0, 2),
+ def = pipe(oldMethod.apply(this, oldArgs), this, method);
+ def.then(args[0], args[1]);
+ // return the original promise
+ return def;
+ };
+ };
+ });
+
+ can.each([
+
+ "created",
+
+ "updated",
+
+ "destroyed"
+ ], function(funcName) {
+ can.Model.prototype[funcName] = function(attrs) {
+ var stub,
+ constructor = this.constructor;
+
+ // Update attributes if attributes have been passed
+ stub = attrs && typeof attrs == 'object' && this.attr(attrs.attr ? attrs.attr() : attrs);
+
+ // triggers change event that bubble's like
+ // handler( 'change','1.destroyed' ). This is used
+ // to remove items on destroyed from Model Lists.
+ // but there should be a better way.
+ can.trigger(this, "change", funcName)
+
+
+ // Call event on the instance's Class
+ can.trigger(constructor, funcName, this);
+ };
+ });
+
+ // Model lists are just like `Observe.List` except that when their items are
+ // destroyed, it automatically gets removed from the list.
+
+ var ML = can.Model.List = can.Observe.List({
+ setup: function(params) {
+ if (can.isPlainObject(params) && !can.isArray(params)) {
+ can.Observe.List.prototype.setup.apply(this);
+ this.replace(this.constructor.Observe.findAll(params))
+ } else {
+ can.Observe.List.prototype.setup.apply(this, arguments);
+ }
+ },
+ _changes: function(ev, attr) {
+ can.Observe.List.prototype._changes.apply(this, arguments);
+ if (/\w+\.destroyed/.test(attr)) {
+ var index = this.indexOf(ev.target);
+ if (index != -1) {
+ this.splice(index, 1);
+ }
+ }
+ }
+ })
+
+ return can.Model;
+ })(__m3, __m11);
+
+ // ## view/view.js
+ var __m15 = (function(can) {
+ // ## view.js
+ // `can.view`
+ // _Templating abstraction._
+
+ var isFunction = can.isFunction,
+ makeArray = can.makeArray,
+ // Used for hookup `id`s.
+ hookupId = 1,
+
+ $view = can.view = can.template = function(view, data, helpers, callback) {
+ // If helpers is a `function`, it is actually a callback.
+ if (isFunction(helpers)) {
+ callback = helpers;
+ helpers = undefined;
+ }
+
+ var pipe = function(result) {
+ return $view.frag(result);
+ },
+ // In case we got a callback, we need to convert the can.view.render
+ // result to a document fragment
+ wrapCallback = isFunction(callback) ? function(frag) {
+ callback(pipe(frag));
+ } : null,
+ // Get the result.
+ result = $view.render(view, data, helpers, wrapCallback),
+ deferred = can.Deferred();
+
+ if (isFunction(result)) {
+ return result;
+ }
+
+ if (can.isDeferred(result)) {
+ result.then(function(result, data) {
+ deferred.resolve.call(deferred, pipe(result), data);
+ }, function() {
+ deferred.fail.apply(deferred, arguments);
+ });
+ return deferred;
+ }
+
+ // Convert it into a dom frag.
+ return pipe(result);
+ };
+
+ can.extend($view, {
+ // creates a frag and hooks it up all at once
+ frag: function(result, parentNode) {
+ return $view.hookup($view.fragment(result), parentNode);
+ },
+
+ // simply creates a frag
+ // this is used internally to create a frag
+ // insert it
+ // then hook it up
+ fragment: function(result) {
+ var frag = can.buildFragment(result, document.body);
+ // If we have an empty frag...
+ if (!frag.childNodes.length) {
+ frag.appendChild(document.createTextNode(''));
+ }
+ return frag;
+ },
+
+ // Convert a path like string into something that's ok for an `element` ID.
+ toId: function(src) {
+ return can.map(src.toString().split(/\/|\./g), function(part) {
+ // Dont include empty strings in toId functions
+ if (part) {
+ return part;
+ }
+ }).join("_");
+ },
+
+ hookup: function(fragment, parentNode) {
+ var hookupEls = [],
+ id,
+ func;
+
+ // Get all `childNodes`.
+ can.each(fragment.childNodes ? can.makeArray(fragment.childNodes) : fragment, function(node) {
+ if (node.nodeType === 1) {
+ hookupEls.push(node);
+ hookupEls.push.apply(hookupEls, can.makeArray(node.getElementsByTagName('*')));
+ }
+ });
+
+ // Filter by `data-view-id` attribute.
+ can.each(hookupEls, function(el) {
+ if (el.getAttribute && (id = el.getAttribute('data-view-id')) && (func = $view.hookups[id])) {
+ func(el, parentNode, id);
+ delete $view.hookups[id];
+ el.removeAttribute('data-view-id');
+ }
+ });
+
+ return fragment;
+ },
+
+
+ hookups: {},
+
+
+ hook: function(cb) {
+ $view.hookups[++hookupId] = cb;
+ return " data-view-id='" + hookupId + "'";
+ },
+
+
+ cached: {},
+
+ cachedRenderers: {},
+
+
+ cache: true,
+
+
+ register: function(info) {
+ this.types["." + info.suffix] = info;
+ },
+
+ types: {},
+
+
+ ext: ".ejs",
+
+
+ registerScript: function() {},
+
+
+ preload: function() {},
+
+
+ render: function(view, data, helpers, callback) {
+ // If helpers is a `function`, it is actually a callback.
+ if (isFunction(helpers)) {
+ callback = helpers;
+ helpers = undefined;
+ }
+
+ // See if we got passed any deferreds.
+ var deferreds = getDeferreds(data);
+
+ if (deferreds.length) { // Does data contain any deferreds?
+ // The deferred that resolves into the rendered content...
+ var deferred = new can.Deferred(),
+ dataCopy = can.extend({}, data);
+
+ // Add the view request to the list of deferreds.
+ deferreds.push(get(view, true))
+
+ // Wait for the view and all deferreds to finish...
+ can.when.apply(can, deferreds).then(function(resolved) {
+ // Get all the resolved deferreds.
+ var objs = makeArray(arguments),
+ // Renderer is the last index of the data.
+ renderer = objs.pop(),
+ // The result of the template rendering with data.
+ result;
+
+ // Make data look like the resolved deferreds.
+ if (can.isDeferred(data)) {
+ dataCopy = usefulPart(resolved);
+ } else {
+ // Go through each prop in data again and
+ // replace the defferreds with what they resolved to.
+ for (var prop in data) {
+ if (can.isDeferred(data[prop])) {
+ dataCopy[prop] = usefulPart(objs.shift());
+ }
+ }
+ }
+
+ // Get the rendered result.
+ result = renderer(dataCopy, helpers);
+
+ // Resolve with the rendered view.
+ deferred.resolve(result, dataCopy);
+
+ // If there's a `callback`, call it back with the result.
+ callback && callback(result, dataCopy);
+ }, function() {
+ deferred.reject.apply(deferred, arguments)
+ });
+ // Return the deferred...
+ return deferred;
+ } else {
+ // No deferreds! Render this bad boy.
+ var response,
+ // If there's a `callback` function
+ async = isFunction(callback),
+ // Get the `view` type
+ deferred = get(view, async);
+
+ // If we are `async`...
+ if (async) {
+ // Return the deferred
+ response = deferred;
+ // And fire callback with the rendered result.
+ deferred.then(function(renderer) {
+ callback(data ? renderer(data, helpers) : renderer);
+ })
+ } else {
+ // if the deferred is resolved, call the cached renderer instead
+ // this is because it's possible, with recursive deferreds to
+ // need to render a view while its deferred is _resolving_. A _resolving_ deferred
+ // is a deferred that was just resolved and is calling back it's success callbacks.
+ // If a new success handler is called while resoliving, it does not get fired by
+ // jQuery's deferred system. So instead of adding a new callback
+ // we use the cached renderer.
+ // We also add __view_id on the deferred so we can look up it's cached renderer.
+ // In the future, we might simply store either a deferred or the cached result.
+ if (deferred.state() === "resolved" && deferred.__view_id) {
+ var currentRenderer = $view.cachedRenderers[deferred.__view_id];
+ return data ? currentRenderer(data, helpers) : currentRenderer;
+ } else {
+ // Otherwise, the deferred is complete, so
+ // set response to the result of the rendering.
+ deferred.then(function(renderer) {
+ response = data ? renderer(data, helpers) : renderer;
+ });
+ }
+ }
+
+ return response;
+ }
+ },
+
+
+ registerView: function(id, text, type, def) {
+ // Get the renderer function.
+ var func = (type || $view.types[$view.ext]).renderer(id, text);
+ def = def || new can.Deferred();
+
+ // Cache if we are caching.
+ if ($view.cache) {
+ $view.cached[id] = def;
+ def.__view_id = id;
+ $view.cachedRenderers[id] = func;
+ }
+
+ // Return the objects for the response's `dataTypes`
+ // (in this case view).
+ return def.resolve(func);
+ }
+ });
+
+ // Makes sure there's a template, if not, have `steal` provide a warning.
+ var checkText = function(text, url) {
+ if (!text.length) {
+
+ throw "can.view: No template or empty template:" + url;
+ }
+ },
+ // `Returns a `view` renderer deferred.
+ // `url` - The url to the template.
+ // `async` - If the ajax request should be asynchronous.
+ // Returns a deferred.
+ get = function(url, async) {
+ var suffix = url.match(/\.[\w\d]+$/),
+ type,
+ // If we are reading a script element for the content of the template,
+ // `el` will be set to that script element.
+ el,
+ // A unique identifier for the view (used for caching).
+ // This is typically derived from the element id or
+ // the url for the template.
+ id,
+ // The ajax request used to retrieve the template content.
+ jqXHR;
+
+ //If the url has a #, we assume we want to use an inline template
+ //from a script element and not current page's HTML
+ if (url.match(/^#/)) {
+ url = url.substr(1);
+ }
+ // If we have an inline template, derive the suffix from the `text/???` part.
+ // This only supports `<script>` tags.
+ if (el = document.getElementById(url)) {
+ suffix = "." + el.type.match(/\/(x\-)?(.+)/)[2];
+ }
+
+ // If there is no suffix, add one.
+ if (!suffix && !$view.cached[url]) {
+ url += (suffix = $view.ext);
+ }
+
+ if (can.isArray(suffix)) {
+ suffix = suffix[0]
+ }
+
+ // Convert to a unique and valid id.
+ id = $view.toId(url);
+
+ // If an absolute path, use `steal` to get it.
+ // You should only be using `//` if you are using `steal`.
+ if (url.match(/^\/\//)) {
+ var sub = url.substr(2);
+ url = !window.steal ?
+ sub :
+ steal.config().root.mapJoin("" + steal.id(sub));
+ }
+
+ // Set the template engine type.
+ type = $view.types[suffix];
+
+ // If it is cached,
+ if ($view.cached[id]) {
+ // Return the cached deferred renderer.
+ return $view.cached[id];
+
+ // Otherwise if we are getting this from a `<script>` element.
+ } else if (el) {
+ // Resolve immediately with the element's `innerHTML`.
+ return $view.registerView(id, el.innerHTML, type);
+ } else {
+ // Make an ajax request for text.
+ var d = new can.Deferred();
+ can.ajax({
+ async: async,
+ url: url,
+ dataType: "text",
+ error: function(jqXHR) {
+ checkText("", url);
+ d.reject(jqXHR);
+ },
+ success: function(text) {
+ // Make sure we got some text back.
+ checkText(text, url);
+ $view.registerView(id, text, type, d)
+ }
+ });
+ return d;
+ }
+ },
+ // Gets an `array` of deferreds from an `object`.
+ // This only goes one level deep.
+ getDeferreds = function(data) {
+ var deferreds = [];
+
+ // pull out deferreds
+ if (can.isDeferred(data)) {
+ return [data]
+ } else {
+ for (var prop in data) {
+ if (can.isDeferred(data[prop])) {
+ deferreds.push(data[prop]);
+ }
+ }
+ }
+ return deferreds;
+ },
+ // Gets the useful part of a resolved deferred.
+ // This is for `model`s and `can.ajax` that resolve to an `array`.
+ usefulPart = function(resolved) {
+ return can.isArray(resolved) && resolved[1] === 'success' ? resolved[0] : resolved
+ };
+
+ //!steal-pluginify-remove-start
+ if (window.steal) {
+ steal.type("view js", function(options, success, error) {
+ var type = $view.types["." + options.type],
+ id = $view.toId(options.id);
+
+ options.text = "steal('" + (type.plugin || "can/view/" + options.type) + "',function(can){return " + "can.view.preload('" + id + "'," + options.text + ");\n})";
+ success();
+ })
+ }
+ //!steal-pluginify-remove-end
+
+ can.extend($view, {
+ register: function(info) {
+ this.types["." + info.suffix] = info;
+
+ //!steal-pluginify-remove-start
+ if (window.steal) {
+ steal.type(info.suffix + " view js", function(options, success, error) {
+ var type = $view.types["." + options.type],
+ id = $view.toId(options.id + '');
+
+ options.text = type.script(id, options.text)
+ success();
+ })
+ };
+ //!steal-pluginify-remove-end
+
+ $view[info.suffix] = function(id, text) {
+ if (!text) {
+ // Return a nameless renderer
+ var renderer = function() {
+ return $view.frag(renderer.render.apply(this, arguments));
+ }
+ renderer.render = function() {
+ var renderer = info.renderer(null, id);
+ return renderer.apply(renderer, arguments);
+ }
+ return renderer;
+ }
+
+ $view.preload(id, info.renderer(id, text));
+ return can.view(id);
+ }
+ },
+ registerScript: function(type, id, src) {
+ return "can.view.preload('" + id + "'," + $view.types["." + type].script(id, src) + ");";
+ },
+ preload: function(id, renderer) {
+ $view.cached[id] = new can.Deferred().resolve(function(data, helpers) {
+ return renderer.call(data, data, helpers);
+ });
+
+ function frag() {
+ return $view.frag(renderer.apply(this, arguments));
+ }
+ // expose the renderer for mustache
+ frag.render = renderer;
+ return frag;
+ }
+
+ });
+
+ return can;
+ })(__m3);
+
+ // ## view/elements.js
+ var __m18 = (function() {
+
+ var elements = {
+ tagToContentPropMap: {
+ option: "textContent" in document.createElement("option") ? "textContent" : "innerText",
+ textarea: "value"
+ },
+
+ attrMap: {
+ "class": "className",
+ "value": "value",
+ "innerText": "innerText",
+ "textContent": "textContent",
+ "checked": true,
+ "disabled": true,
+ "readonly": true,
+ "required": true
+ },
+ // elements whos default value we should set
+ defaultValue: ["input", "textarea"],
+ // a map of parent element to child elements
+ tagMap: {
+ "": "span",
+ table: "tbody",
+ tr: "td",
+ ol: "li",
+ ul: "li",
+ tbody: "tr",
+ thead: "tr",
+ tfoot: "tr",
+ select: "option",
+ optgroup: "option"
+ },
+ // a tag's parent element
+ reverseTagMap: {
+ tr: "tbody",
+ option: "select",
+ td: "tr",
+ th: "tr",
+ li: "ul"
+ },
+
+ getParentNode: function(el, defaultParentNode) {
+ return defaultParentNode && el.parentNode.nodeType === 11 ? defaultParentNode : el.parentNode;
+ },
+ // set an attribute on an element
+ setAttr: function(el, attrName, val) {
+ var tagName = el.nodeName.toString().toLowerCase(),
+ prop = elements.attrMap[attrName];
+ // if this is a special property
+ if (prop === true) {
+ el[attrName] = true;
+ } else if (prop) {
+ // set the value as true / false
+ el[prop] = val;
+ if (prop === "value" && can.inArray(tagName, elements.defaultValue) >= 0) {
+ el.defaultValue = val;
+ }
+ } else {
+ el.setAttribute(attrName, val);
+ }
+ },
+ // gets the value of an attribute
+ getAttr: function(el, attrName) {
+ // Default to a blank string for IE7/8
+ return (elements.attrMap[attrName] && el[elements.attrMap[attrName]] ?
+ el[elements.attrMap[attrName]] :
+ el.getAttribute(attrName)) || '';
+ },
+ // removes the attribute
+ removeAttr: function(el, attrName) {
+ if (elements.attrMap[attrName] === true) {
+ el[attrName] = false;
+ } else {
+ el.removeAttribute(attrName);
+ }
+ },
+ contentText: function(text) {
+ if (typeof text == 'string') {
+ return text;
+ }
+ // If has no value, return an empty string.
+ if (!text && text !== 0) {
+ return '';
+ }
+ return "" + text;
+ }
+ };
+
+ return elements;
+ })();
+
+ // ## view/scanner.js
+ var __m17 = (function(can, elements) {
+
+ var newLine = /(\r|\n)+/g,
+ // Escapes characters starting with `\`.
+ clean = function(content) {
+ return content
+ .split('\\').join("\\\\")
+ .split("\n").join("\\n")
+ .split('"').join('\\"')
+ .split("\t").join("\\t");
+ },
+ // Returns a tagName to use as a temporary placeholder for live content
+ // looks forward ... could be slow, but we only do it when necessary
+ getTag = function(tagName, tokens, i) {
+ // if a tagName is provided, use that
+ if (tagName) {
+ return tagName;
+ } else {
+ // otherwise go searching for the next two tokens like "<",TAG
+ while (i < tokens.length) {
+ if (tokens[i] == "<" && elements.reverseTagMap[tokens[i + 1]]) {
+ return elements.reverseTagMap[tokens[i + 1]];
+ }
+ i++;
+ }
+ }
+ return '';
+ },
+ bracketNum = function(content) {
+ return (--content.split("{").length) - (--content.split("}").length);
+ },
+ myEval = function(script) {
+ eval(script);
+ },
+ attrReg = /([^\s]+)[\s]*=[\s]*$/,
+ // Commands for caching.
+ startTxt = 'var ___v1ew = [];',
+ finishTxt = "return ___v1ew.join('')",
+ put_cmd = "___v1ew.push(",
+ insert_cmd = put_cmd,
+ // Global controls (used by other functions to know where we are).
+ // Are we inside a tag?
+ htmlTag = null,
+ // Are we within a quote within a tag?
+ quote = null,
+ // What was the text before the current quote? (used to get the `attr` name)
+ beforeQuote = null,
+ // Whether a rescan is in progress
+ rescan = null,
+ // Used to mark where the element is.
+ status = function() {
+ // `t` - `1`.
+ // `h` - `0`.
+ // `q` - String `beforeQuote`.
+ return quote ? "'" + beforeQuote.match(attrReg)[1] + "'" : (htmlTag ? 1 : 0);
+ };
+
+ can.view.Scanner = Scanner = function(options) {
+ // Set options on self
+ can.extend(this, {
+ text: {},
+ tokens: []
+ }, options);
+
+ // Cache a token lookup
+ this.tokenReg = [];
+ this.tokenSimple = {
+ "<": "<",
+ ">": ">",
+ '"': '"',
+ "'": "'"
+ };
+ this.tokenComplex = [];
+ this.tokenMap = {};
+ for (var i = 0, token; token = this.tokens[i]; i++) {
+
+
+ // Save complex mappings (custom regexp)
+ if (token[2]) {
+ this.tokenReg.push(token[2]);
+ this.tokenComplex.push({
+ abbr: token[1],
+ re: new RegExp(token[2]),
+ rescan: token[3]
+ });
+ }
+ // Save simple mappings (string only, no regexp)
+ else {
+ this.tokenReg.push(token[1]);
+ this.tokenSimple[token[1]] = token[0];
+ }
+ this.tokenMap[token[0]] = token[1];
+ }
+
+ // Cache the token registry.
+ this.tokenReg = new RegExp("(" + this.tokenReg.slice(0).concat(["<", ">", '"', "'"]).join("|") + ")", "g");
+ };
+
+ Scanner.prototype = {
+
+ helpers: [
+
+ {
+ name: /\s*\(([\$\w]+)\)\s*->([^\n]*)/,
+ fn: function(content) {
+ var quickFunc = /\s*\(([\$\w]+)\)\s*->([^\n]*)/,
+ parts = content.match(quickFunc);
+
+ return "can.proxy(function(__){var " + parts[1] + "=can.$(__);" + parts[2] + "}, this);";
+ }
+ }
+ ],
+
+ scan: function(source, name) {
+ var tokens = [],
+ last = 0,
+ simple = this.tokenSimple,
+ complex = this.tokenComplex;
+
+ source = source.replace(newLine, "\n");
+ if (this.transform) {
+ source = this.transform(source);
+ }
+ source.replace(this.tokenReg, function(whole, part) {
+ // offset is the second to last argument
+ var offset = arguments[arguments.length - 2];
+
+ // if the next token starts after the last token ends
+ // push what's in between
+ if (offset > last) {
+ tokens.push(source.substring(last, offset));
+ }
+
+ // push the simple token (if there is one)
+ if (simple[whole]) {
+ tokens.push(whole);
+ }
+ // otherwise lookup complex tokens
+ else {
+ for (var i = 0, token; token = complex[i]; i++) {
+ if (token.re.test(whole)) {
+ tokens.push(token.abbr);
+ // Push a rescan function if one exists
+ if (token.rescan) {
+ tokens.push(token.rescan(part));
+ }
+ break;
+ }
+ }
+ }
+
+ // update the position of the last part of the last token
+ last = offset + part.length;
+ });
+
+ // if there's something at the end, add it
+ if (last < source.length) {
+ tokens.push(source.substr(last));
+ }
+
+ var content = '',
+ buff = [startTxt + (this.text.start || '')],
+ // Helper `function` for putting stuff in the view concat.
+ put = function(content, bonus) {
+ buff.push(put_cmd, '"', clean(content), '"' + (bonus || '') + ');');
+ },
+ // A stack used to keep track of how we should end a bracket
+ // `}`.
+ // Once we have a `<%= %>` with a `leftBracket`,
+ // we store how the file should end here (either `))` or `;`).
+ endStack = [],
+ // The last token, used to remember which tag we are in.
+ lastToken,
+ // The corresponding magic tag.
+ startTag = null,
+ // Was there a magic tag inside an html tag?
+ magicInTag = false,
+ // The current tag name.
+ tagName = '',
+ // stack of tagNames
+ tagNames = [],
+ // Pop from tagNames?
+ popTagName = false,
+ // Declared here.
+ bracketCount,
+ i = 0,
+ token,
+ tmap = this.tokenMap;
+
+ // Reinitialize the tag state goodness.
+ htmlTag = quote = beforeQuote = null;
+
+ for (;
+ (token = tokens[i++]) !== undefined;) {
+ if (startTag === null) {
+ switch (token) {
+ case tmap.left:
+ case tmap.escapeLeft:
+ case tmap.returnLeft:
+ magicInTag = htmlTag && 1;
+ case tmap.commentLeft:
+ // A new line -- just add whatever content within a clean.
+ // Reset everything.
+ startTag = token;
+ if (content.length) {
+ put(content);
+ }
+ content = '';
+ break;
+ case tmap.escapeFull:
+ // This is a full line escape (a line that contains only whitespace and escaped logic)
+ // Break it up into escape left and right
+ magicInTag = htmlTag && 1;
+ rescan = 1;
+ startTag = tmap.escapeLeft;
+ if (content.length) {
+ put(content);
+ }
+ rescan = tokens[i++];
+ content = rescan.content || rescan;
+ if (rescan.before) {
+ put(rescan.before);
+ }
+ tokens.splice(i, 0, tmap.right);
+ break;
+ case tmap.commentFull:
+ // Ignore full line comments.
+ break;
+ case tmap.templateLeft:
+ content += tmap.left;
+ break;
+ case '<':
+ // Make sure we are not in a comment.
+ if (tokens[i].indexOf("!--") !== 0) {
+ htmlTag = 1;
+ magicInTag = 0;
+ }
+ content += token;
+ break;
+ case '>':
+ htmlTag = 0;
+ // content.substr(-1) doesn't work in IE7/8
+ var emptyElement = content.substr(content.length - 1) == "/" || content.substr(content.length - 2) == "--";
+ // if there was a magic tag
+ // or it's an element that has text content between its tags,
+ // but content is not other tags add a hookup
+ // TODO: we should only add `can.EJS.pending()` if there's a magic tag
+ // within the html tags.
+ if (magicInTag || !popTagName && elements.tagToContentPropMap[tagNames[tagNames.length - 1]]) {
+ // make sure / of /> is on the left of pending
+ if (emptyElement) {
+ put(content.substr(0, content.length - 1), ",can.view.pending(),\"/>\"");
+ } else {
+ put(content, ",can.view.pending(),\">\"");
+ }
+ content = '';
+ magicInTag = 0;
+ } else {
+ content += token;
+ }
+ // if it's a tag like <input/>
+ if (emptyElement || popTagName) {
+ // remove the current tag in the stack
+ tagNames.pop();
+ // set the current tag to the previous parent
+ tagName = tagNames[tagNames.length - 1];
+ // Don't pop next time
+ popTagName = false;
+ }
+ break;
+ case "'":
+ case '"':
+ // If we are in an html tag, finding matching quotes.
+ if (htmlTag) {
+ // We have a quote and it matches.
+ if (quote && quote === token) {
+ // We are exiting the quote.
+ quote = null;
+ // Otherwise we are creating a quote.
+ // TODO: does this handle `\`?
+ } else if (quote === null) {
+ quote = token;
+ beforeQuote = lastToken;
+ }
+ }
+ default:
+ // Track the current tag
+ if (lastToken === '<') {
+ tagName = token.split(/\s/)[0];
+ if (tagName.indexOf("/") === 0 && tagNames[tagNames.length - 1] === tagName.substr(1)) {
+ // set tagName to the last tagName
+ // if there are no more tagNames, we'll rely on getTag.
+ tagName = tagNames[tagNames.length - 1];
+ popTagName = true;
+ } else {
+ tagNames.push(tagName);
+ }
+ }
+ content += token;
+ break;
+ }
+ } else {
+ // We have a start tag.
+ switch (token) {
+ case tmap.right:
+ case tmap.returnRight:
+ switch (startTag) {
+ case tmap.left:
+ // Get the number of `{ minus }`
+ bracketCount = bracketNum(content);
+
+ // We are ending a block.
+ if (bracketCount == 1) {
+
+ // We are starting on.
+ buff.push(insert_cmd, "can.view.txt(0,'" + getTag(tagName, tokens, i) + "'," + status() + ",this,function(){", startTxt, content);
+
+ endStack.push({
+ before: "",
+ after: finishTxt + "}));\n"
+ });
+ } else {
+
+ // How are we ending this statement?
+ last = // If the stack has value and we are ending a block...
+ endStack.length && bracketCount == -1 ? // Use the last item in the block stack.
+ endStack.pop() : // Or use the default ending.
+ {
+ after: ";"
+ };
+
+ // If we are ending a returning block,
+ // add the finish text which returns the result of the
+ // block.
+ if (last.before) {
+ buff.push(last.before);
+ }
+ // Add the remaining content.
+ buff.push(content, ";", last.after);
+ }
+ break;
+ case tmap.escapeLeft:
+ case tmap.returnLeft:
+ // We have an extra `{` -> `block`.
+ // Get the number of `{ minus }`.
+ bracketCount = bracketNum(content);
+ // If we have more `{`, it means there is a block.
+ if (bracketCount) {
+ // When we return to the same # of `{` vs `}` end with a `doubleParent`.
+ endStack.push({
+ before: finishTxt,
+ after: "}));"
+ });
+ }
+
+ var escaped = startTag === tmap.escapeLeft ? 1 : 0,
+ commands = {
+ insert: insert_cmd,
+ tagName: getTag(tagName, tokens, i),
+ status: status()
+ };
+
+ for (var ii = 0; ii < this.helpers.length; ii++) {
+ // Match the helper based on helper
+ // regex name value
+ var helper = this.helpers[ii];
+ if (helper.name.test(content)) {
+ content = helper.fn(content, commands);
+
+ // dont escape partials
+ if (helper.name.source == /^>[\s]*\w*/.source) {
+ escaped = 0;
+ }
+ break;
+ }
+ }
+
+ // Handle special cases
+ if (typeof content == 'object') {
+ if (content.raw) {
+ buff.push(content.raw);
+ }
+ } else {
+ // If we have `<%== a(function(){ %>` then we want
+ // `can.EJS.text(0,this, function(){ return a(function(){ var _v1ew = [];`.
+ buff.push(insert_cmd, "can.view.txt(" + escaped + ",'" + tagName + "'," + status() + ",this,function(){ " + (this.text.escape || '') + "return ", content,
+ // If we have a block.
+ bracketCount ?
+ // Start with startTxt `"var _v1ew = [];"`.
+ startTxt :
+ // If not, add `doubleParent` to close push and text.
+ "}));");
+ }
+
+ if (rescan && rescan.after && rescan.after.length) {
+ put(rescan.after.length);
+ rescan = null;
+ }
+ break;
+ }
+ startTag = null;
+ content = '';
+ break;
+ case tmap.templateLeft:
+ content += tmap.left;
+ break;
+ default:
+ content += token;
+ break;
+ }
+ }
+ lastToken = token;
+ }
+
+ // Put it together...
+ if (content.length) {
+ // Should be `content.dump` in Ruby.
+ put(content);
+ }
+ buff.push(";");
+
+ var template = buff.join(''),
+ out = {
+ out: 'with(_VIEW) { with (_CONTEXT) {' + template + " " + finishTxt + "}}"
+ };
+ // Use `eval` instead of creating a function, because it is easier to debug.
+ myEval.call(out, 'this.fn = (function(_CONTEXT,_VIEW){' + out.out + '});\r\n//@ sourceURL=' + name + ".js");
+
+ return out;
+ }
+ };
+
+ return Scanner;
+ })(__m15, __m18);
+
+ // ## view/node_lists.js
+ var __m21 = (function(can) {
+
+ // text node expando test
+ var canExpando = true;
+ try {
+ document.createTextNode('')._ = 0;
+ } catch (ex) {
+ canExpando = false;
+ }
+
+ // a mapping of element ids to nodeList ids
+ var nodeMap = {},
+ // a mapping of ids to text nodes
+ textNodeMap = {},
+ // a mapping of nodeList ids to nodeList
+ nodeListMap = {},
+ expando = "ejs_" + Math.random(),
+ _id = 0,
+ id = function(node) {
+ if (canExpando || node.nodeType !== 3) {
+ if (node[expando]) {
+ return node[expando];
+ } else {
+ return node[expando] = (node.nodeName ? "element_" : "obj_") + (++_id);
+ }
+ } else {
+ for (var textNodeID in textNodeMap) {
+ if (textNodeMap[textNodeID] === node) {
+ return textNodeID;
+ }
+ }
+
+ textNodeMap["text_" + (++_id)] = node;
+ return "text_" + _id;
+ }
+ },
+ // removes a nodeListId from a node's nodeListIds
+ removeNodeListId = function(node, nodeListId) {
+ var nodeListIds = nodeMap[id(node)];
+ if (nodeListIds) {
+ var index = can.inArray(nodeListId, nodeListIds);
+
+ if (index >= 0) {
+ nodeListIds.splice(index, 1);
+ }
+ if (!nodeListIds.length) {
+ delete nodeMap[id(node)];
+ }
+ }
+ },
+ addNodeListId = function(node, nodeListId) {
+ var nodeListIds = nodeMap[id(node)];
+ if (!nodeListIds) {
+ nodeListIds = nodeMap[id(node)] = [];
+ }
+ nodeListIds.push(nodeListId);
+ };
+
+ var nodeLists = {
+ id: id,
+ // replaces the contents of one node list with the nodes in another list
+ replace: function(oldNodeList, newNodes) {
+ // for each node in the node list
+ oldNodeList = can.makeArray(oldNodeList);
+
+ // try every set
+ //can.each( oldNodeList, function(node){
+ var node = oldNodeList[0]
+ // for each nodeList the node is in
+ can.each(can.makeArray(nodeMap[id(node)]), function(nodeListId) {
+
+ // if startNode to endNode is
+ // within list, replace that list
+ // I think the problem is not the WHOLE part is being
+ // matched
+ var nodeList = nodeListMap[nodeListId],
+ startIndex = can.inArray(node, nodeList),
+ endIndex = can.inArray(oldNodeList[oldNodeList.length - 1], nodeList);
+
+
+ // remove this nodeListId from each node
+ if (startIndex >= 0 && endIndex >= 0) {
+ for (var i = startIndex; i <= endIndex; i++) {
+ var n = nodeList[i];
+ removeNodeListId(n, nodeListId);
+ }
+ // swap in new nodes into the nodeLIst
+ nodeList.splice.apply(nodeList, [startIndex, endIndex - startIndex + 1].concat(newNodes));
+
+ // tell these new nodes they belong to the nodeList
+ can.each(newNodes, function(node) {
+ addNodeListId(node, nodeListId);
+ });
+ } else {
+ nodeLists.unregister(nodeList);
+ }
+ });
+ //});
+ },
+ // registers a list of nodes
+ register: function(nodeList) {
+ var nLId = id(nodeList);
+ nodeListMap[nLId] = nodeList;
+
+ can.each(nodeList, function(node) {
+ addNodeListId(node, nLId);
+ });
+
+ },
+ // removes mappings
+ unregister: function(nodeList) {
+ var nLId = id(nodeList);
+ can.each(nodeList, function(node) {
+ removeNodeListId(node, nLId);
+ });
+ delete nodeListMap[nLId];
+ },
+ nodeMap: nodeMap,
+ nodeListMap: nodeListMap
+ }
+ var ids = function(nodeList) {
+ return nodeList.map(function(n) {
+ return id(n) + ":" + (n.innerHTML || n.nodeValue)
+ })
+ }
+ return nodeLists;
+
+ })(__m3);
+
+ // ## view/live.js
+ var __m20 = (function(can, elements, view, nodeLists) {
+ // ## live.js
+ // The live module provides live binding for computes
+ // and can.Observe.List.
+ // Currently, it's API is designed for `can/view/render`, but
+ // it could easily be used for other purposes.
+
+ // ### Helper methods
+ // #### setup
+ // `setup(HTMLElement, bind(data), unbind(data)) -> data`
+ // Calls bind right away, but will call unbind
+ // if the element is "destroyed" (removed from the DOM).
+ var setup = function(el, bind, unbind) {
+ var teardown = function() {
+ unbind(data)
+ can.unbind.call(el, 'destroyed', teardown);
+ },
+ data = {
+ teardownCheck: function(parent) {
+ if (!parent) {
+ teardown();
+ }
+ }
+ }
+
+ can.bind.call(el, 'destroyed', teardown);
+ bind(data)
+ return data;
+ },
+ // #### listen
+ // Calls setup, but presets bind and unbind to
+ // operate on a compute
+ listen = function(el, compute, change) {
+ return setup(el, function() {
+ compute.bind("change", change);
+ }, function(data) {
+ compute.unbind("change", change);
+ if (data.nodeList) {
+ nodeLists.unregister(data.nodeList);
+ }
+ });
+ },
+ // #### getAttributeParts
+ // Breaks up a string like foo='bar' into ["foo","'bar'""]
+ getAttributeParts = function(newVal) {
+ return (newVal || "").replace(/['"]/g, '').split('=')
+ }
+ // #### insertElementsAfter
+ // Appends elements after the last item in oldElements.
+ insertElementsAfter = function(oldElements, newFrag) {
+ var last = oldElements[oldElements.length - 1];
+
+ // Insert it in the `document` or `documentFragment`
+ if (last.nextSibling) {
+ last.parentNode.insertBefore(newFrag, last.nextSibling);
+ } else {
+ last.parentNode.appendChild(newFrag);
+ }
+ };
+
+ var live = {
+ nodeLists: nodeLists,
+ list: function(el, list, func, context, parentNode) {
+ // A mapping of the index to an array
+ // of elements that represent the item.
+ // Each array is registered so child or parent
+ // live structures can update the elements
+ var nodesMap = [],
+
+ add = function(ev, items, index) {
+
+ // Collect new html and mappings
+ var frag = document.createDocumentFragment(),
+ newMappings = [];
+ can.each(items, function(item) {
+ var itemHTML = func.call(context, item),
+ itemFrag = can.view.frag(itemHTML, parentNode);
+
+ newMappings.push(can.makeArray(itemFrag.childNodes));
+ frag.appendChild(itemFrag);
+ })
+
+ // Inserting at the end of the list
+ if (!nodesMap[index]) {
+ insertElementsAfter(
+ index == 0 ? [text] :
+ nodesMap[index - 1], frag)
+ } else {
+ var el = nodesMap[index][0];
+ el.parentNode.insertBefore(frag, el)
+ }
+ // register each item
+ can.each(newMappings, function(nodeList) {
+ nodeLists.register(nodeList)
+ });
+ [].splice.apply(nodesMap, [index, 0].concat(newMappings));
+ },
+ remove = function(ev, items, index) {
+ var removedMappings = nodesMap.splice(index, items.length),
+ itemsToRemove = [];
+
+ can.each(removedMappings, function(nodeList) {
+ // add items that we will remove all at once
+ [].push.apply(itemsToRemove, nodeList)
+ // Update any parent lists to remove these items
+ nodeLists.replace(nodeList, []);
+ // unregister the list
+ nodeLists.unregister(nodeList);
+
+ });
+ can.remove(can.$(itemsToRemove));
+ },
+ parentNode = elements.getParentNode(el, parentNode),
+ text = document.createTextNode("");
+
+ // Setup binding and teardown to add and remove events
+ setup(parentNode, function() {
+ list.bind("add", add).bind("remove", remove)
+ }, function() {
+ list.unbind("add", add).unbind("remove", remove);
+ can.each(nodesMap, function(nodeList) {
+ nodeLists.unregister(nodeList);
+ })
+ })
+
+ insertElementsAfter([el], text);
+ can.remove(can.$(el));
+ add({}, list, 0);
+
+ },
+ html: function(el, compute, parentNode) {
+ var parentNode = elements.getParentNode(el, parentNode),
+
+ data = listen(parentNode, compute, function(ev, newVal, oldVal) {
+ var attached = nodes[0].parentNode;
+ // update the nodes in the DOM with the new rendered value
+ if (attached) {
+ makeAndPut(newVal);
+ }
+ data.teardownCheck(nodes[0].parentNode);
+ });
+
+ var nodes,
+ makeAndPut = function(val) {
+ // create the fragment, but don't hook it up
+ // we need to insert it into the document first
+ var frag = can.view.frag(val, parentNode),
+ // keep a reference to each node
+ newNodes = can.makeArray(frag.childNodes);
+ // Insert it in the `document` or `documentFragment`
+ insertElementsAfter(nodes || [el], frag)
+ // nodes hasn't been set yet
+ if (!nodes) {
+ can.remove(can.$(el));
+ nodes = newNodes;
+ // set the teardown nodeList
+ data.nodeList = nodes;
+ nodeLists.register(nodes);
+ } else {
+ // Update node Array's to point to new nodes
+ // and then remove the old nodes.
+ // It has to be in this order for Mootools
+ // and IE because somehow, after an element
+ // is removed from the DOM, it loses its
+ // expando values.
+ var nodesToRemove = can.makeArray(nodes);
+ nodeLists.replace(nodes, newNodes);
+ can.remove(can.$(nodesToRemove));
+ }
+ };
+ makeAndPut(compute(), [el]);
+
+ },
+ text: function(el, compute, parentNode) {
+ var parent = elements.getParentNode(el, parentNode);
+
+ // setup listening right away so we don't have to re-calculate value
+ var data = listen(el.parentNode !== parent ? el.parentNode : parent, compute, function(ev, newVal, oldVal) {
+ // Sometimes this is 'unknown' in IE and will throw an exception if it is
+ if (typeof node.nodeValue != 'unknown') {
+ node.nodeValue = "" + newVal;
+ }
+ data.teardownCheck(node.parentNode);
+ });
+
+ var node = document.createTextNode(compute());
+
+ if (el.parentNode !== parent) {
+ parent = el.parentNode;
+ parent.insertBefore(node, el);
+ parent.removeChild(el);
+ } else {
+ parent.insertBefore(node, el);
+ parent.removeChild(el);
+ }
+ },
+ attributes: function(el, compute, currentValue) {
+ var setAttrs = function(newVal) {
+ var parts = getAttributeParts(newVal),
+ newAttrName = parts.shift();
+
+ // Remove if we have a change and used to have an `attrName`.
+ if ((newAttrName != attrName) && attrName) {
+ elements.removeAttr(el, attrName);
+ }
+ // Set if we have a new `attrName`.
+ if (newAttrName) {
+ elements.setAttr(el, newAttrName, parts.join('='));
+ attrName = newAttrName;
+ }
+ }
+
+ listen(el, compute, function(ev, newVal) {
+ setAttrs(newVal)
+ })
+ // current value has been set
+ if (arguments.length >= 3) {
+ var attrName = getAttributeParts(currentValue)[0]
+ } else {
+ setAttrs(compute())
+ }
+ },
+ attributePlaceholder: '__!!__',
+ attributeReplace: /__!!__/g,
+ attribute: function(el, attributeName, compute) {
+ listen(el, compute, function(ev, newVal) {
+ elements.setAttr(el, attributeName, hook.render());
+ })
+
+ var wrapped = can.$(el),
+ hooks;
+
+ // Get the list of hookups or create one for this element.
+ // Hooks is a map of attribute names to hookup `data`s.
+ // Each hookup data has:
+ // `render` - A `function` to render the value of the attribute.
+ // `funcs` - A list of hookup `function`s on that attribute.
+ // `batchNum` - The last event `batchNum`, used for performance.
+ hooks = can.data(wrapped, 'hooks');
+ if (!hooks) {
+ can.data(wrapped, 'hooks', hooks = {});
+ }
+
+ // Get the attribute value.
+ var attr = elements.getAttr(el, attributeName),
+ // Split the attribute value by the template.
+ // Only split out the first __!!__ so if we have multiple hookups in the same attribute,
+ // they will be put in the right spot on first render
+ parts = attr.split(live.attributePlaceholder),
+ goodParts = [],
+ hook;
+ goodParts.push(parts.shift(),
+ parts.join(live.attributePlaceholder));
+
+ // If we already had a hookup for this attribute...
+ if (hooks[attributeName]) {
+ // Just add to that attribute's list of `function`s.
+ hooks[attributeName].computes.push(compute);
+ } else {
+ // Create the hookup data.
+ hooks[attributeName] = {
+ render: function() {
+ var i = 0,
+ // attr doesn't have a value in IE
+ newAttr = attr ? attr.replace(live.attributeReplace, function() {
+ return elements.contentText(hook.computes[i++]());
+ }) : elements.contentText(hook.computes[i++]());
+ return newAttr;
+ },
+ computes: [compute],
+ batchNum: undefined
+ };
+ }
+
+ // Save the hook for slightly faster performance.
+ hook = hooks[attributeName];
+
+ // Insert the value in parts.
+ goodParts.splice(1, 0, compute());
+
+ // Set the attribute.
+ elements.setAttr(el, attributeName, goodParts.join(""));
+
+ }
+ }
+ return live;
+
+ })(__m3, __m18, __m15, __m21);
+
+ // ## view/render.js
+ var __m19 = (function(can, elements, live) {
+
+ var pendingHookups = [],
+ tagChildren = function(tagName) {
+ var newTag = elements.tagMap[tagName] || "span";
+ if (newTag === "span") {
+ //innerHTML in IE doesn't honor leading whitespace after empty elements
+ return "@@!!@@";
+ }
+ return "<" + newTag + ">" + tagChildren(newTag) + "</" + newTag + ">";
+ },
+ contentText = function(input, tag) {
+
+ // If it's a string, return.
+ if (typeof input == 'string') {
+ return input;
+ }
+ // If has no value, return an empty string.
+ if (!input && input !== 0) {
+ return '';
+ }
+
+ // If it's an object, and it has a hookup method.
+ var hook = (input.hookup &&
+
+ // Make a function call the hookup method.
+
+ function(el, id) {
+ input.hookup.call(input, el, id);
+ }) ||
+
+ // Or if it's a `function`, just use the input.
+ (typeof input == 'function' && input);
+
+ // Finally, if there is a `function` to hookup on some dom,
+ // add it to pending hookups.
+ if (hook) {
+ if (tag) {
+ return "<" + tag + " " + can.view.hook(hook) + "></" + tag + ">"
+ } else {
+ pendingHookups.push(hook);
+ }
+
+ return '';
+ }
+
+ // Finally, if all else is `false`, `toString()` it.
+ return "" + input;
+ },
+ // Returns escaped/sanatized content for anything other than a live-binding
+ contentEscape = function(txt) {
+ return (typeof txt == 'string' || typeof txt == 'number') ?
+ can.esc(txt) :
+ contentText(txt);
+ };
+
+ var current;
+
+ can.extend(can.view, {
+ live: live,
+ setupLists: function() {
+
+ var old = can.view.lists,
+ data;
+
+ can.view.lists = function(list, renderer) {
+ data = {
+ list: list,
+ renderer: renderer
+ }
+ }
+ return function() {
+ can.view.lists = old;
+ return data;
+ }
+ },
+ pending: function() {
+ // TODO, make this only run for the right tagName
+ var hooks = pendingHookups.slice(0);
+ lastHookups = hooks;
+ pendingHookups = [];
+ return can.view.hook(function(el) {
+ can.each(hooks, function(fn) {
+ fn(el);
+ });
+ });
+ },
+
+
+ txt: function(escape, tagName, status, self, func) {
+ var listTeardown = can.view.setupLists(),
+ emptyHandler = function() {},
+ unbind = function() {
+ compute.unbind("change", emptyHandler)
+ };
+
+ var compute = can.compute(func, self, false);
+ // bind to get and temporarily cache the value
+ compute.bind("change", emptyHandler);
+ // call the "wrapping" function and get the binding information
+ var tag = (elements.tagMap[tagName] || "span"),
+ listData = listTeardown(),
+ value = compute();
+
+
+ if (listData) {
+ return "<" + tag + can.view.hook(function(el, parentNode) {
+ live.list(el, listData.list, listData.renderer, self, parentNode);
+ }) + "></" + tag + ">";
+ }
+
+ // If we had no observes just return the value returned by func.
+ if (!compute.hasDependencies) {
+ unbind();
+ return (escape || status !== 0 ? contentEscape : contentText)(value, status === 0 && tag);
+ }
+
+ // the property (instead of innerHTML elements) to adjust. For
+ // example options should use textContent
+ var contentProp = elements.tagToContentPropMap[tagName];
+
+
+ // The magic tag is outside or between tags.
+ if (status === 0 && !contentProp) {
+ // Return an element tag with a hookup in place of the content
+ return "<" + tag + can.view.hook(
+ escape ?
+ // If we are escaping, replace the parentNode with
+ // a text node who's value is `func`'s return value.
+
+ function(el, parentNode) {
+ live.text(el, compute, parentNode);
+ unbind();
+ } :
+ // If we are not escaping, replace the parentNode with a
+ // documentFragment created as with `func`'s return value.
+
+ function(el, parentNode) {
+ live.html(el, compute, parentNode);
+ unbind();
+ //children have to be properly nested HTML for buildFragment to work properly
+ }) + ">" + tagChildren(tag) + "</" + tag + ">";
+ // In a tag, but not in an attribute
+ } else if (status === 1) {
+ // remember the old attr name
+ pendingHookups.push(function(el) {
+ live.attributes(el, compute, compute());
+ unbind();
+ });
+ return compute();
+ } else { // In an attribute...
+ var attributeName = status === 0 ? contentProp : status;
+ // if the magic tag is inside the element, like `<option><% TAG %></option>`,
+ // we add this hookup to the last element (ex: `option`'s) hookups.
+ // Otherwise, the magic tag is in an attribute, just add to the current element's
+ // hookups.
+ (status === 0 ? lastHookups : pendingHookups).push(function(el) {
+ live.attribute(el, attributeName, compute);
+ unbind();
+ });
+ return live.attributePlaceholder;
+ }
+ }
+ });
+
+ return can;
+ })(__m15, __m18, __m20, __m2);
+
+ // ## view/ejs/ejs.js
+ var __m16 = (function(can) {
+ // ## ejs.js
+ // `can.EJS`
+ // _Embedded JavaScript Templates._
+
+ // Helper methods.
+ var extend = can.extend,
+ EJS = function(options) {
+ // Supports calling EJS without the constructor
+ // This returns a function that renders the template.
+ if (this.constructor != EJS) {
+ var ejs = new EJS(options);
+ return function(data, helpers) {
+ return ejs.render(data, helpers);
+ };
+ }
+ // If we get a `function` directly, it probably is coming from
+ // a `steal`-packaged view.
+ if (typeof options == "function") {
+ this.template = {
+ fn: options
+ };
+ return;
+ }
+ // Set options on self.
+ extend(this, options);
+ this.template = this.scanner.scan(this.text, this.name);
+ };
+
+ can.EJS = EJS;
+
+
+ EJS.prototype.
+
+ render = function(object, extraHelpers) {
+ object = object || {};
+ return this.template.fn.call(object, object, new EJS.Helpers(object, extraHelpers || {}));
+ };
+
+ extend(EJS.prototype, {
+
+ scanner: new can.view.Scanner({
+
+ tokens: [
+ ["templateLeft", "<%%"], // Template
+ ["templateRight", "%>"], // Right Template
+ ["returnLeft", "<%=="], // Return Unescaped
+ ["escapeLeft", "<%="], // Return Escaped
+ ["commentLeft", "<%#"], // Comment
+ ["left", "<%"], // Run --- this is hack for now
+ ["right", "%>"], // Right -> All have same FOR Mustache ...
+ ["returnRight", "%>"]
+ ],
+
+
+ transform: function(source) {
+ return source.replace(/<%([\s\S]+?)%>/gm, function(whole, part) {
+ var brackets = [],
+ foundBracketPair,
+ i;
+
+ // Look for brackets (for removing self-contained blocks)
+ part.replace(/[{}]/gm, function(bracket, offset) {
+ brackets.push([bracket, offset]);
+ });
+
+ // Remove bracket pairs from the list of replacements
+ do {
+ foundBracketPair = false;
+ for (i = brackets.length - 2; i >= 0; i--) {
+ if (brackets[i][0] == '{' && brackets[i + 1][0] == '}') {
+ brackets.splice(i, 2);
+ foundBracketPair = true;
+ break;
+ }
+ }
+ } while (foundBracketPair);
+
+ // Unmatched brackets found, inject EJS tags
+ if (brackets.length >= 2) {
+ var result = ['<%'],
+ bracket,
+ last = 0;
+ for (i = 0; bracket = brackets[i]; i++) {
+ result.push(part.substring(last, last = bracket[1]));
+ if ((bracket[0] == '{' && i < brackets.length - 1) || (bracket[0] == '}' && i > 0)) {
+ result.push(bracket[0] == '{' ? '{ %><% ' : ' %><% }');
+ } else {
+ result.push(bracket[0]);
+ }
+ ++last;
+ }
+ result.push(part.substring(last), '%>');
+ return result.join('');
+ }
+ // Otherwise return the original
+ else {
+ return '<%' + part + '%>';
+ }
+ });
+ }
+ })
+ });
+
+ EJS.Helpers = function(data, extras) {
+ this._data = data;
+ this._extras = extras;
+ extend(this, extras);
+ };
+
+
+ EJS.Helpers.prototype = {
+ // TODO Deprecated!!
+ list: function(list, cb) {
+
+ can.each(list, function(item, i) {
+ cb(item, i, list)
+ })
+ },
+ each: function(list, cb) {
+ // Normal arrays don't get live updated
+ if (can.isArray(list)) {
+ this.list(list, cb);
+ } else {
+ can.view.lists(list, cb);
+ }
+ }
+ };
+
+ // Options for `steal`'s build.
+ can.view.register({
+ suffix: "ejs",
+ // returns a `function` that renders the view.
+ script: function(id, src) {
+ return "can.EJS(function(_CONTEXT,_VIEW) { " + new EJS({
+ text: src,
+ name: id
+ }).template.out + " })";
+ },
+ renderer: function(id, text) {
+ return EJS({
+ text: text,
+ name: id
+ });
+ }
+ });
+
+ return can;
+ })(__m3, __m15, __m2, __m13, __m17, __m19);
+
+ // ## control/control.js
+ var __m22 = (function(can) {
+ // ## control.js
+ // `can.Control`
+ // _Controller_
+
+ // Binds an element, returns a function that unbinds.
+ var bind = function(el, ev, callback) {
+
+ can.bind.call(el, ev, callback);
+
+ return function() {
+ can.unbind.call(el, ev, callback);
+ };
+ },
+ isFunction = can.isFunction,
+ extend = can.extend,
+ each = can.each,
+ slice = [].slice,
+ paramReplacer = /\{([^\}]+)\}/g,
+ special = can.getObject("$.event.special", [can]) || {},
+
+ // Binds an element, returns a function that unbinds.
+ delegate = function(el, selector, ev, callback) {
+ can.delegate.call(el, selector, ev, callback);
+ return function() {
+ can.undelegate.call(el, selector, ev, callback);
+ };
+ },
+
+ // Calls bind or unbind depending if there is a selector.
+ binder = function(el, ev, callback, selector) {
+ return selector ?
+ delegate(el, can.trim(selector), ev, callback) :
+ bind(el, ev, callback);
+ },
+
+ basicProcessor;
+
+ var Control = can.Control = can.Construct(
+
+ {
+ // Setup pre-processes which methods are event listeners.
+
+ setup: function() {
+
+ // Allow contollers to inherit "defaults" from super-classes as it
+ // done in `can.Construct`
+ can.Construct.setup.apply(this, arguments);
+
+ // If you didn't provide a name, or are `control`, don't do anything.
+ if (can.Control) {
+
+ // Cache the underscored names.
+ var control = this,
+ funcName;
+
+ // Calculate and cache actions.
+ control.actions = {};
+ for (funcName in control.prototype) {
+ if (control._isAction(funcName)) {
+ control.actions[funcName] = control._action(funcName);
+ }
+ }
+ }
+ },
+
+ // Moves `this` to the first argument, wraps it with `jQuery` if it's an element
+ _shifter: function(context, name) {
+
+ var method = typeof name == "string" ? context[name] : name;
+
+ if (!isFunction(method)) {
+ method = context[method];
+ }
+
+ return function() {
+ context.called = name;
+ return method.apply(context, [this.nodeName ? can.$(this) : this].concat(slice.call(arguments, 0)));
+ };
+ },
+
+ // Return `true` if is an action.
+
+ _isAction: function(methodName) {
+
+ var val = this.prototype[methodName],
+ type = typeof val;
+ // if not the constructor
+ return (methodName !== 'constructor') &&
+ // and is a function or links to a function
+ (type == "function" || (type == "string" && isFunction(this.prototype[val]))) &&
+ // and is in special, a processor, or has a funny character
+ !! (special[methodName] || processors[methodName] || /[^\w]/.test(methodName));
+ },
+ // Takes a method name and the options passed to a control
+ // and tries to return the data necessary to pass to a processor
+ // (something that binds things).
+
+ _action: function(methodName, options) {
+
+ // If we don't have options (a `control` instance), we'll run this
+ // later.
+ paramReplacer.lastIndex = 0;
+ if (options || !paramReplacer.test(methodName)) {
+ // If we have options, run sub to replace templates `{}` with a
+ // value from the options or the window
+ var convertedName = options ? can.sub(methodName, [options, window]) : methodName;
+ if (!convertedName) {
+ return null;
+ }
+ // If a `{}` template resolves to an object, `convertedName` will be
+ // an array
+ var arr = can.isArray(convertedName),
+
+ // Get the name
+ name = arr ? convertedName[1] : convertedName,
+
+ // Grab the event off the end
+ parts = name.split(/\s+/g),
+ event = parts.pop();
+
+ return {
+ processor: processors[event] || basicProcessor,
+ parts: [name, parts.join(" "), event],
+ delegate: arr ? convertedName[0] : undefined
+ };
+ }
+ },
+ // An object of `{eventName : function}` pairs that Control uses to
+ // hook up events auto-magically.
+
+ processors: {},
+ // A object of name-value pairs that act as default values for a
+ // control instance
+ defaults: {}
+
+ }, {
+
+ // Sets `this.element`, saves the control in `data, binds event
+ // handlers.
+
+ setup: function(element, options) {
+
+ var cls = this.constructor,
+ pluginname = cls.pluginName || cls._fullName,
+ arr;
+
+ // Want the raw element here.
+ this.element = can.$(element)
+
+ if (pluginname && pluginname !== 'can_control') {
+ // Set element and `className` on element.
+ this.element.addClass(pluginname);
+ }
+
+ (arr = can.data(this.element, "controls")) || can.data(this.element, "controls", arr = []);
+ arr.push(this);
+
+ // Option merging.
+
+ this.options = extend({}, cls.defaults, options);
+
+ // Bind all event handlers.
+ this.on();
+
+ // Gets passed into `init`.
+
+ return [this.element, this.options];
+ },
+
+ on: function(el, selector, eventName, func) {
+ if (!el) {
+
+ // Adds bindings.
+ this.off();
+
+ // Go through the cached list of actions and use the processor
+ // to bind
+ var cls = this.constructor,
+ bindings = this._bindings,
+ actions = cls.actions,
+ element = this.element,
+ destroyCB = can.Control._shifter(this, "destroy"),
+ funcName, ready;
+
+ for (funcName in actions) {
+ // Only push if we have the action and no option is `undefined`
+ if (actions.hasOwnProperty(funcName) &&
+ (ready = actions[funcName] || cls._action(funcName, this.options))) {
+ bindings.push(ready.processor(ready.delegate || element,
+ ready.parts[2], ready.parts[1], funcName, this));
+ }
+ }
+
+
+ // Setup to be destroyed...
+ // don't bind because we don't want to remove it.
+ can.bind.call(element, "destroyed", destroyCB);
+ bindings.push(function(el) {
+ can.unbind.call(el, "destroyed", destroyCB);
+ });
+ return bindings.length;
+ }
+
+ if (typeof el == 'string') {
+ func = eventName;
+ eventName = selector;
+ selector = el;
+ el = this.element;
+ }
+
+ if (func === undefined) {
+ func = eventName;
+ eventName = selector;
+ selector = null;
+ }
+
+ if (typeof func == 'string') {
+ func = can.Control._shifter(this, func);
+ }
+
+ this._bindings.push(binder(el, eventName, func, selector));
+
+ return this._bindings.length;
+ },
+ // Unbinds all event handlers on the controller.
+
+ off: function() {
+ var el = this.element[0]
+ each(this._bindings || [], function(value) {
+ value(el);
+ });
+ // Adds bindings.
+ this._bindings = [];
+ },
+ // Prepares a `control` for garbage collection
+
+ destroy: function() {
+ //Control already destroyed
+ if (this.element === null) {
+
+ return;
+ }
+ var Class = this.constructor,
+ pluginName = Class.pluginName || Class._fullName,
+ controls;
+
+ // Unbind bindings.
+ this.off();
+
+ if (pluginName && pluginName !== 'can_control') {
+ // Remove the `className`.
+ this.element.removeClass(pluginName);
+ }
+
+ // Remove from `data`.
+ controls = can.data(this.element, "controls");
+ controls.splice(can.inArray(this, controls), 1);
+
+ can.trigger(this, "destroyed"); // In case we want to know if the `control` is removed.
+
+ this.element = null;
+ }
+ });
+
+ var processors = can.Control.processors,
+ // Processors do the binding.
+ // They return a function that unbinds when called.
+ // The basic processor that binds events.
+ basicProcessor = function(el, event, selector, methodName, control) {
+ return binder(el, event, can.Control._shifter(control, methodName), selector);
+ };
+
+ // Set common events to be processed as a `basicProcessor`
+ each(["change", "click", "contextmenu", "dblclick", "keydown", "keyup",
+ "keypress", "mousedown", "mousemove", "mouseout", "mouseover",
+ "mouseup", "reset", "resize", "scroll", "select", "submit", "focusin",
+ "focusout", "mouseenter", "mouseleave",
+ // #104 - Add touch events as default processors
+ // TOOD feature detect?
+ "touchstart", "touchmove", "touchcancel", "touchend", "touchleave"
+ ], function(v) {
+ processors[v] = basicProcessor;
+ });
+
+ return Control;
+ })(__m3, __m1);
+
+ // ## util/string/deparam/deparam.js
+ var __m24 = (function(can) {
+
+ // ## deparam.js
+ // `can.deparam`
+ // _Takes a string of name value pairs and returns a Object literal that represents those params._
+ var digitTest = /^\d+$/,
+ keyBreaker = /([^\[\]]+)|(\[\])/g,
+ paramTest = /([^?#]*)(#.*)?$/,
+ prep = function(str) {
+ return decodeURIComponent(str.replace(/\+/g, " "));
+ };
+
+
+ can.extend(can, {
+
+ deparam: function(params) {
+
+ var data = {},
+ pairs, lastPart;
+
+ if (params && paramTest.test(params)) {
+
+ pairs = params.split('&'),
+
+ can.each(pairs, function(pair) {
+
+ var parts = pair.split('='),
+ key = prep(parts.shift()),
+ value = prep(parts.join("=")),
+ current = data;
+
+ if (key) {
+ parts = key.match(keyBreaker);
+
+ for (var j = 0, l = parts.length - 1; j < l; j++) {
+ if (!current[parts[j]]) {
+ // If what we are pointing to looks like an `array`
+ current[parts[j]] = digitTest.test(parts[j + 1]) || parts[j + 1] == "[]" ? [] : {};
+ }
+ current = current[parts[j]];
+ }
+ lastPart = parts.pop();
+ if (lastPart == "[]") {
+ current.push(value);
+ } else {
+ current[lastPart] = value;
+ }
+ }
+ });
+ }
+ return data;
+ }
+ });
+ return can;
+ })(__m3, __m2);
+
+ // ## route/route.js
+ var __m23 = (function(can) {
+
+ // ## route.js
+ // `can.route`
+ // _Helps manage browser history (and client state) by synchronizing the
+ // `window.location.hash` with a `can.Observe`._
+ // Helper methods used for matching routes.
+ var
+ // `RegExp` used to match route variables of the type ':name'.
+ // Any word character or a period is matched.
+ matcher = /\:([\w\.]+)/g,
+ // Regular expression for identifying &key=value lists.
+ paramsMatcher = /^(?:&[^=]+=[^&]*)+/,
+ // Converts a JS Object into a list of parameters that can be
+ // inserted into an html element tag.
+ makeProps = function(props) {
+ var tags = [];
+ can.each(props, function(val, name) {
+ tags.push((name === 'className' ? 'class' : name) + '="' +
+ (name === "href" ? val : can.esc(val)) + '"');
+ });
+ return tags.join(" ");
+ },
+ // Checks if a route matches the data provided. If any route variable
+ // is not present in the data, the route does not match. If all route
+ // variables are present in the data, the number of matches is returned
+ // to allow discerning between general and more specific routes.
+ matchesData = function(route, data) {
+ var count = 0,
+ i = 0,
+ defaults = {};
+ // look at default values, if they match ...
+ for (var name in route.defaults) {
+ if (route.defaults[name] === data[name]) {
+ // mark as matched
+ defaults[name] = 1;
+ count++;
+ }
+ }
+ for (; i < route.names.length; i++) {
+ if (!data.hasOwnProperty(route.names[i])) {
+ return -1;
+ }
+ if (!defaults[route.names[i]]) {
+ count++;
+ }
+
+ }
+
+ return count;
+ },
+ onready = !0,
+ location = window.location,
+ wrapQuote = function(str) {
+ return (str + '').replace(/([.?*+\^$\[\]\\(){}|\-])/g, "\\$1");
+ },
+ each = can.each,
+ extend = can.extend;
+
+ can.route = function(url, defaults) {
+ defaults = defaults || {};
+ // Extract the variable names and replace with `RegExp` that will match
+ // an atual URL with values.
+ var names = [],
+ test = url.replace(matcher, function(whole, name, i) {
+ names.push(name);
+ var next = "\\" + (url.substr(i + whole.length, 1) || can.route._querySeparator);
+ // a name without a default value HAS to have a value
+ // a name that has a default value can be empty
+ // The `\\` is for string-escaping giving single `\` for `RegExp` escaping.
+ return "([^" + next + "]" + (defaults[name] ? "*" : "+") + ")";
+ });
+
+ // Add route in a form that can be easily figured out.
+ can.route.routes[url] = {
+ // A regular expression that will match the route when variable values
+ // are present; i.e. for `:page/:type` the `RegExp` is `/([\w\.]*)/([\w\.]*)/` which
+ // will match for any value of `:page` and `:type` (word chars or period).
+ test: new RegExp("^" + test + "($|" + wrapQuote(can.route._querySeparator) + ")"),
+ // The original URL, same as the index for this entry in routes.
+ route: url,
+ // An `array` of all the variable names in this route.
+ names: names,
+ // Default values provided for the variables.
+ defaults: defaults,
+ // The number of parts in the URL separated by `/`.
+ length: url.split('/').length
+ };
+ return can.route;
+ };
+
+
+ extend(can.route, {
+
+ _querySeparator: '&',
+ _paramsMatcher: paramsMatcher,
+
+
+ param: function(data, _setRoute) {
+ // Check if the provided data keys match the names in any routes;
+ // Get the one with the most matches.
+ var route,
+ // Need to have at least 1 match.
+ matches = 0,
+ matchCount,
+ routeName = data.route,
+ propCount = 0;
+
+ delete data.route;
+
+ each(data, function() {
+ propCount++;
+ });
+ // Otherwise find route.
+ each(can.route.routes, function(temp, name) {
+ // best route is the first with all defaults matching
+
+
+ matchCount = matchesData(temp, data);
+ if (matchCount > matches) {
+ route = temp;
+ matches = matchCount;
+ }
+ if (matchCount >= propCount) {
+ return false;
+ }
+ });
+ // If we have a route name in our `can.route` data, and it's
+ // just as good as what currently matches, use that
+ if (can.route.routes[routeName] && matchesData(can.route.routes[routeName], data) === matches) {
+ route = can.route.routes[routeName];
+ }
+ // If this is match...
+ if (route) {
+ var cpy = extend({}, data),
+ // Create the url by replacing the var names with the provided data.
+ // If the default value is found an empty string is inserted.
+ res = route.route.replace(matcher, function(whole, name) {
+ delete cpy[name];
+ return data[name] === route.defaults[name] ? "" : encodeURIComponent(data[name]);
+ }),
+ after;
+ // Remove matching default values
+ each(route.defaults, function(val, name) {
+ if (cpy[name] === val) {
+ delete cpy[name];
+ }
+ });
+
+ // The remaining elements of data are added as
+ // `&` separated parameters to the url.
+ after = can.param(cpy);
+ // if we are paraming for setting the hash
+ // we also want to make sure the route value is updated
+ if (_setRoute) {
+ can.route.attr('route', route.route);
+ }
+ return res + (after ? can.route._querySeparator + after : "");
+ }
+ // If no route was found, there is no hash URL, only paramters.
+ return can.isEmptyObject(data) ? "" : can.route._querySeparator + can.param(data);
+ },
+
+ deparam: function(url) {
+ // See if the url matches any routes by testing it against the `route.test` `RegExp`.
+ // By comparing the URL length the most specialized route that matches is used.
+ var route = {
+ length: -1
+ };
+ each(can.route.routes, function(temp, name) {
+ if (temp.test.test(url) && temp.length > route.length) {
+ route = temp;
+ }
+ });
+ // If a route was matched.
+ if (route.length > -1) {
+
+ var // Since `RegExp` backreferences are used in `route.test` (parens)
+ // the parts will contain the full matched string and each variable (back-referenced) value.
+ parts = url.match(route.test),
+ // Start will contain the full matched string; parts contain the variable values.
+ start = parts.shift(),
+ // The remainder will be the `&key=value` list at the end of the URL.
+ remainder = url.substr(start.length - (parts[parts.length - 1] === can.route._querySeparator ? 1 : 0)),
+ // If there is a remainder and it contains a `&key=value` list deparam it.
+ obj = (remainder && can.route._paramsMatcher.test(remainder)) ? can.deparam(remainder.slice(1)) : {};
+
+ // Add the default values for this route.
+ obj = extend(true, {}, route.defaults, obj);
+ // Overwrite each of the default values in `obj` with those in
+ // parts if that part is not empty.
+ each(parts, function(part, i) {
+ if (part && part !== can.route._querySeparator) {
+ obj[route.names[i]] = decodeURIComponent(part);
+ }
+ });
+ obj.route = route.route;
+ return obj;
+ }
+ // If no route was matched, it is parsed as a `&key=value` list.
+ if (url.charAt(0) !== can.route._querySeparator) {
+ url = can.route._querySeparator + url;
+ }
+ return can.route._paramsMatcher.test(url) ? can.deparam(url.slice(1)) : {};
+ },
+
+ data: new can.Observe({}),
+
+ routes: {},
+
+ ready: function(val) {
+ if (val === false) {
+ onready = val;
+ }
+ if (val === true || onready === true) {
+ can.route._setup();
+ setState();
+ }
+ return can.route;
+ },
+
+ url: function(options, merge) {
+ if (merge) {
+ options = extend({}, curParams, options)
+ }
+ return "#!" + can.route.param(options);
+ },
+
+ link: function(name, options, props, merge) {
+ return "<a " + makeProps(
+ extend({
+ href: can.route.url(options, merge)
+ }, props)) + ">" + name + "</a>";
+ },
+
+ current: function(options) {
+ return location.hash == "#!" + can.route.param(options)
+ },
+ _setup: function() {
+ // If the hash changes, update the `can.route.data`.
+ can.bind.call(window, 'hashchange', setState);
+ },
+ _getHash: function() {
+ return location.href.split(/#!?/)[1] || "";
+ },
+ _setHash: function(serialized) {
+ var path = (can.route.param(serialized, true));
+ location.hash = "#!" + path;
+ return path;
+ }
+ });
+
+
+ // The functions in the following list applied to `can.route` (e.g. `can.route.attr('...')`) will
+ // instead act on the `can.route.data` observe.
+ each(['bind', 'unbind', 'delegate', 'undelegate', 'attr', 'removeAttr'], function(name) {
+ can.route[name] = function() {
+ // `delegate` and `undelegate` require
+ // the `can/observe/delegate` plugin
+ if (!can.route.data[name]) {
+ return;
+ }
+
+ return can.route.data[name].apply(can.route.data, arguments);
+ }
+ })
+
+ var // A ~~throttled~~ debounced function called multiple times will only fire once the
+ // timer runs down. Each call resets the timer.
+ timer,
+ // Intermediate storage for `can.route.data`.
+ curParams,
+ // Deparameterizes the portion of the hash of interest and assign the
+ // values to the `can.route.data` removing existing values no longer in the hash.
+ // setState is called typically by hashchange which fires asynchronously
+ // So it's possible that someone started changing the data before the
+ // hashchange event fired. For this reason, it will not set the route data
+ // if the data is changing or the hash already matches the hash that was set.
+ setState = can.route.setState = function() {
+ var hash = can.route._getHash();
+ curParams = can.route.deparam(hash);
+
+ // if the hash data is currently changing, or
+ // the hash is what we set it to anyway, do NOT change the hash
+ if (!changingData || hash !== lastHash) {
+ can.route.attr(curParams, true);
+ }
+ },
+ // The last hash caused by a data change
+ lastHash,
+ // Are data changes pending that haven't yet updated the hash
+ changingData;
+
+ // If the `can.route.data` changes, update the hash.
+ // Using `.serialize()` retrieves the raw data contained in the `observable`.
+ // This function is ~~throttled~~ debounced so it only updates once even if multiple values changed.
+ // This might be able to use batchNum and avoid this.
+ can.route.bind("change", function(ev, attr) {
+ // indicate that data is changing
+ changingData = 1;
+ clearTimeout(timer);
+ timer = setTimeout(function() {
+ // indicate that the hash is set to look like the data
+ changingData = 0;
+ var serialized = can.route.data.serialize();
+
+ lastHash = can.route._setHash(serialized);
+ }, 1);
+ });
+ // `onready` event...
+ can.bind.call(document, "ready", can.route.ready);
+
+ // Libraries other than jQuery don't execute the document `ready` listener
+ // if we are already DOM ready
+ if ((document.readyState === 'complete' || document.readyState === "interactive") && onready) {
+ can.route.ready();
+ }
+
+ // extend route to have a similar property
+ // that is often checked in mustache to determine
+ // an object's observability
+ can.route.constructor.canMakeObserve = can.Observe.canMakeObserve;
+
+ return can.route;
+ })(__m3, __m11, __m24);
+
+ // ## control/route/route.js
+ var __m25 = (function(can) {
+
+ // ## control/route.js
+ // _Controller route integration._
+
+ can.Control.processors.route = function(el, event, selector, funcName, controller) {
+ selector = selector || "";
+ can.route(selector);
+ var batchNum,
+ check = function(ev, attr, how) {
+ if (can.route.attr('route') === (selector) &&
+ (ev.batchNum === undefined || ev.batchNum !== batchNum)) {
+
+ batchNum = ev.batchNum;
+
+ var d = can.route.attr();
+ delete d.route;
+ if (can.isFunction(controller[funcName])) {
+ controller[funcName](d);
+ } else {
+ controller[controller[funcName]](d);
+ }
+
+ }
+ };
+ can.route.bind('change', check);
+ return function() {
+ can.route.unbind('change', check);
+ };
+ };
+
+ return can;
+ })(__m3, __m23, __m22);
+
+ window['can'] = __m4;
+ })();
\ No newline at end of file
Added: tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.object.js
===================================================================
--- tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.object.js (rev 0)
+++ tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.object.js 2013-06-17 15:45:26 UTC (rev 92)
@@ -0,0 +1,137 @@
+/*!
+ * CanJS - 1.1.6
+ * http://canjs.us/
+ * Copyright (c) 2013 Bitovi
+ * Wed, 29 May 2013 18:59:29 GMT
+ * Licensed MIT
+ * Includes: can/util/object
+ * Download from: http://canjs.com
+ */
+(function(can) {
+
+ var isArray = can.isArray,
+ // essentially returns an object that has all the must have comparisons ...
+ // must haves, do not return true when provided undefined
+ cleanSet = function(obj, compares) {
+ var copy = can.extend({}, obj);
+ for (var prop in copy) {
+ var compare = compares[prop] === undefined ? compares["*"] : compares[prop];
+ if (same(copy[prop], undefined, compare)) {
+ delete copy[prop]
+ }
+ }
+ return copy;
+ },
+ propCount = function(obj) {
+ var count = 0;
+ for (var prop in obj) count++;
+ return count;
+ };
+
+ can.Object = {};
+
+ var same = can.Object.same = function(a, b, compares, aParent, bParent, deep) {
+ var aType = typeof a,
+ aArray = isArray(a),
+ comparesType = typeof compares,
+ compare;
+
+ if (comparesType == 'string' || compares === null) {
+ compares = compareMethods[compares];
+ comparesType = 'function'
+ }
+ if (comparesType == 'function') {
+ return compares(a, b, aParent, bParent)
+ }
+ compares = compares || {};
+
+ if (a instanceof Date) {
+ return a === b;
+ }
+ if (deep === -1) {
+ return aType === 'object' || a === b;
+ }
+ if (aType !== typeof b || aArray !== isArray(b)) {
+ return false;
+ }
+ if (a === b) {
+ return true;
+ }
+ if (aArray) {
+ if (a.length !== b.length) {
+ return false;
+ }
+ for (var i = 0; i < a.length; i++) {
+ compare = compares[i] === undefined ? compares["*"] : compares[i]
+ if (!same(a[i], b[i], a, b, compare)) {
+ return false;
+ }
+ };
+ return true;
+ } else if (aType === "object" || aType === 'function') {
+ var bCopy = can.extend({}, b);
+ for (var prop in a) {
+ compare = compares[prop] === undefined ? compares["*"] : compares[prop];
+ if (!same(a[prop], b[prop], compare, a, b, deep === false ? -1 : undefined)) {
+ return false;
+ }
+ delete bCopy[prop];
+ }
+ // go through bCopy props ... if there is no compare .. return false
+ for (prop in bCopy) {
+ if (compares[prop] === undefined || !same(undefined, b[prop], compares[prop], a, b, deep === false ? -1 : undefined)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ };
+
+ can.Object.subsets = function(checkSet, sets, compares) {
+ var len = sets.length,
+ subsets = [],
+ checkPropCount = propCount(checkSet),
+ setLength;
+
+ for (var i = 0; i < len; i++) {
+ //check this subset
+ var set = sets[i];
+ if (can.Object.subset(checkSet, set, compares)) {
+ subsets.push(set)
+ }
+ }
+ return subsets;
+ };
+
+ can.Object.subset = function(subset, set, compares) {
+ // go through set {type: 'folder'} and make sure every property
+ // is in subset {type: 'folder', parentId :5}
+ // then make sure that set has fewer properties
+ // make sure we are only checking 'important' properties
+ // in subset (ones that have to have a value)
+
+ var setPropCount = 0,
+ compares = compares || {};
+
+ for (var prop in set) {
+
+ if (!same(subset[prop], set[prop], compares[prop], subset, set)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ var compareMethods = {
+ "null": function() {
+ return true;
+ },
+ i: function(a, b) {
+ return ("" + a).toLowerCase() == ("" + b).toLowerCase()
+ }
+ }
+
+ return can.Object;
+
+ })(can);
\ No newline at end of file
Added: tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.observe.attributes.js
===================================================================
--- tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.observe.attributes.js (rev 0)
+++ tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.observe.attributes.js 2013-06-17 15:45:26 UTC (rev 92)
@@ -0,0 +1,174 @@
+/*!
+ * CanJS - 1.1.6
+ * http://canjs.us/
+ * Copyright (c) 2013 Bitovi
+ * Wed, 29 May 2013 18:59:29 GMT
+ * Licensed MIT
+ * Includes: can/observe/attributes
+ * Download from: http://canjs.com
+ */
+(function(can, Observe) {
+
+ can.each([can.Observe, can.Model], function(clss) {
+ // in some cases model might not be defined quite yet.
+ if (clss === undefined) {
+ return;
+ }
+ var isObject = function(obj) {
+ return typeof obj === 'object' && obj !== null && obj;
+ };
+
+ can.extend(clss, {
+
+ attributes: {},
+
+
+ convert: {
+ "date": function(str) {
+ var type = typeof str;
+ if (type === "string") {
+ return isNaN(Date.parse(str)) ? null : Date.parse(str)
+ } else if (type === 'number') {
+ return new Date(str)
+ } else {
+ return str
+ }
+ },
+ "number": function(val) {
+ return parseFloat(val);
+ },
+ "boolean": function(val) {
+ if (val === 'false' || val === '0' || !val) {
+ return false;
+ }
+ return true;
+ },
+ "default": function(val, oldVal, error, type) {
+ var construct = can.getObject(type),
+ context = window,
+ realType;
+ // if type has a . we need to look it up
+ if (type.indexOf(".") >= 0) {
+ // get everything before the last .
+ realType = type.substring(0, type.lastIndexOf("."));
+ // get the object before the last .
+ context = can.getObject(realType);
+ }
+ return typeof construct == "function" ? construct.call(context, val, oldVal) : val;
+ }
+ },
+
+ serialize: {
+ "default": function(val, type) {
+ return isObject(val) && val.serialize ? val.serialize() : val;
+ },
+ "date": function(val) {
+ return val && val.getTime()
+ }
+ }
+ });
+
+ // overwrite setup to do this stuff
+ var oldSetup = clss.setup;
+
+
+ clss.setup = function(superClass, stat, proto) {
+ var self = this;
+ oldSetup.call(self, superClass, stat, proto);
+
+ can.each(["attributes"], function(name) {
+ if (!self[name] || superClass[name] === self[name]) {
+ self[name] = {};
+ }
+ });
+
+ can.each(["convert", "serialize"], function(name) {
+ if (superClass[name] != self[name]) {
+ self[name] = can.extend({}, superClass[name], self[name]);
+ }
+ });
+ };
+ });
+
+ var oldSetup = can.Observe.prototype.setup;
+
+ can.Observe.prototype.setup = function(obj) {
+
+ var diff = {};
+
+ oldSetup.call(this, obj);
+
+ can.each(this.constructor.defaults, function(value, key) {
+ if (!this.hasOwnProperty(key)) {
+ diff[key] = value;
+ }
+ }, this);
+
+ this._init = 1;
+ this.attr(diff);
+ delete this._init;
+ };
+
+ can.Observe.prototype.__convert = function(prop, value) {
+ // check if there is a
+
+ var Class = this.constructor,
+ oldVal = this.attr(prop),
+ type, converter;
+
+ if (Class.attributes) {
+ // the type of the attribute
+ type = Class.attributes[prop];
+ converter = Class.convert[type] || Class.convert['default'];
+ }
+
+ return value === null || !type ?
+ // just use the value
+ value :
+ // otherwise, pass to the converter
+ converter.call(Class, value, oldVal, function() {}, type);
+ };
+
+ can.Observe.prototype.serialize = function(attrName, stack) {
+ var where = {},
+ Class = this.constructor,
+ attrs = {};
+
+ stack = can.isArray(stack) ? stack : [];
+ stack.push(this._cid);
+
+ if (attrName !== undefined) {
+ attrs[attrName] = this[attrName];
+ } else {
+ attrs = this.__get();
+ }
+
+ can.each(attrs, function(val, name) {
+ var type, converter;
+
+ // If this is an observe, check that it wasn't serialized earlier in the stack.
+ if (val instanceof can.Observe && can.inArray(val._cid, stack) > -1) {
+ // Since this object has already been serialized once,
+ // just reference the id (or undefined if it doesn't exist).
+ where[name] = val.attr('id');
+ } else {
+ type = Class.attributes ? Class.attributes[name] : 0;
+ converter = Class.serialize ? Class.serialize[type] : 0;
+
+ // if the value is an object, and has a attrs or serialize function
+ where[name] = val && typeof val.serialize == 'function' ?
+ // call attrs or serialize to get the original data back
+ val.serialize(undefined, stack) :
+ // otherwise if we have a converter
+ converter ?
+ // use the converter
+ converter(val, type) :
+ // or return the val
+ val;
+ }
+ });
+
+ return attrName != undefined ? where[attrName] : where;
+ };
+ return can.Observe;
+ })(can);
\ No newline at end of file
Added: tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.observe.backup.js
===================================================================
--- tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.observe.backup.js (rev 0)
+++ tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.observe.backup.js 2013-06-17 15:45:26 UTC (rev 92)
@@ -0,0 +1,52 @@
+/*!
+ * CanJS - 1.1.6
+ * http://canjs.us/
+ * Copyright (c) 2013 Bitovi
+ * Wed, 29 May 2013 18:59:29 GMT
+ * Licensed MIT
+ * Includes: can/observe/backup
+ * Download from: http://canjs.com
+ */
+(function(can) {
+ var flatProps = function(a) {
+ var obj = {};
+ for (var prop in a) {
+ if (typeof a[prop] !== 'object' || a[prop] === null || a[prop] instanceof Date) {
+ obj[prop] = a[prop]
+ }
+ }
+ return obj;
+ };
+
+ can.extend(can.Observe.prototype, {
+
+
+ backup: function() {
+ this._backupStore = this._attrs();
+ return this;
+ },
+
+
+ isDirty: function(checkAssociations) {
+ return this._backupStore && !can.Object.same(this._attrs(),
+ this._backupStore,
+ undefined,
+ undefined,
+ undefined, !! checkAssociations);
+ },
+
+
+ restore: function(restoreAssociations) {
+ var props = restoreAssociations ? this._backupStore : flatProps(this._backupStore)
+
+ if (this.isDirty(restoreAssociations)) {
+ this._attrs(props);
+ }
+
+ return this;
+ }
+
+ })
+
+ return can.Observe;
+ })(can);
\ No newline at end of file
Added: tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.observe.delegate.js
===================================================================
--- tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.observe.delegate.js (rev 0)
+++ tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.observe.delegate.js 2013-06-17 15:45:26 UTC (rev 92)
@@ -0,0 +1,215 @@
+/*!
+ * CanJS - 1.1.6
+ * http://canjs.us/
+ * Copyright (c) 2013 Bitovi
+ * Wed, 29 May 2013 18:59:29 GMT
+ * Licensed MIT
+ * Includes: can/observe/delegate
+ * Download from: http://canjs.com
+ */
+(function(can) {
+
+
+
+ // ** - 'this' will be the deepest item changed
+ // * - 'this' will be any changes within *, but * will be the
+ // this returned
+
+ // tells if the parts part of a delegate matches the broken up props of the event
+ // gives the prop to use as 'this'
+ // - parts - the attribute name of the delegate split in parts ['foo','*']
+ // - props - the split props of the event that happened ['foo','bar','0']
+ // - returns - the attribute to delegate too ('foo.bar'), or null if not a match
+ var delegateMatches = function(parts, props) {
+ //check props parts are the same or
+ var len = parts.length,
+ i = 0,
+ // keeps the matched props we will use
+ matchedProps = [],
+ prop;
+
+ // if the event matches
+ for (i; i < len; i++) {
+ prop = props[i]
+ // if no more props (but we should be matching them)
+ // return null
+ if (typeof prop !== 'string') {
+ return null;
+ } else
+ // if we have a "**", match everything
+ if (parts[i] == "**") {
+ return props.join(".");
+ } else
+ // a match, but we want to delegate to "*"
+ if (parts[i] == "*") {
+ // only do this if there is nothing after ...
+ matchedProps.push(prop);
+ } else if (prop === parts[i]) {
+ matchedProps.push(prop);
+ } else {
+ return null;
+ }
+ }
+ return matchedProps.join(".");
+ },
+ // gets a change event and tries to figure out which
+ // delegates to call
+ delegateHandler = function(event, prop, how, newVal, oldVal) {
+ // pre-split properties to save some regexp time
+ var props = prop.split("."),
+ delegates = (this._observe_delegates || []).slice(0),
+ delegate,
+ attr,
+ matchedAttr,
+ hasMatch,
+ valuesEqual;
+ event.attr = prop;
+ event.lastAttr = props[props.length - 1];
+
+ // for each delegate
+ for (var i = 0; delegate = delegates[i++];) {
+
+ // if there is a batchNum, this means that this
+ // event is part of a series of events caused by a single
+ // attrs call. We don't want to issue the same event
+ // multiple times
+ // setting the batchNum happens later
+ if ((event.batchNum && delegate.batchNum === event.batchNum) || delegate.undelegated) {
+ continue;
+ }
+
+ // reset match and values tests
+ hasMatch = undefined;
+ valuesEqual = true;
+
+ // yeah, all this under here has to be redone v
+
+ // for each attr in a delegate
+ for (var a = 0; a < delegate.attrs.length; a++) {
+
+ attr = delegate.attrs[a];
+
+ // check if it is a match
+ if (matchedAttr = delegateMatches(attr.parts, props)) {
+ hasMatch = matchedAttr;
+ }
+ // if it has a value, make sure it's the right value
+ // if it's set, we should probably check that it has a
+ // value no matter what
+ if (attr.value && valuesEqual) {
+ valuesEqual = attr.value === "" + this.attr(attr.attr)
+ } else if (valuesEqual && delegate.attrs.length > 1) {
+ // if there are multiple attributes, each has to at
+ // least have some value
+ valuesEqual = this.attr(attr.attr) !== undefined
+ }
+ }
+
+
+ // if there is a match and valuesEqual ... call back
+ if (hasMatch && valuesEqual) {
+ // how to get to the changed property from the delegate
+ var from = prop.replace(hasMatch + ".", "");
+
+ // if this event is part of a batch, set it on the delegate
+ // to only send one event
+ if (event.batchNum) {
+ delegate.batchNum = event.batchNum
+ }
+
+ // if we listen to change, fire those with the same attrs
+ // TODO: the attrs should probably be using from
+ if (delegate.event === 'change') {
+ arguments[1] = from;
+ event.curAttr = hasMatch;
+ delegate.callback.apply(this.attr(hasMatch), can.makeArray(arguments));
+ } else if (delegate.event === how) {
+
+ // if it's a match, callback with the location of the match
+ delegate.callback.apply(this.attr(hasMatch), [event, newVal, oldVal, from]);
+ } else if (delegate.event === 'set' &&
+ how == 'add') {
+ // if we are listening to set, we should also listen to add
+ delegate.callback.apply(this.attr(hasMatch), [event, newVal, oldVal, from]);
+ }
+ }
+
+ }
+ };
+
+ can.extend(can.Observe.prototype, {
+
+ delegate: function(selector, event, handler) {
+ selector = can.trim(selector);
+ var delegates = this._observe_delegates || (this._observe_delegates = []),
+ attrs = [],
+ selectorRegex = /([^\s=,]+)(?:=("[^",]*"|'[^',]*'|[^\s"',]*))?(,?)\s*/g,
+ matches;
+
+ // parse each property in the selector
+ while (matches = selectorRegex.exec(selector)) {
+ // we need to do a little doctoring to make up for the quotes.
+ if (matches[2] && can.inArray(matches[2].substr(0, 1), ['"', "'"]) >= 0) {
+ matches[2] = matches[2].substr(1, -1);
+ }
+
+ attrs.push({
+ // the attribute name
+ attr: matches[1],
+ // the attribute name, pre-split for speed
+ parts: matches[1].split('.'),
+ // the value associated with this property (if there was one given)
+ value: matches[2],
+ // whether this selector combines with the one after it with AND or OR
+ or: matches[3] === ','
+ });
+ }
+
+ // delegates has pre-processed info about the event
+ delegates.push({
+ // the attrs name for unbinding
+ selector: selector,
+ // an object of attribute names and values {type: 'recipe',id: undefined}
+ // undefined means a value was not defined
+ attrs: attrs,
+ callback: handler,
+ event: event
+ });
+ if (delegates.length === 1) {
+ this.bind("change", delegateHandler)
+ }
+ return this;
+ },
+
+ undelegate: function(selector, event, handler) {
+ selector = selector && can.trim(selector);
+
+ var i = 0,
+ delegates = this._observe_delegates || [],
+ delegateOb;
+ if (selector) {
+ while (i < delegates.length) {
+ delegateOb = delegates[i];
+ if (delegateOb.callback === handler ||
+ (!handler && delegateOb.selector === selector)) {
+ delegateOb.undelegated = true;
+ delegates.splice(i, 1)
+ } else {
+ i++;
+ }
+ }
+ } else {
+ // remove all delegates
+ delegates = [];
+ }
+ if (!delegates.length) {
+ //can.removeData(this, "_observe_delegates");
+ this.unbind("change", delegateHandler)
+ }
+ return this;
+ }
+ });
+ // add helpers for testing ..
+ can.Observe.prototype.delegate.matches = delegateMatches;
+ return can.Observe;
+ })(can);
\ No newline at end of file
Added: tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.observe.setter.js
===================================================================
--- tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.observe.setter.js (rev 0)
+++ tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.observe.setter.js 2013-06-17 15:45:26 UTC (rev 92)
@@ -0,0 +1,63 @@
+/*!
+ * CanJS - 1.1.6
+ * http://canjs.us/
+ * Copyright (c) 2013 Bitovi
+ * Wed, 29 May 2013 18:59:29 GMT
+ * Licensed MIT
+ * Includes: can/observe/setter
+ * Download from: http://canjs.com
+ */
+(function(can) {
+
+ can.classize = function(s, join) {
+ // this can be moved out ..
+ // used for getter setter
+ var parts = s.split(can.undHash),
+ i = 0;
+ for (; i < parts.length; i++) {
+ parts[i] = can.capitalize(parts[i]);
+ }
+
+ return parts.join(join || '');
+ }
+
+ var classize = can.classize,
+ proto = can.Observe.prototype,
+ old = proto.__set;
+
+ proto.__set = function(prop, value, current, success, error) {
+ // check if there's a setter
+ var cap = classize(prop),
+ setName = "set" + cap,
+ errorCallback = function(errors) {
+ var stub = error && error.call(self, errors);
+
+ // if 'setter' is on the page it will trigger
+ // the error itself and we dont want to trigger
+ // the event twice. :)
+ if (stub !== false) {
+ can.trigger(self, "error", [prop, errors], true);
+ }
+
+ return false;
+ },
+ self = this;
+
+ // if we have a setter
+ if (this[setName] &&
+ // call the setter, if returned value is undefined,
+ // this means the setter is async so we
+ // do not call update property and return right away
+ (value = this[setName](value, function(value) {
+ old.call(self, prop, value, current, success, errorCallback)
+ },
+ errorCallback)) === undefined) {
+ return;
+ }
+
+ old.call(self, prop, value, current, success, errorCallback);
+
+ return this;
+ };
+ return can.Observe;
+ })(can);
\ No newline at end of file
Added: tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.observe.validations.js
===================================================================
--- tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.observe.validations.js (rev 0)
+++ tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.observe.validations.js 2013-06-17 15:45:26 UTC (rev 92)
@@ -0,0 +1,219 @@
+/*!
+ * CanJS - 1.1.6
+ * http://canjs.us/
+ * Copyright (c) 2013 Bitovi
+ * Wed, 29 May 2013 18:59:29 GMT
+ * Licensed MIT
+ * Includes: can/observe/validations
+ * Download from: http://canjs.com
+ */
+(function(can) {
+ //validations object is by property. You can have validations that
+ //span properties, but this way we know which ones to run.
+ // proc should return true if there's an error or the error message
+ var validate = function(attrNames, options, proc) {
+ // normalize argumetns
+ if (!proc) {
+ proc = options;
+ options = {};
+ }
+
+ options = options || {};
+ attrNames = typeof attrNames == 'string' ? [attrNames] : can.makeArray(attrNames);
+
+ // run testIf if it exists
+ if (options.testIf && !options.testIf.call(this)) {
+ return;
+ }
+
+ var self = this;
+ can.each(attrNames, function(attrName) {
+ // Add a test function for each attribute
+ if (!self.validations[attrName]) {
+ self.validations[attrName] = [];
+ }
+
+ self.validations[attrName].push(function(newVal) {
+ // if options has a message return that, otherwise, return the error
+ var res = proc.call(this, newVal, attrName);
+ return res === undefined ? undefined : (options.message || res);
+ })
+ });
+ };
+
+ var old = can.Observe.prototype.__set;
+ can.Observe.prototype.__set = function(prop, value, current, success, error) {
+ var self = this,
+ validations = self.constructor.validations,
+ errorCallback = function(errors) {
+ var stub = error && error.call(self, errors);
+
+ // if 'setter' is on the page it will trigger
+ // the error itself and we dont want to trigger
+ // the event twice. :)
+ if (stub !== false) {
+ can.trigger(self, "error", [prop, errors], true);
+ }
+
+ return false;
+ };
+
+ old.call(self, prop, value, current, success, errorCallback);
+
+ if (validations && validations[prop]) {
+ var errors = self.errors(prop);
+ errors && errorCallback(errors)
+ }
+
+ return this;
+ }
+
+ can.each([can.Observe, can.Model], function(clss) {
+ // in some cases model might not be defined quite yet.
+ if (clss === undefined) {
+ return;
+ }
+ var oldSetup = clss.setup;
+
+
+ can.extend(clss, {
+ setup: function(superClass) {
+ oldSetup.apply(this, arguments);
+ if (!this.validations || superClass.validations === this.validations) {
+ this.validations = {};
+ }
+ },
+
+ validate: validate,
+
+
+ validationMessages: {
+ format: "is invalid",
+ inclusion: "is not a valid option (perhaps out of range)",
+ lengthShort: "is too short",
+ lengthLong: "is too long",
+ presence: "can't be empty",
+ range: "is out of range",
+ numericality: "must be a number"
+ },
+
+
+ validateFormatOf: function(attrNames, regexp, options) {
+ validate.call(this, attrNames, options, function(value) {
+ if ((typeof value !== 'undefined' && value !== null && value !== '') && String(value).match(regexp) == null) {
+ return this.constructor.validationMessages.format;
+ }
+ });
+ },
+
+
+ validateInclusionOf: function(attrNames, inArray, options) {
+ validate.call(this, attrNames, options, function(value) {
+ if (typeof value == 'undefined') {
+ return;
+ }
+
+ for (var i = 0; i < inArray.length; i++) {
+ if (inArray[i] == value) {
+ return;
+ }
+ }
+
+ return this.constructor.validationMessages.inclusion;
+ });
+ },
+
+
+ validateLengthOf: function(attrNames, min, max, options) {
+ validate.call(this, attrNames, options, function(value) {
+ if (((typeof value === 'undefined' || value === null) && min > 0) ||
+ (typeof value !== 'undefined' && value !== null && value.length < min)) {
+ return this.constructor.validationMessages.lengthShort + " (min=" + min + ")";
+ } else if (typeof value != 'undefined' && value !== null && value.length > max) {
+ return this.constructor.validationMessages.lengthLong + " (max=" + max + ")";
+ }
+ });
+ },
+
+
+ validatePresenceOf: function(attrNames, options) {
+ validate.call(this, attrNames, options, function(value) {
+ if (typeof value == 'undefined' || value === "" || value === null) {
+ return this.constructor.validationMessages.presence;
+ }
+ });
+ },
+
+
+ validateRangeOf: function(attrNames, low, hi, options) {
+ validate.call(this, attrNames, options, function(value) {
+ if (((typeof value == 'undefined' || value === null) && low > 0) ||
+ (typeof value !== 'undefined' && value !== null && (value < low || value > hi))) {
+ return this.constructor.validationMessages.range + " [" + low + "," + hi + "]";
+ }
+ });
+ },
+
+
+ validatesNumericalityOf: function(attrNames) {
+ validate.call(this, attrNames, function(value) {
+ var res = !isNaN(parseFloat(value)) && isFinite(value);
+ if (!res) {
+ return this.constructor.validationMessages.numericality;
+ }
+ });
+ }
+ });
+ });
+
+ can.extend(can.Observe.prototype, {
+
+
+ errors: function(attrs, newVal) {
+ // convert attrs to an array
+ if (attrs) {
+ attrs = can.isArray(attrs) ? attrs : [attrs];
+ }
+
+ var errors = {},
+ self = this,
+ attr,
+ // helper function that adds error messages to errors object
+ // attr - the name of the attribute
+ // funcs - the validation functions
+ addErrors = function(attr, funcs) {
+ can.each(funcs, function(func) {
+ var res = func.call(self, isTest ? (self.__convert ?
+ self.__convert(attr, newVal) :
+ newVal) : self[attr]);
+ if (res) {
+ if (!errors[attr]) {
+ errors[attr] = [];
+ }
+ errors[attr].push(res);
+ }
+
+ });
+ },
+ validations = this.constructor.validations,
+ isTest = attrs && attrs.length === 1 && arguments.length === 2;
+
+ // go through each attribute or validation and
+ // add any errors
+ can.each(attrs || validations || {}, function(funcs, attr) {
+ // if we are iterating through an array, use funcs
+ // as the attr name
+ if (typeof attr == 'number') {
+ attr = funcs;
+ funcs = validations[attr];
+ }
+ // add errors to the
+ addErrors(attr, funcs || []);
+ });
+
+ // return errors as long as we have one
+ return can.isEmptyObject(errors) ? null : isTest ? errors[attrs[0]] : errors;
+ }
+ });
+ return can.Observe;
+ })(can);
\ No newline at end of file
Added: tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.util.string.js
===================================================================
--- tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.util.string.js (rev 0)
+++ tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.util.string.js 2013-06-17 15:45:26 UTC (rev 92)
@@ -0,0 +1,163 @@
+/*!
+ * CanJS - 1.1.6
+ * http://canjs.us/
+ * Copyright (c) 2013 Bitovi
+ * Wed, 29 May 2013 18:59:29 GMT
+ * Licensed MIT
+ * Includes: can/util/string
+ * Download from: http://canjs.com
+ */
+(function(can) {
+ // ##string.js
+ // _Miscellaneous string utility functions._
+
+ // Several of the methods in this plugin use code adapated from Prototype
+ // Prototype JavaScript framework, version 1.6.0.1.
+ // © 2005-2007 Sam Stephenson
+ var strUndHash = /_|-/,
+ strColons = /\=\=/,
+ strWords = /([A-Z]+)([A-Z][a-z])/g,
+ strLowUp = /([a-z\d])([A-Z])/g,
+ strDash = /([a-z\d])([A-Z])/g,
+ strReplacer = /\{([^\}]+)\}/g,
+ strQuote = /"/g,
+ strSingleQuote = /'/g,
+
+ // Returns the `prop` property from `obj`.
+ // If `add` is true and `prop` doesn't exist in `obj`, create it as an
+ // empty object.
+ getNext = function(obj, prop, add) {
+ var result = obj[prop];
+
+ if (result === undefined && add === true) {
+ result = obj[prop] = {}
+ }
+ return result
+ },
+
+ // Returns `true` if the object can have properties (no `null`s).
+ isContainer = function(current) {
+ return (/^f|^o/).test(typeof current);
+ };
+
+ can.extend(can, {
+ // Escapes strings for HTML.
+
+ esc: function(content) {
+ // Convert bad values into empty strings
+ var isInvalid = content === null || content === undefined || (isNaN(content) && ("" + content === 'NaN'));
+ return ("" + (isInvalid ? '' : content))
+ .replace(/&/g, '&')
+ .replace(/</g, '<')
+ .replace(/>/g, '>')
+ .replace(strQuote, '"')
+ .replace(strSingleQuote, "'");
+ },
+
+
+ getObject: function(name, roots, add) {
+
+ // The parts of the name we are looking up
+ // `['App','Models','Recipe']`
+ var parts = name ? name.split('.') : [],
+ length = parts.length,
+ current,
+ r = 0,
+ i, container, rootsLength;
+
+ // Make sure roots is an `array`.
+ roots = can.isArray(roots) ? roots : [roots || window];
+
+ rootsLength = roots.length
+
+ if (!length) {
+ return roots[0];
+ }
+
+ // For each root, mark it as current.
+ for (r; r < rootsLength; r++) {
+ current = roots[r];
+ container = undefined;
+
+ // Walk current to the 2nd to last object or until there
+ // is not a container.
+ for (i = 0; i < length && isContainer(current); i++) {
+ container = current;
+ current = getNext(container, parts[i]);
+ }
+
+ // If we found property break cycle
+ if (container !== undefined && current !== undefined) {
+ break
+ }
+ }
+
+ // Remove property from found container
+ if (add === false && current !== undefined) {
+ delete container[parts[i - 1]]
+ }
+
+ // When adding property add it to the first root
+ if (add === true && current === undefined) {
+ current = roots[0]
+
+ for (i = 0; i < length && isContainer(current); i++) {
+ current = getNext(current, parts[i], true);
+ }
+ }
+
+ return current;
+ },
+ // Capitalizes a string.
+
+ capitalize: function(s, cache) {
+ // Used to make newId.
+ return s.charAt(0).toUpperCase() + s.slice(1);
+ },
+
+ // Underscores a string.
+
+ underscore: function(s) {
+ return s
+ .replace(strColons, '/')
+ .replace(strWords, '$1_$2')
+ .replace(strLowUp, '$1_$2')
+ .replace(strDash, '_')
+ .toLowerCase();
+ },
+ // Micro-templating.
+
+ sub: function(str, data, remove) {
+ var obs = [];
+
+ str = str || '';
+
+ obs.push(str.replace(strReplacer, function(whole, inside) {
+
+ // Convert inside to type.
+ var ob = can.getObject(inside, data, remove === true ? false : undefined);
+
+ if (ob === undefined) {
+ obs = null;
+ return "";
+ }
+
+ // If a container, push into objs (which will return objects found).
+ if (isContainer(ob) && obs) {
+ obs.push(ob);
+ return "";
+ }
+
+ return "" + ob;
+ }));
+
+ return obs === null ? obs : (obs.length <= 1 ? obs[0] : obs);
+ },
+
+ // These regex's are used throughout the rest of can, so let's make
+ // them available.
+ replacer: strReplacer,
+ undHash: strUndHash
+ });
+ return can;
+ })(can);
\ No newline at end of file
Added: tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.view.modifiers.js
===================================================================
--- tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.view.modifiers.js (rev 0)
+++ tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.view.modifiers.js 2013-06-17 15:45:26 UTC (rev 92)
@@ -0,0 +1,164 @@
+/*!
+ * CanJS - 1.1.6
+ * http://canjs.us/
+ * Copyright (c) 2013 Bitovi
+ * Wed, 29 May 2013 18:59:29 GMT
+ * Licensed MIT
+ * Includes: can/view/modifiers
+ * Download from: http://canjs.com
+ */
+(function($, can) {
+ //---- ADD jQUERY HELPERS -----
+ //converts jquery functions to use views
+ var convert, modify, isTemplate, isHTML, isDOM, getCallback,
+ // text and val cannot produce an element, so don't run hookups on them
+ noHookup = {
+ 'val': true,
+ 'text': true
+ };
+
+ convert = function(func_name) {
+ // save the old jQuery helper
+ var old = $.fn[func_name];
+
+ // replace it with our new helper
+ $.fn[func_name] = function() {
+
+ var args = can.makeArray(arguments),
+ callbackNum,
+ callback,
+ self = this,
+ result;
+
+ // if the first arg is a deferred
+ // wait until it finishes, and call
+ // modify with the result
+ if (can.isDeferred(args[0])) {
+ args[0].done(function(res) {
+ modify.call(self, [res], old);
+ })
+ return this;
+ }
+ //check if a template
+ else if (isTemplate(args)) {
+
+ // if we should operate async
+ if ((callbackNum = getCallback(args))) {
+ callback = args[callbackNum];
+ args[callbackNum] = function(result) {
+ modify.call(self, [result], old);
+ callback.call(self, result);
+ };
+ can.view.apply(can.view, args);
+ return this;
+ }
+ // call view with args (there might be deferreds)
+ result = can.view.apply(can.view, args);
+
+ // if we got a string back
+ if (!can.isDeferred(result)) {
+ // we are going to call the old method with that string
+ args = [result];
+ } else {
+ // if there is a deferred, wait until it is done before calling modify
+ result.done(function(res) {
+ modify.call(self, [res], old);
+ })
+ return this;
+ }
+ }
+ return noHookup[func_name] ? old.apply(this, args) :
+ modify.call(this, args, old);
+ };
+ };
+
+ // modifies the content of the element
+ // but also will run any hookup
+ modify = function(args, old) {
+ var res, stub, hooks;
+
+ //check if there are new hookups
+ for (var hasHookups in can.view.hookups) {
+ break;
+ }
+
+ //if there are hookups, turn into a frag
+ // and insert that
+ // by using a frag, the element can be recursively hooked up
+ // before insterion
+ if (hasHookups && args[0] && isHTML(args[0])) {
+ args[0] = can.view.frag(args[0]).childNodes;
+ }
+
+ //then insert into DOM
+ res = old.apply(this, args);
+
+ return res;
+ };
+
+ // returns true or false if the args indicate a template is being used
+ // $('#foo').html('/path/to/template.ejs',{data})
+ // in general, we want to make sure the first arg is a string
+ // and the second arg is data
+ isTemplate = function(args) {
+ // save the second arg type
+ var secArgType = typeof args[1];
+
+ // the first arg is a string
+ return typeof args[0] == "string" &&
+ // the second arg is an object or function
+ (secArgType == 'object' || secArgType == 'function') &&
+ // but it is not a dom element
+ !isDOM(args[1]);
+ };
+ // returns true if the arg is a jQuery object or HTMLElement
+ isDOM = function(arg) {
+ return arg.nodeType || (arg[0] && arg[0].nodeType)
+ };
+ // returns whether the argument is some sort of HTML data
+ isHTML = function(arg) {
+ if (isDOM(arg)) {
+ // if jQuery object or DOM node we're good
+ return true;
+ } else if (typeof arg === "string") {
+ // if string, do a quick sanity check that we're HTML
+ arg = can.trim(arg);
+ return arg.substr(0, 1) === "<" && arg.substr(arg.length - 1, 1) === ">" && arg.length >= 3;
+ } else {
+ // don't know what you are
+ return false;
+ }
+ };
+
+ //returns the callback arg number if there is one (for async view use)
+ getCallback = function(args) {
+ return typeof args[3] === 'function' ? 3 : typeof args[2] === 'function' && 2;
+ };
+
+
+ $.fn.hookup = function() {
+ can.view.frag(this);
+ return this;
+ };
+
+ can.each([
+
+ "prepend",
+
+ "append",
+
+ "after",
+
+ "before",
+
+ "text",
+
+ "html",
+
+ "replaceWith", "val"
+ ], function(func) {
+ convert(func);
+ });
+
+ return can;
+ })(jQuery, can);
\ No newline at end of file
Added: tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.view.mustache.js
===================================================================
--- tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.view.mustache.js (rev 0)
+++ tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.view.mustache.js 2013-06-17 15:45:26 UTC (rev 92)
@@ -0,0 +1,897 @@
+/*!
+ * CanJS - 1.1.6
+ * http://canjs.us/
+ * Copyright (c) 2013 Bitovi
+ * Wed, 29 May 2013 18:59:28 GMT
+ * Licensed MIT
+ * Includes: can/view/mustache
+ * Download from: http://canjs.com
+ */
+(function(can) {
+
+ // # mustache.js
+ // `can.Mustache`: The Mustache templating engine.
+ // See the [Transformation](#section-29) section within *Scanning Helpers* for a detailed explanation
+ // of the runtime render code design. The majority of the Mustache engine implementation
+ // occurs within the *Transformation* scanning helper.
+
+ // ## Initialization
+ // Define the view extension.
+ can.view.ext = ".mustache";
+
+ // ### Setup internal helper variables and functions.
+ // An alias for the context variable used for tracking a stack of contexts.
+ // This is also used for passing to helper functions to maintain proper context.
+ var CONTEXT = '___c0nt3xt',
+ // An alias for the variable used for the hash object that can be passed
+ // to helpers via `options.hash`.
+ HASH = '___h4sh',
+ // An alias for the function that adds a new context to the context stack.
+ STACK = '___st4ck',
+ STACKED = '___st4ck3d',
+ // An alias for the most used context stacking call.
+ CONTEXT_STACK = STACK + '(' + CONTEXT + ',this)',
+ CONTEXT_OBJ = '{context:' + CONTEXT_STACK + ',options:options}',
+
+
+ isObserve = function(obj) {
+ return obj !== null && can.isFunction(obj.attr) && obj.constructor && !! obj.constructor.canMakeObserve;
+ },
+
+
+ isArrayLike = function(obj) {
+ return obj && obj.splice && typeof obj.length == 'number';
+ },
+
+ // ## Mustache
+
+ Mustache = function(options, helpers) {
+ // Support calling Mustache without the constructor.
+ // This returns a function that renders the template.
+ if (this.constructor != Mustache) {
+ var mustache = new Mustache(options);
+ return function(data, options) {
+ return mustache.render(data, options);
+ };
+ }
+
+ // If we get a `function` directly, it probably is coming from
+ // a `steal`-packaged view.
+ if (typeof options == "function") {
+ this.template = {
+ fn: options
+ };
+ return;
+ }
+
+ // Set options on self.
+ can.extend(this, options);
+ this.template = this.scanner.scan(this.text, this.name);
+ };
+
+
+ // Put Mustache on the `can` object.
+ can.Mustache = window.Mustache = Mustache;
+
+
+ Mustache.prototype.
+
+ render = function(object, options) {
+ object = object || {};
+ options = options || {};
+ if (!options.helpers && !options.partials) {
+ options.helpers = options;
+ }
+ return this.template.fn.call(object, object, {
+ _data: object,
+ options: options
+ });
+ };
+
+ can.extend(Mustache.prototype, {
+ // Share a singleton scanner for parsing templates.
+ scanner: new can.view.Scanner({
+ // A hash of strings for the scanner to inject at certain points.
+ text: {
+ // This is the logic to inject at the beginning of a rendered template.
+ // This includes initializing the `context` stack.
+ start: 'var ' + CONTEXT + ' = this && this.' + STACKED + ' ? this : [];' + CONTEXT + '.' + STACKED + ' = true;' + 'var ' + STACK + ' = function(context, self) {' + 'var s;' + 'if (arguments.length == 1 && context) {' + 's = !context.' + STACKED + ' ? [context] : context;' +
+ // Handle helpers with custom contexts (#228)
+ '} else if (!context.' + STACKED + ') {' + 's = [self, context];' + '} else if (context && context === self && context.' + STACKED + ') {' + 's = context.slice(0);' + '} else {' + 's = context && context.' + STACKED + ' ? context.concat([self]) : ' + STACK + '(context).concat([self]);' + '}' + 'return (s.' + STACKED + ' = true) && s;' + '};'
+ },
+
+ // An ordered token registry for the scanner.
+ // This needs to be ordered by priority to prevent token parsing errors.
+ // Each token follows the following structure:
+ // [
+ // // Which key in the token map to match.
+ // "tokenMapName",
+ // // A simple token to match, like "{{".
+ // "token",
+ // // Optional. A complex (regexp) token to match that
+ // // overrides the simple token.
+ // "[\\s\\t]*{{",
+ // // Optional. A function that executes advanced
+ // // manipulation of the matched content. This is
+ // // rarely used.
+ // function(content){
+ // return content;
+ // }
+ // ]
+ tokens: [
+ // Return unescaped
+ ["returnLeft", "{{{", "{{[{&]"],
+ // Full line comments
+ ["commentFull", "{{!}}", "^[\\s\\t]*{{!.+?}}\\n"],
+ // Inline comments
+ ["commentLeft", "{{!", "(\\n[\\s\\t]*{{!|{{!)"],
+ // Full line escapes
+ // This is used for detecting lines with only whitespace and an escaped tag
+ ["escapeFull", "{{}}", "(^[\\s\\t]*{{[#/^][^}]+?}}\\n|\\n[\\s\\t]*{{[#/^][^}]+?}}\\n|\\n[\\s\\t]*{{[#/^][^}]+?}}$)",
+ function(content) {
+ return {
+ before: /^\n.+?\n$/.test(content) ? '\n' : '',
+ content: content.match(/\{\{(.+?)\}\}/)[1] || ''
+ };
+ }
+ ],
+ // Return escaped
+ ["escapeLeft", "{{"],
+ // Close return unescaped
+ ["returnRight", "}}}"],
+ // Close tag
+ ["right", "}}"]
+ ],
+
+ // ## Scanning Helpers
+ // This is an array of helpers that transform content that is within escaped tags like `{{token}}`. These helpers are solely for the scanning phase; they are unrelated to Mustache/Handlebars helpers which execute at render time. Each helper has a definition like the following:
+ // {
+ // // The content pattern to match in order to execute.
+ // // Only the first matching helper is executed.
+ // name: /pattern to match/,
+ // // The function to transform the content with.
+ // // @param {String} content The content to transform.
+ // // @param {Object} cmd Scanner helper data.
+ // // {
+ // // insert: "insert command",
+ // // tagName: "div",
+ // // status: 0
+ // // }
+ // fn: function(content, cmd) {
+ // return 'for text injection' ||
+ // { raw: 'to bypass text injection' };
+ // }
+ // }
+ helpers: [
+ // ### Partials
+ // Partials begin with a greater than sign, like {{> box}}.
+ // Partials are rendered at runtime (as opposed to compile time),
+ // so recursive partials are possible. Just avoid infinite loops.
+ // For example, this template and partial:
+ // base.mustache:
+ // <h2>Names</h2>
+ // {{#names}}
+ // {{> user}}
+ // {{/names}}
+ // user.mustache:
+ // <strong>{{name}}</strong>
+ {
+ name: /^>[\s]*\w*/,
+ fn: function(content, cmd) {
+ // Get the template name and call back into the render method,
+ // passing the name and the current context.
+ var templateName = can.trim(content.replace(/^>\s?/, '')).replace(/["|']/g, "");
+ return "options.partials && options.partials['" + templateName + "'] ? can.Mustache.renderPartial(options.partials['" + templateName + "']," +
+ CONTEXT_STACK + ",options) : can.Mustache.render('" + templateName + "', " + CONTEXT_STACK + ")";
+ }
+ },
+
+ // ### Data Hookup
+ // This will attach the data property of `this` to the element
+ // its found on using the first argument as the data attribute
+ // key.
+ // For example:
+ // <li id="nameli" {{ data 'name' }}></li>
+ // then later you can access it like:
+ // can.$('#nameli').data('name');
+ {
+ name: /^\s*data\s/,
+ fn: function(content, cmd) {
+ var attr = content.match(/["|'](.*)["|']/)[1];
+ // return a function which calls `can.data` on the element
+ // with the attribute name with the current context.
+ return "can.proxy(function(__){" +
+ // "var context = this[this.length-1];" +
+ // "context = context." + STACKED + " ? context[context.length-2] : context;" +
+ "can.data(can.$(__),'" + attr + "', this.pop()); }, " + CONTEXT_STACK + ")";
+ }
+ },
+
+ // ### Transformation (default)
+ // This transforms all content to its interpolated equivalent,
+ // including calls to the corresponding helpers as applicable.
+ // This outputs the render code for almost all cases.
+ // #### Definitions
+ // * `context` - This is the object that the current rendering context operates within.
+ // Each nested template adds a new `context` to the context stack.
+ // * `stack` - Mustache supports nested sections,
+ // each of which add their own context to a stack of contexts.
+ // Whenever a token gets interpolated, it will check for a match against the
+ // last context in the stack, then iterate through the rest of the stack checking for matches.
+ // The first match is the one that gets returned.
+ // * `Mustache.txt` - This serializes a collection of logic, optionally contained within a section.
+ // If this is a simple interpolation, only the interpolation lookup will be passed.
+ // If this is a section, then an `options` object populated by the truthy (`options.fn`) and
+ // falsey (`options.inverse`) encapsulated functions will also be passed. This section handling
+ // exists to support the runtime context nesting that Mustache supports.
+ // * `Mustache.get` - This resolves an interpolation reference given a stack of contexts.
+ // * `options` - An object containing methods for executing the inner contents of sections or helpers.
+ // `options.fn` - Contains the inner template logic for a truthy section.
+ // `options.inverse` - Contains the inner template logic for a falsey section.
+ // `options.hash` - Contains the merged hash object argument for custom helpers.
+ // #### Design
+ // This covers the design of the render code that the transformation helper generates.
+ // ##### Pseudocode
+ // A detailed explanation is provided in the following sections, but here is some brief pseudocode
+ // that gives a high level overview of what the generated render code does (with a template similar to
+ // `"{{#a}}{{b.c.d.e.name}}{{/a}}" == "Phil"`).
+ // *Initialize the render code.*
+ // view = []
+ // context = []
+ // stack = fn { context.concat([this]) }
+ // *Render the root section.*
+ // view.push( "string" )
+ // view.push( can.view.txt(
+ // *Render the nested section with `can.Mustache.txt`.*
+ // txt(
+ // *Add the current context to the stack.*
+ // stack(),
+ // *Flag this for truthy section mode.*
+ // "#",
+ // *Interpolate and check the `a` variable for truthyness using the stack with `can.Mustache.get`.*
+ // get( "a", stack() ),
+ // *Include the nested section's inner logic.
+ // The stack argument is usually the parent section's copy of the stack,
+ // but it can be an override context that was passed by a custom helper.
+ // Sections can nest `0..n` times -- **NESTCEPTION**.*
+ // { fn: fn(stack) {
+ // *Render the nested section (everything between the `{{#a}}` and `{{/a}}` tokens).*
+ // view = []
+ // view.push( "string" )
+ // view.push(
+ // *Add the current context to the stack.*
+ // stack(),
+ // *Flag this as interpolation-only mode.*
+ // null,
+ // *Interpolate the `b.c.d.e.name` variable using the stack.*
+ // get( "b.c.d.e.name", stack() ),
+ // )
+ // view.push( "string" )
+ // *Return the result for the nested section.*
+ // return view.join()
+ // }}
+ // )
+ // ))
+ // view.push( "string" )
+ // *Return the result for the root section, which includes all nested sections.*
+ // return view.join()
+ // ##### Initialization
+ // Each rendered template is started with the following initialization code:
+ // var ___v1ew = [];
+ // var ___c0nt3xt = [];
+ // ___c0nt3xt.___st4ck = true;
+ // var ___st4ck = function(context, self) {
+ // var s;
+ // if (arguments.length == 1 && context) {
+ // s = !context.___st4ck ? [context] : context;
+ // } else {
+ // s = context && context.___st4ck
+ // ? context.concat([self])
+ // : ___st4ck(context).concat([self]);
+ // }
+ // return (s.___st4ck = true) && s;
+ // };
+ // The `___v1ew` is the the array used to serialize the view.
+ // The `___c0nt3xt` is a stacking array of contexts that slices and expands with each nested section.
+ // The `___st4ck` function is used to more easily update the context stack in certain situations.
+ // Usually, the stack function simply adds a new context (`self`/`this`) to a context stack.
+ // However, custom helpers will occasionally pass override contexts that need their own context stack.
+ // ##### Sections
+ // Each section, `{{#section}} content {{/section}}`, within a Mustache template generates a section
+ // context in the resulting render code. The template itself is treated like a root section, with the
+ // same execution logic as any others. Each section can have `0..n` nested sections within it.
+ // Here's an example of a template without any descendent sections.
+ // Given the template: `"{{a.b.c.d.e.name}}" == "Phil"`
+ // Would output the following render code:
+ // ___v1ew.push("\"");
+ // ___v1ew.push(can.view.txt(1, '', 0, this, function() {
+ // return can.Mustache.txt(___st4ck(___c0nt3xt, this), null,
+ // can.Mustache.get("a.b.c.d.e.name",
+ // ___st4ck(___c0nt3xt, this))
+ // );
+ // }));
+ // ___v1ew.push("\" == \"Phil\"");
+ // The simple strings will get appended to the view. Any interpolated references (like `{{a.b.c.d.e.name}}`)
+ // will be pushed onto the view via `can.view.txt` in order to support live binding.
+ // The function passed to `can.view.txt` will call `can.Mustache.txt`, which serializes the object data by doing
+ // a context lookup with `can.Mustache.get`.
+ // `can.Mustache.txt`'s first argument is a copy of the context stack with the local context `this` added to it.
+ // This stack will grow larger as sections nest.
+ // The second argument is for the section type. This will be `"#"` for truthy sections, `"^"` for falsey,
+ // or `null` if it is an interpolation instead of a section.
+ // The third argument is the interpolated value retrieved with `can.Mustache.get`, which will perform the
+ // context lookup and return the approriate string or object.
+ // Any additional arguments, if they exist, are used for passing arguments to custom helpers.
+ // For nested sections, the last argument is an `options` object that contains the nested section's logic.
+ // Here's an example of a template with a single nested section.
+ // Given the template: `"{{#a}}{{b.c.d.e.name}}{{/a}}" == "Phil"`
+ // Would output the following render code:
+ // ___v1ew.push("\"");
+ // ___v1ew.push(can.view.txt(0, '', 0, this, function() {
+ // return can.Mustache.txt(___st4ck(___c0nt3xt, this), "#",
+ // can.Mustache.get("a", ___st4ck(___c0nt3xt, this)),
+ // [{
+ // _: function() {
+ // return ___v1ew.join("");
+ // }
+ // }, {
+ // fn: function(___c0nt3xt) {
+ // var ___v1ew = [];
+ // ___v1ew.push(can.view.txt(1, '', 0, this,
+ // function() {
+ // return can.Mustache.txt(
+ // ___st4ck(___c0nt3xt, this),
+ // null,
+ // can.Mustache.get("b.c.d.e.name",
+ // ___st4ck(___c0nt3xt, this))
+ // );
+ // }
+ // ));
+ // return ___v1ew.join("");
+ // }
+ // }]
+ // )
+ // }));
+ // ___v1ew.push("\" == \"Phil\"");
+ // This is specified as a truthy section via the `"#"` argument. The last argument includes an array of helper methods used with `options`.
+ // These act similarly to custom helpers: `options.fn` will be called for truthy sections, `options.inverse` will be called for falsey sections.
+ // The `options._` function only exists as a dummy function to make generating the section nesting easier (a section may have a `fn`, `inverse`,
+ // or both, but there isn't any way to determine that at compilation time).
+ // Within the `fn` function is the section's render context, which in this case will render anything between the `{{#a}}` and `{{/a}}` tokens.
+ // This function has `___c0nt3xt` as an argument because custom helpers can pass their own override contexts. For any case where custom helpers
+ // aren't used, `___c0nt3xt` will be equivalent to the `___st4ck(___c0nt3xt, this)` stack created by its parent section. The `inverse` function
+ // works similarly, except that it is added when `{{^a}}` and `{{else}}` are used. `var ___v1ew = []` is specified in `fn` and `inverse` to
+ // ensure that live binding in nested sections works properly.
+ // All of these nested sections will combine to return a compiled string that functions similar to EJS in its uses of `can.view.txt`.
+ // #### Implementation
+ {
+ name: /^.*$/,
+ fn: function(content, cmd) {
+ var mode = false,
+ result = [];
+
+ // Trim the content so we don't have any trailing whitespace.
+ content = can.trim(content);
+
+ // Determine what the active mode is.
+ // * `#` - Truthy section
+ // * `^` - Falsey section
+ // * `/` - Close the prior section
+ // * `else` - Inverted section (only exists within a truthy/falsey section)
+ if (content.length && (mode = content.match(/^([#^/]|else$)/))) {
+ mode = mode[0];
+ switch (mode) {
+ // Open a new section.
+ case '#':
+ case '^':
+ result.push(cmd.insert + 'can.view.txt(0,\'' + cmd.tagName + '\',' + cmd.status + ',this,function(){ return ');
+ break;
+ // Close the prior section.
+ case '/':
+ return {
+ raw: 'return ___v1ew.join("");}}])}));'
+ };
+ break;
+ }
+
+ // Trim the mode off of the content.
+ content = content.substring(1);
+ }
+
+ // `else` helpers are special and should be skipped since they don't
+ // have any logic aside from kicking off an `inverse` function.
+ if (mode != 'else') {
+ var args = [],
+ i = 0,
+ hashing = false,
+ arg, split, m;
+
+ // Parse the helper arguments.
+ // This needs uses this method instead of a split(/\s/) so that
+ // strings with spaces can be correctly parsed.
+ (can.trim(content) + ' ').replace(/((([^\s]+?=)?('.*?'|".*?"))|.*?)\s/g, function(whole, part) {
+ args.push(part);
+ });
+
+ // Start the content render block.
+ result.push('can.Mustache.txt(' + CONTEXT_OBJ + ',' + (mode ? '"' + mode + '"' : 'null') + ',');
+
+ // Iterate through the helper arguments, if there are any.
+ for (; arg = args[i]; i++) {
+ i && result.push(',');
+
+ // Check for special helper arguments (string/number/boolean/hashes).
+ if (i && (m = arg.match(/^(('.*?'|".*?"|[0-9]+\.?[0-9]*|true|false)|((.+?)=(('.*?'|".*?"|[0-9]+\.?[0-9]*|true|false)|(.+))))$/))) {
+ // Found a native type like string/number/boolean.
+ if (m[2]) {
+ result.push(m[0]);
+ }
+ // Found a hash object.
+ else {
+ // Open the hash object.
+ if (!hashing) {
+ hashing = true;
+ result.push('{' + HASH + ':{');
+ }
+
+ // Add the key/value.
+ result.push(m[4], ':', m[6] ? m[6] : 'can.Mustache.get("' + m[5].replace(/"/g, '\\"') + '",' + CONTEXT_OBJ + ')');
+
+ // Close the hash if this was the last argument.
+ if (i == args.length - 1) {
+ result.push('}}');
+ }
+ }
+ }
+ // Otherwise output a normal interpolation reference.
+ else {
+ result.push('can.Mustache.get("' +
+ // Include the reference name.
+ arg.replace(/"/g, '\\"') + '",' +
+ // Then the stack of context.
+ CONTEXT_OBJ +
+ // Flag as a helper method to aid performance,
+ // if it is a known helper (anything with > 0 arguments).
+ (i == 0 && args.length > 1 ? ',true' : ',false') +
+ (i > 0 ? ',true' : ',false') +
+ ')');
+ }
+ }
+ }
+
+ // Create an option object for sections of code.
+ mode && mode != 'else' && result.push(',[{_:function(){');
+ switch (mode) {
+ // Truthy section
+ case '#':
+ result.push('return ___v1ew.join("");}},{fn:function(' + CONTEXT + '){var ___v1ew = [];');
+ break;
+ // If/else section
+ // Falsey section
+ case 'else':
+ case '^':
+ result.push('return ___v1ew.join("");}},{inverse:function(' + CONTEXT + '){var ___v1ew = [];');
+ break;
+ // Not a section
+ default:
+ result.push(');');
+ break;
+ }
+
+ // Return a raw result if there was a section, otherwise return the default string.
+ result = result.join('');
+ return mode ? {
+ raw: result
+ } : result;
+ }
+ }
+ ]
+ })
+ });
+
+ // Add in default scanner helpers first.
+ // We could probably do this differently if we didn't 'break' on every match.
+ var helpers = can.view.Scanner.prototype.helpers;
+ for (var i = 0; i < helpers.length; i++) {
+ Mustache.prototype.scanner.helpers.unshift(helpers[i]);
+ };
+
+
+ Mustache.txt = function(context, mode, name) {
+ // Grab the extra arguments to pass to helpers.
+ var args = Array.prototype.slice.call(arguments, 3),
+ // Create a default `options` object to pass to the helper.
+ options = can.extend.apply(can, [{
+ fn: function() {},
+ inverse: function() {}
+ }
+ ].concat(mode ? args.pop() : []));
+
+
+ var extra = {};
+ if (context.context) {
+ extra = context.options;
+ context = context.context;
+ }
+
+ // Check for a registered helper or a helper-like function.
+ if (helper = (Mustache.getHelper(name, extra) || (can.isFunction(name) && !name.isComputed && {
+ fn: name
+ }))) {
+ // Use the most recent context as `this` for the helper.
+ var stack = context[STACKED] && context,
+ context = (stack && context[context.length - 1]) || context,
+ // Update the options with a function/inverse (the inner templates of a section).
+ opts = {
+ fn: can.proxy(options.fn, context),
+ inverse: can.proxy(options.inverse, context)
+ },
+ lastArg = args[args.length - 1];
+
+ // Store the context stack in the options if one exists
+ if (stack) {
+ opts.contexts = stack;
+ }
+ // Add the hash to `options` if one exists
+ if (lastArg && lastArg[HASH]) {
+ opts.hash = args.pop()[HASH];
+ }
+ args.push(opts);
+
+ // Call the helper.
+ return helper.fn.apply(context, args) || '';
+ }
+
+ // if a compute, get the value
+ if (can.isFunction(name) && name.isComputed) {
+ name = name();
+ }
+
+ // An array of arguments to check for truthyness when evaluating sections.
+ var validArgs = args.length ? args : [name],
+ // Whether the arguments meet the condition of the section.
+ valid = true,
+ result = [],
+ i, helper, argIsObserve, arg;
+ // Validate the arguments based on the section mode.
+ if (mode) {
+ for (i = 0; i < validArgs.length; i++) {
+ arg = validArgs[i];
+ argIsObserve = typeof arg !== 'undefined' && isObserve(arg);
+ // Array-like objects are falsey if their length = 0.
+ if (isArrayLike(arg)) {
+ // Use .attr to trigger binding on empty lists returned from function
+ if (mode == '#') {
+ valid = valid && !! (argIsObserve ? arg.attr('length') : arg.length);
+ } else if (mode == '^') {
+ valid = valid && !(argIsObserve ? arg.attr('length') : arg.length);
+ }
+ }
+ // Otherwise just check if it is truthy or not.
+ else {
+ valid = mode == '#' ? valid && !! arg : mode == '^' ? valid && !arg : valid;
+ }
+ }
+ }
+
+ // Otherwise interpolate like normal.
+ if (valid) {
+ switch (mode) {
+ // Truthy section.
+ case '#':
+ // Iterate over arrays
+ if (isArrayLike(name)) {
+ var isObserveList = isObserve(name);
+
+ // Add the reference to the list in the contexts.
+ for (i = 0; i < name.length; i++) {
+ result.push(options.fn.call(name[i], context) || '');
+
+ // Ensure that live update works on observable lists
+ isObserveList && name.attr('' + i);
+ }
+ return result.join('');
+ }
+ // Normal case.
+ else {
+ return options.fn.call(name || {}, context) || '';
+ }
+ break;
+ // Falsey section.
+ case '^':
+ return options.inverse.call(name || {}, context) || '';
+ break;
+ default:
+ // Add + '' to convert things like numbers to strings.
+ // This can cause issues if you are trying to
+ // eval on the length but this is the more
+ // common case.
+ return '' + (name !== undefined ? name : '');
+ break;
+ }
+ }
+
+ return '';
+ };
+
+
+ Mustache.get = function(ref, contexts, isHelper, isArgument) {
+ var options = contexts.options || {};
+ contexts = contexts.context || contexts;
+ // Assume the local object is the last context in the stack.
+ var obj = contexts[contexts.length - 1],
+ // Assume the parent context is the second to last context in the stack.
+ context = contexts[contexts.length - 2],
+ // Split the reference (like `a.b.c`) into an array of key names.
+ names = ref.indexOf('\\.') == -1
+ // Reference doesn't contain escaped periods
+ ? ref.split('.')
+ // Reference contains escaped periods (`a.b\c.foo` == `a["b.c"].foo)
+ : (function() {
+ var names = [],
+ last = 0;
+ ref.replace(/(\\)?\./g, function($0, $1, index) {
+ if (!$1) {
+ names.push(ref.slice(last, index).replace(/\\\./g, '.'));
+ last = index + $0.length;
+ }
+ });
+ names.push(ref.slice(last).replace(/\\\./g, '.'));
+ return names;
+ })(),
+ namesLength = names.length,
+ value, lastValue, name, i, j,
+ // if we walk up and don't find a property, we default
+ // to listening on an undefined property of the first
+ // context that is an observe
+ defaultObserve,
+ defaultObserveName;
+
+ // Handle `this` references for list iteration: {{.}} or {{this}}
+ if (/^\.|this$/.test(ref)) {
+ // If context isn't an object, then it was a value passed by a helper so use it as an override.
+ if (!/^object|undefined$/.test(typeof context)) {
+ return context || '';
+ }
+ // Otherwise just return the closest object.
+ else {
+ while (value = contexts.pop()) {
+ if (typeof value !== 'undefined') {
+ return value;
+ }
+ }
+ return '';
+ }
+ }
+ // Handle object resolution (like `a.b.c`).
+ else if (!isHelper) {
+ // Reverse iterate through the contexts (last in, first out).
+ for (i = contexts.length - 1; i >= 0; i--) {
+ // Check the context for the reference
+ value = contexts[i];
+
+ // Is the value a compute?
+ if (can.isFunction(value) && value.isComputed) {
+ value = value();
+ }
+
+ // Make sure the context isn't a failed object before diving into it.
+ if (typeof value !== 'undefined' && value !== null) {
+ var isHelper = Mustache.getHelper(ref, options);
+ for (j = 0; j < namesLength; j++) {
+ // Keep running up the tree while there are matches.
+ if (typeof value[names[j]] !== 'undefined' && value[names[j]] !== null) {
+ lastValue = value;
+ value = value[name = names[j]];
+ }
+ // if there's a name conflict between property and helper
+ // property wins
+ else if (isHelper) {
+ return ref;
+ }
+ // If it's undefined, still match if the parent is an Observe.
+ else if (isObserve(value)) {
+ defaultObserve = value;
+ defaultObserveName = names[j];
+ lastValue = value = undefined;
+ break;
+ } else {
+ lastValue = value = undefined;
+ break;
+ }
+ }
+ }
+
+ // Found a matched reference.
+ if (value !== undefined) {
+ return Mustache.resolve(value, lastValue, name, isArgument);
+ }
+ }
+ }
+
+ if (defaultObserve &&
+ // if there's not a helper by this name and no attribute with this name
+ !(Mustache.getHelper(ref) &&
+ can.inArray(defaultObserveName, can.Observe.keys(defaultObserve)) === -1)) {
+ return defaultObserve.compute(defaultObserveName);
+ }
+ // Support helpers without arguments, but only if there wasn't a matching data reference.
+ // Helpers have priority over local function, see https://github.com/bitovi/canjs/issues/258
+ if (value = Mustache.getHelper(ref, options)) {
+ return ref;
+ } else if (typeof obj !== 'undefined' && obj !== null && can.isFunction(obj[ref])) {
+ // Support helper-like functions as anonymous helpers
+ return obj[ref];
+ }
+
+ return '';
+ };
+
+
+ Mustache.resolve = function(value, lastValue, name, isArgument) {
+ if (lastValue && can.isFunction(lastValue[name]) && isArgument) {
+ if (lastValue[name].isComputed) {
+ return lastValue[name];
+ }
+ // Don't execute functions if they are parameters for a helper and are not a can.compute
+ // Need to bind it to the original context so that that information doesn't get lost by the helper
+ return function() {
+ return lastValue[name].apply(lastValue, arguments);
+ };
+ }
+ // Support attributes on compute objects
+ else if (lastValue && can.isFunction(lastValue) && lastValue.isComputed) {
+ return lastValue()[name];
+ }
+ // Support functions stored in objects.
+ else if (lastValue && can.isFunction(lastValue[name])) {
+ return lastValue[name]();
+ }
+ // Invoke the length to ensure that Observe.List events fire.
+ else if (isObserve(value) && isArrayLike(value) && value.attr('length')) {
+ return value;
+ }
+ // Add support for observes
+ else if (lastValue && isObserve(lastValue)) {
+ return lastValue.compute(name);
+ } else if (can.isFunction(value)) {
+ return value();
+ } else {
+ return value;
+ }
+ };
+
+
+ // ## Helpers
+ // Helpers are functions that can be called from within a template.
+ // These helpers differ from the scanner helpers in that they execute
+ // at runtime instead of during compilation.
+ // Custom helpers can be added via `can.Mustache.registerHelper`,
+ // but there are also some built-in helpers included by default.
+ // Most of the built-in helpers are little more than aliases to actions
+ // that the base version of Mustache simply implies based on the
+ // passed in object.
+ // Built-in helpers:
+ // * `data` - `data` is a special helper that is implemented via scanning helpers.
+ // It hooks up the active element to the active data object: `<div {{data "key"}} />`
+ // * `if` - Renders a truthy section: `{{#if var}} render {{/if}}`
+ // * `unless` - Renders a falsey section: `{{#unless var}} render {{/unless}}`
+ // * `each` - Renders an array: `{{#each array}} render {{this}} {{/each}}`
+ // * `with` - Opens a context section: `{{#with var}} render {{/with}}`
+ Mustache._helpers = {};
+
+ Mustache.registerHelper = function(name, fn) {
+ this._helpers[name] = {
+ name: name,
+ fn: fn
+ };
+ };
+
+
+ Mustache.getHelper = function(name, options) {
+ return options && options.helpers && options.helpers[name] && {
+ fn: options.helpers[name]
+ } || this._helpers[name]
+ for (var i = 0, helper; helper = [i]; i++) {
+ // Find the correct helper
+ if (helper.name == name) {
+ return helper;
+ }
+ }
+ return null;
+ };
+
+
+ Mustache.render = function(partial, context) {
+ // Make sure the partial being passed in
+ // isn't a variable like { partial: "foo.mustache" }
+ if (!can.view.cached[partial] && context[partial]) {
+ partial = context[partial];
+ }
+
+ // Call into `can.view.render` passing the
+ // partial and context.
+ return can.view.render(partial, context);
+ };
+
+ Mustache.renderPartial = function(partial, context, options) {
+ return partial.render ? partial.render(context, options) :
+ partial(context, options);
+ };
+
+ // The built-in Mustache helpers.
+ can.each({
+ // Implements the `if` built-in helper.
+
+ 'if': function(expr, options) {
+ if ( !! Mustache.resolve(expr)) {
+ return options.fn(options.contexts || this);
+ } else {
+ return options.inverse(options.contexts || this);
+ }
+ },
+ // Implements the `unless` built-in helper.
+
+ 'unless': function(expr, options) {
+ if (!Mustache.resolve(expr)) {
+ return options.fn(options.contexts || this);
+ }
+ },
+
+ // Implements the `each` built-in helper.
+
+ 'each': function(expr, options) {
+ expr = Mustache.resolve(expr);
+ if ( !! expr && isArrayLike(expr)) {
+ if (isObserve(expr) && expr.attr('length')) {
+ return can.view.lists && can.view.lists(expr, function(item) {
+ return options.fn(item);
+ });
+ } else {
+ var result = [];
+ for (var i = 0; i < expr.length; i++) {
+ result.push(options.fn(expr[i]));
+ }
+ return result.join('');
+ }
+ }
+ },
+ // Implements the `with` built-in helper.
+
+ 'with': function(expr, options) {
+ var ctx = expr;
+ expr = Mustache.resolve(expr);
+ if ( !! expr) {
+ return options.fn(ctx);
+ }
+ }
+
+ }, function(fn, name) {
+ Mustache.registerHelper(name, fn);
+ });
+
+ // ## Registration
+ // Registers Mustache with can.view.
+ can.view.register({
+ suffix: "mustache",
+
+ contentType: "x-mustache-template",
+
+ // Returns a `function` that renders the view.
+ script: function(id, src) {
+ return "can.Mustache(function(_CONTEXT,_VIEW) { " + new Mustache({
+ text: src,
+ name: id
+ }).template.out + " })";
+ },
+
+ renderer: function(id, text) {
+ return Mustache({
+ text: text,
+ name: id
+ });
+ }
+ });
+
+ return can;
+ })(can);
\ No newline at end of file
Added: tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.yui.js
===================================================================
--- tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.yui.js (rev 0)
+++ tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.yui.js 2013-06-17 15:45:26 UTC (rev 92)
@@ -0,0 +1,5035 @@
+/*!
+ * CanJS - 1.1.6
+ * http://canjs.us/
+ * Copyright (c) 2013 Bitovi
+ * Wed, 29 May 2013 18:59:27 GMT
+ * Licensed MIT
+ * Includes: can/construct,can/observe,can/observe/compute,can/model,can/view,can/view/ejs,can/control,can/route,can/control/route
+ * Download from: http://canjs.com
+ */
+(function(undefined) {
+
+ // ## util/can.js
+ var __m4 = (function() {
+ var can = window.can || {};
+ if (typeof GLOBALCAN === 'undefined' || GLOBALCAN !== false) {
+ window.can = can;
+ }
+
+ can.isDeferred = function(obj) {
+ var isFunction = this.isFunction;
+ // Returns `true` if something looks like a deferred.
+ return obj && isFunction(obj.then) && isFunction(obj.pipe);
+ };
+
+ var cid = 0;
+ can.cid = function(object, name) {
+ if (object._cid) {
+ return object._cid
+ } else {
+ return object._cid = (name || "") + (++cid)
+ }
+ }
+ can.VERSION = '@EDGE';
+ return can;
+ })();
+
+ // ## util/event.js
+ var __m6 = (function(can) {
+
+ // event.js
+ // ---------
+ // _Basic event wrapper._
+ can.addEvent = function(event, fn) {
+ if (!this.__bindEvents) {
+ this.__bindEvents = {};
+ }
+ var eventName = event.split(".")[0];
+
+ if (!this.__bindEvents[eventName]) {
+ this.__bindEvents[eventName] = [];
+ }
+ this.__bindEvents[eventName].push({
+ handler: fn,
+ name: event
+ });
+ return this;
+ };
+ can.removeEvent = function(event, fn) {
+ if (!this.__bindEvents) {
+ return;
+ }
+ var i = 0,
+ events = this.__bindEvents[event.split(".")[0]],
+ ev;
+ while (i < events.length) {
+ ev = events[i]
+ if ((fn && ev.handler === fn) || (!fn && ev.name === event)) {
+ events.splice(i, 1);
+ } else {
+ i++;
+ }
+ }
+ return this;
+ };
+ can.dispatch = function(event) {
+ if (!this.__bindEvents) {
+ return;
+ }
+
+ var eventName = event.type.split(".")[0],
+ handlers = (this.__bindEvents[eventName] || []).slice(0),
+ self = this,
+ args = [event].concat(event.data || []);
+
+ can.each(handlers, function(ev) {
+ event.data = args.slice(1);
+ ev.handler.apply(self, args);
+ });
+ }
+
+ return can;
+
+ })(__m4);
+
+ // ## util/fragment.js
+ var __m7 = (function(can) {
+
+ // fragment.js
+ // ---------
+ // _DOM Fragment support._
+ var fragmentRE = /^\s*<(\w+)[^>]*>/,
+ fragment = function(html, name) {
+ if (name === undefined) {
+ name = fragmentRE.test(html) && RegExp.$1;
+ }
+
+ if (html && can.isFunction(html.replace)) {
+ // Fix "XHTML"-style tags in all browsers
+ html = html.replace(/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, "<$1></$2>");
+ }
+
+ var container = document.createElement('div'),
+ temp = document.createElement('div')
+
+ // IE's parser will strip any `<tr><td>` tags when `innerHTML`
+ // is called on a `tbody`. To get around this, we construct a
+ // valid table with a `tbody` that has the `innerHTML` we want.
+ // Then the container is the `firstChild` of the `tbody`.
+ // [source](http://www.ericvasilik.com/2006/07/code-karma.html).
+ if (name === "tbody" || name === "tfoot" || name === "thead") {
+ temp.innerHTML = "<table>" + html + "</table>";
+ container = temp.firstChild.nodeType === 3 ? temp.lastChild : temp.firstChild;
+ } else if (name === "tr") {
+ temp.innerHTML = "<table><tbody>" + html + "</tbody></table>";
+ container = temp.firstChild.nodeType === 3 ? temp.lastChild : temp.firstChild.firstChild;
+ } else if (name === "td" || name === "th") {
+ temp.innerHTML = "<table><tbody><tr>" + html + "</tr></tbody></table>";
+ container = temp.firstChild.nodeType === 3 ? temp.lastChild : temp.firstChild.firstChild.firstChild;
+ } else if (name === 'option') {
+ temp.innerHTML = "<select>" + html + "</select>";
+ container = temp.firstChild.nodeType === 3 ? temp.lastChild : temp.firstChild;
+ } else {
+ container.innerHTML = '' + html;
+ }
+
+ // IE8 barfs if you pass slice a `childNodes` object, so make a copy.
+ var tmp = {},
+ children = container.childNodes;
+ tmp.length = children.length;
+ for (var i = 0; i < children.length; i++) {
+ tmp[i] = children[i];
+ }
+ return [].slice.call(tmp);
+ }
+
+ can.buildFragment = function(html, nodes) {
+ var parts = fragment(html),
+ frag = document.createDocumentFragment();
+
+ can.each(parts, function(part) {
+ frag.appendChild(part);
+ })
+ return frag;
+ };
+
+ return can;
+ })(__m4);
+
+ // ## util/array/each.js
+ var __m8 = (function(can) {
+ can.each = function(elements, callback, context) {
+ var i = 0,
+ key;
+ if (elements) {
+ if (typeof elements.length === 'number' && elements.pop) {
+ if (elements.attr) {
+ elements.attr('length');
+ }
+ for (key = elements.length; i < key; i++) {
+ if (callback.call(context || elements[i], elements[i], i, elements) === false) {
+ break;
+ }
+ }
+ } else if (elements.hasOwnProperty) {
+ for (key in elements) {
+ if (elements.hasOwnProperty(key)) {
+ if (callback.call(context || elements[key], elements[key], key, elements) === false) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ return elements;
+ };
+
+ return can;
+ })(__m4);
+
+ // ## util/object/isplain/isplain.js
+ var __m9 = (function(can) {
+ var core_hasOwn = Object.prototype.hasOwnProperty,
+ isWindow = function(obj) {
+ return obj != null && obj == obj.window;
+ },
+ isPlainObject = function(obj) {
+ // Must be an Object.
+ // Because of IE, we also have to check the presence of the constructor property.
+ // Make sure that DOM nodes and window objects don't pass through, as well
+ if (!obj || (typeof obj !== "object") || obj.nodeType || isWindow(obj)) {
+ return false;
+ }
+
+ try {
+ // Not own constructor property must be Object
+ if (obj.constructor && !core_hasOwn.call(obj, "constructor") && !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) {
+ return false;
+ }
+ } catch (e) {
+ // IE8,9 Will throw exceptions on certain host objects #9897
+ return false;
+ }
+
+ // Own properties are enumerated firstly, so to speed up,
+ // if last one is own, then all properties are own.
+
+ var key;
+ for (key in obj) {}
+
+ return key === undefined || core_hasOwn.call(obj, key);
+ }
+
+ can.isPlainObject = isPlainObject;
+ return can;
+ })(__m4);
+
+ // ## util/deferred.js
+ var __m10 = (function(can) {
+
+ // deferred.js
+ // ---------
+ // _Lightweight, jQuery style deferreds._
+ // extend is usually provided by the wrapper but to avoid steal.then calls
+ // we define a simple extend here as well
+ var extend = function(target, src) {
+ for (var key in src) {
+ if (src.hasOwnProperty(key)) {
+ target[key] = src[key];
+ }
+ }
+ },
+ Deferred = function(func) {
+ if (!(this instanceof Deferred))
+ return new Deferred();
+
+ this._doneFuncs = [];
+ this._failFuncs = [];
+ this._resultArgs = null;
+ this._status = "";
+
+ // Check for option `function` -- call it with this as context and as first
+ // parameter, as specified in jQuery API.
+ func && func.call(this, this);
+ };
+
+ can.Deferred = Deferred;
+ can.when = Deferred.when = function() {
+ var args = can.makeArray(arguments);
+ if (args.length < 2) {
+ var obj = args[0];
+ if (obj && (can.isFunction(obj.isResolved) && can.isFunction(obj.isRejected))) {
+ return obj;
+ } else {
+ return Deferred().resolve(obj);
+ }
+ } else {
+
+ var df = Deferred(),
+ done = 0,
+ // Resolve params -- params of each resolve, we need to track them down
+ // to be able to pass them in the correct order if the master
+ // needs to be resolved.
+ rp = [];
+
+ can.each(args, function(arg, j) {
+ arg.done(function() {
+ rp[j] = (arguments.length < 2) ? arguments[0] : arguments;
+ if (++done == args.length) {
+ df.resolve.apply(df, rp);
+ }
+ }).fail(function() {
+ df.reject((arguments.length === 1) ? arguments[0] : arguments);
+ });
+ });
+
+ return df;
+
+ }
+ }
+
+ var resolveFunc = function(type, _status) {
+ return function(context) {
+ var args = this._resultArgs = (arguments.length > 1) ? arguments[1] : [];
+ return this.exec(context, this[type], args, _status);
+ }
+ },
+ doneFunc = function(type, _status) {
+ return function() {
+ var self = this;
+ // In Safari, the properties of the `arguments` object are not enumerable,
+ // so we have to convert arguments to an `Array` that allows `can.each` to loop over them.
+ can.each(Array.prototype.slice.call(arguments), function(v, i, args) {
+ if (!v)
+ return;
+ if (v.constructor === Array) {
+ args.callee.apply(self, v)
+ } else {
+ // Immediately call the `function` if the deferred has been resolved.
+ if (self._status === _status)
+ v.apply(self, self._resultArgs || []);
+
+ self[type].push(v);
+ }
+ });
+ return this;
+ }
+ };
+
+ extend(Deferred.prototype, {
+ pipe: function(done, fail) {
+ var d = can.Deferred();
+ this.done(function() {
+ d.resolve(done.apply(this, arguments));
+ });
+
+ this.fail(function() {
+ if (fail) {
+ d.reject(fail.apply(this, arguments));
+ } else {
+ d.reject.apply(d, arguments);
+ }
+ });
+ return d;
+ },
+ resolveWith: resolveFunc("_doneFuncs", "rs"),
+ rejectWith: resolveFunc("_failFuncs", "rj"),
+ done: doneFunc("_doneFuncs", "rs"),
+ fail: doneFunc("_failFuncs", "rj"),
+ always: function() {
+ var args = can.makeArray(arguments);
+ if (args.length && args[0])
+ this.done(args[0]).fail(args[0]);
+
+ return this;
+ },
+
+ then: function() {
+ var args = can.makeArray(arguments);
+ // Fail `function`(s)
+ if (args.length > 1 && args[1])
+ this.fail(args[1]);
+
+ // Done `function`(s)
+ if (args.length && args[0])
+ this.done(args[0]);
+
+ return this;
+ },
+
+ state: function() {
+ switch (this._status) {
+ case 'rs':
+ return 'resolved';
+ case 'rj':
+ return 'rejected';
+ default:
+ return 'pending';
+ }
+ },
+
+ isResolved: function() {
+ return this._status === "rs";
+ },
+
+ isRejected: function() {
+ return this._status === "rj";
+ },
+
+ reject: function() {
+ return this.rejectWith(this, arguments);
+ },
+
+ resolve: function() {
+ return this.resolveWith(this, arguments);
+ },
+
+ exec: function(context, dst, args, st) {
+ if (this._status !== "")
+ return this;
+
+ this._status = st;
+
+ can.each(dst, function(d) {
+ d.apply(context, args);
+ });
+
+ return this;
+ }
+ });
+
+ return can;
+ })(__m4);
+
+ // ## util/hashchange.js
+ var __m11 = (function() {
+ // This is a workaround for libraries that don't natively listen to the window hashchange event
+ ! function() {
+ var addEvent = function(el, ev, fn) {
+ if (el.addEventListener) {
+ el.addEventListener(ev, fn, false);
+ } else if (el.attachEvent) {
+ el.attachEvent('on' + ev, fn);
+ } else {
+ el['on' + ev] = fn;
+ }
+ },
+ onHashchange = function() {
+ can.trigger(window, 'hashchange');
+ };
+
+ addEvent(window, 'hashchange', onHashchange);
+ }();
+ })();
+
+ // ## util/yui/yui.js
+ var __m3 = (function(can) {
+
+ // ---------
+ // _YUI node list._
+ // `can.Y` is set as part of the build process.
+ // `YUI().use('*')` is called for when `YUI` is statically loaded (like when running tests).
+ var Y = can.Y = can.Y || YUI().use('*');
+
+ // Map string helpers.
+ can.trim = function(s) {
+ return Y.Lang.trim(s);
+ }
+
+ // Map array helpers.
+ can.makeArray = function(arr) {
+ if (!arr) {
+ return [];
+ }
+ return Y.Array(arr);
+ };
+ can.isArray = Y.Lang.isArray;
+ can.inArray = function(item, arr) {
+ if (!arr) {
+ return -1;
+ }
+ return Y.Array.indexOf(arr, item);
+ };
+
+ can.map = function(arr, fn) {
+ return Y.Array.map(can.makeArray(arr || []), fn);
+ };
+
+ // Map object helpers.
+ can.extend = function(first) {
+ var deep = first === true ? 1 : 0,
+ target = arguments[deep],
+ i = deep + 1,
+ arg;
+ for (; arg = arguments[i]; i++) {
+ Y.mix(target, arg, true, null, null, !! deep);
+ }
+ return target;
+ }
+ can.param = function(object) {
+ return Y.QueryString.stringify(object, {
+ arrayKey: true
+ })
+ }
+ can.isEmptyObject = function(object) {
+ return Y.Object.isEmpty(object);
+ }
+
+ // Map function helpers.
+ can.proxy = function(func, context) {
+ return Y.bind.apply(Y, arguments);
+ }
+ can.isFunction = function(f) {
+ return Y.Lang.isFunction(f);
+ }
+
+ // Element -- get the wrapped helper.
+ var prepareNodeList = function(nodelist) {
+ nodelist.each(function(node, i) {
+ nodelist[i] = node.getDOMNode();
+ });
+ nodelist.length = nodelist.size();
+ return nodelist;
+ }
+ can.$ = function(selector) {
+ if (selector === window) {
+ return window;
+ } else if (selector instanceof Y.NodeList) {
+ return prepareNodeList(selector);
+ } else if (typeof selector === "object" && !can.isArray(selector) && typeof selector.nodeType === "undefined" && !selector.getDOMNode) {
+ return selector;
+ } else {
+ return prepareNodeList(Y.all(selector));
+ }
+ }
+ can.get = function(wrapped, index) {
+ return wrapped._nodes[index];
+ }
+ can.append = function(wrapped, html) {
+ wrapped.each(function(node) {
+ if (typeof html === 'string') {
+ html = can.buildFragment(html, node)
+ }
+ node.append(html)
+ });
+ }
+ can.addClass = function(wrapped, className) {
+ return wrapped.addClass(className);
+ }
+ can.data = function(wrapped, key, value) {
+ if (value === undefined) {
+
+ return wrapped.item(0).getData(key)
+ } else {
+ return wrapped.item(0).setData(key, value)
+ }
+ }
+ can.remove = function(wrapped) {
+ return wrapped.remove() && wrapped.destroy();
+ }
+ // Destroyed method.
+ can._yNodeDestroy = can._yNodeDestroy || Y.Node.prototype.destroy;
+ Y.Node.prototype.destroy = function() {
+ can.trigger(this, "destroyed", [], false)
+ can._yNodeDestroy.apply(this, arguments)
+ }
+ // Let `nodelist` know about the new destroy...
+ Y.NodeList.addMethod("destroy", Y.Node.prototype.destroy);
+
+ // Ajax
+ var optionsMap = {
+ type: "method",
+ success: undefined,
+ error: undefined
+ }
+ var updateDeferred = function(request, d) {
+ // `YUI` only returns a request if it is asynchronous.
+ if (request && request.io) {
+ var xhr = request.io;
+ for (var prop in xhr) {
+ if (typeof d[prop] == 'function') {
+ d[prop] = function() {
+ xhr[prop].apply(xhr, arguments)
+ }
+ } else {
+ d[prop] = prop[xhr]
+ }
+ }
+ }
+ }
+ can.ajax = function(options) {
+ var d = can.Deferred(),
+ requestOptions = can.extend({}, options);
+
+ for (var option in optionsMap) {
+ if (requestOptions[option] !== undefined) {
+ requestOptions[optionsMap[option]] = requestOptions[option];
+ delete requestOptions[option]
+ }
+ }
+ requestOptions.sync = !options.async;
+
+ var success = options.success,
+ error = options.error;
+
+ requestOptions.on = {
+ success: function(transactionid, response) {
+ var data = response.responseText;
+ if (options.dataType === 'json') {
+ data = eval("(" + data + ")")
+ }
+ updateDeferred(request, d);
+ d.resolve(data);
+ success && success(data, "success", request);
+ },
+ failure: function(transactionid, response) {
+ updateDeferred(request, d);
+ d.reject(request, "error");
+ error && error(request, "error");
+ }
+ };
+
+ var request = Y.io(requestOptions.url, requestOptions);
+ updateDeferred(request, d);
+ return d;
+
+ }
+
+ // Events - The `id` of the `function` to be bound, used as an expando on the `function`
+ // so we can lookup it's `remove` object.
+ var yuiEventId = 0,
+ // Takes a node list, goes through each node
+ // and adds events data that has a map of events to
+ // `callbackId` to `remove` object. It looks like
+ // `{click: {5: {remove: fn}}}`.
+ addBinding = function(nodelist, selector, ev, cb) {
+ if (nodelist instanceof Y.NodeList || !nodelist.on || nodelist.getDOMNode) {
+ nodelist.each(function(node) {
+ var node = can.$(node);
+ var events = can.data(node, "events"),
+ eventName = ev + ":" + selector;
+ if (!events) {
+ can.data(node, "events", events = {});
+ }
+ if (!events[eventName]) {
+ events[eventName] = {};
+ }
+ if (cb.__bindingsIds === undefined) {
+ cb.__bindingsIds = yuiEventId++;
+ }
+ events[eventName][cb.__bindingsIds] = selector ? node.item(0).delegate(ev, cb, selector) : node.item(0).on(ev, cb);
+ });
+ } else {
+ var obj = nodelist,
+ events = obj.__canEvents = obj.__canEvents || {};
+ if (!events[ev]) {
+ events[ev] = {};
+ }
+ if (cb.__bindingsIds === undefined) {
+ cb.__bindingsIds = yuiEventId++;
+ }
+ events[ev][cb.__bindingsIds] = obj.on(ev, cb);
+ }
+ },
+ // Removes a binding on a `nodelist` by finding
+ // the remove object within the object's data.
+ removeBinding = function(nodelist, selector, ev, cb) {
+ if (nodelist instanceof Y.NodeList || !nodelist.on || nodelist.getDOMNode) {
+ nodelist.each(function(node) {
+ var node = can.$(node),
+ events = can.data(node, "events");
+ if (events) {
+ var eventName = ev + ":" + selector,
+ handlers = events[eventName],
+ handler = handlers[cb.__bindingsIds];
+ handler.detach();
+ delete handlers[cb.__bindingsIds];
+ if (can.isEmptyObject(handlers)) {
+ delete events[ev];
+ }
+ if (can.isEmptyObject(events)) {}
+ }
+ });
+ } else {
+ var obj = nodelist,
+ events = obj.__canEvents || {},
+ handlers = events[ev],
+ handler = handlers[cb.__bindingsIds];
+ handler.detach();
+ delete handlers[cb.__bindingsIds];
+ if (can.isEmptyObject(handlers)) {
+ delete events[ev];
+ }
+ if (can.isEmptyObject(events)) {}
+ }
+ }
+ can.bind = function(ev, cb) {
+ // If we can bind to it...
+ if (this.bind && this.bind !== can.bind) {
+ this.bind(ev, cb)
+ } else if (this.on || this.nodeType) {
+ addBinding(can.$(this), undefined, ev, cb)
+ } else if (this.addEvent) {
+ this.addEvent(ev, cb)
+ } else {
+ // Make it bind-able...
+ can.addEvent.call(this, ev, cb)
+ }
+ return this;
+ }
+ can.unbind = function(ev, cb) {
+ // If we can bind to it...
+ if (this.unbind && this.unbind !== can.unbind) {
+ this.unbind(ev, cb)
+ } else if (this.on || this.nodeType) {
+ removeBinding(can.$(this), undefined, ev, cb);
+ } else {
+ // Make it bind-able...
+ can.removeEvent.call(this, ev, cb)
+ }
+ return this;
+ }
+ can.trigger = function(item, event, args, bubble) {
+ if (item instanceof Y.NodeList) {
+ item = item.item(0);
+ }
+ if (item.getDOMNode) {
+ item = item.getDOMNode();
+ }
+
+ if (item.nodeName) {
+ item = Y.Node(item);
+ if (bubble === false) {
+ // Force stop propagation by listening to `on` and then
+ // immediately disconnecting
+ item.once(event, function(ev) {
+ ev.stopPropagation && ev.stopPropagation();
+ ev.cancelBubble = true;
+ ev._stopper && ev._stopper();
+ })
+ }
+ realTrigger(item.getDOMNode(), event, {})
+ } else {
+ if (typeof event === 'string') {
+ event = {
+ type: event
+ }
+ }
+ event.target = event.target || item
+ event.data = args
+ can.dispatch.call(item, event)
+ }
+ };
+ // Allow `dom` `destroyed` events.
+ Y.mix(Y.Node.DOM_EVENTS, {
+ destroyed: true,
+ foo: true
+ });
+
+ can.delegate = function(selector, ev, cb) {
+ if (this.on || this.nodeType) {
+ addBinding(can.$(this), selector, ev, cb)
+ } else if (this.delegate) {
+ this.delegate(selector, ev, cb)
+ }
+ return this;
+ }
+ can.undelegate = function(selector, ev, cb) {
+ if (this.on || this.nodeType) {
+ removeBinding(can.$(this), selector, ev, cb);
+ } else if (this.undelegate) {
+ this.undelegate(selector, ev, cb)
+ }
+ return this;
+ }
+
+ // `realTrigger` taken from `dojo`.
+ var leaveRe = /mouse(enter|leave)/,
+ _fix = function(_, p) {
+ return "mouse" + (p == "enter" ? "over" : "out");
+ },
+ realTrigger = document.createEvent ? function(n, e, a) {
+ // the same branch
+ var ev = document.createEvent("HTMLEvents");
+ e = e.replace(leaveRe, _fix);
+ ev.initEvent(e, true, true);
+ a && can.extend(ev, a);
+ n.dispatchEvent(ev);
+ } : function(n, e, a) {
+ // the janktastic branch
+ var ev = "on" + e,
+ stop = false,
+ lc = e.toLowerCase(),
+ node = n;
+ try {
+ // FIXME: is this worth it? for mixed-case native event support:? Opera ends up in the
+ // createEvent path above, and also fails on _some_ native-named events.
+ // if(lc !== e && d.indexOf(d.NodeList.events, lc) >= 0){
+ // // if the event is one of those listed in our NodeList list
+ // // in lowercase form but is mixed case, throw to avoid
+ // // fireEvent. /me sighs. http://gist.github.com/315318
+ // throw("janktastic");
+ // }
+ n.fireEvent(ev);
+ } catch (er) {
+ // a lame duck to work with. we're probably a 'custom event'
+ var evdata = can.extend({
+ type: e,
+ target: n,
+ faux: true,
+ // HACK: [needs] added support for customStopper to _base/event.js
+ // some tests will fail until del._stopPropagation has support.
+ _stopper: function() {
+ stop = this.cancelBubble;
+ }
+ }, a);
+ realTriggerHandler(n, e, evdata);
+
+ // handle bubbling of custom events, unless the event was stopped.
+ while (!stop && n !== document && n.parentNode) {
+ n = n.parentNode;
+ realTriggerHandler(n, e, evdata);
+ //can.isFunction(n[ev]) && n[ev](evdata);
+ }
+ }
+ },
+ realTriggerHandler = function(n, e, evdata) {
+ var node = Y.Node(n),
+ handlers = can.Y.Event.getListeners(node._yuid, e);
+ if (handlers) {
+ for (var i = 0; i < handlers.length; i++) {
+ handlers[i].fire(evdata)
+ }
+ }
+ };
+
+ return can;
+ })(__m4, YUI, __m6, __m7, __m8, __m9, __m10, __m11);
+
+ // ## util/string/string.js
+ var __m2 = (function(can) {
+ // ##string.js
+ // _Miscellaneous string utility functions._
+
+ // Several of the methods in this plugin use code adapated from Prototype
+ // Prototype JavaScript framework, version 1.6.0.1.
+ // © 2005-2007 Sam Stephenson
+ var strUndHash = /_|-/,
+ strColons = /\=\=/,
+ strWords = /([A-Z]+)([A-Z][a-z])/g,
+ strLowUp = /([a-z\d])([A-Z])/g,
+ strDash = /([a-z\d])([A-Z])/g,
+ strReplacer = /\{([^\}]+)\}/g,
+ strQuote = /"/g,
+ strSingleQuote = /'/g,
+
+ // Returns the `prop` property from `obj`.
+ // If `add` is true and `prop` doesn't exist in `obj`, create it as an
+ // empty object.
+ getNext = function(obj, prop, add) {
+ var result = obj[prop];
+
+ if (result === undefined && add === true) {
+ result = obj[prop] = {}
+ }
+ return result
+ },
+
+ // Returns `true` if the object can have properties (no `null`s).
+ isContainer = function(current) {
+ return (/^f|^o/).test(typeof current);
+ };
+
+ can.extend(can, {
+ // Escapes strings for HTML.
+
+ esc: function(content) {
+ // Convert bad values into empty strings
+ var isInvalid = content === null || content === undefined || (isNaN(content) && ("" + content === 'NaN'));
+ return ("" + (isInvalid ? '' : content))
+ .replace(/&/g, '&')
+ .replace(/</g, '<')
+ .replace(/>/g, '>')
+ .replace(strQuote, '"')
+ .replace(strSingleQuote, "'");
+ },
+
+
+ getObject: function(name, roots, add) {
+
+ // The parts of the name we are looking up
+ // `['App','Models','Recipe']`
+ var parts = name ? name.split('.') : [],
+ length = parts.length,
+ current,
+ r = 0,
+ i, container, rootsLength;
+
+ // Make sure roots is an `array`.
+ roots = can.isArray(roots) ? roots : [roots || window];
+
+ rootsLength = roots.length
+
+ if (!length) {
+ return roots[0];
+ }
+
+ // For each root, mark it as current.
+ for (r; r < rootsLength; r++) {
+ current = roots[r];
+ container = undefined;
+
+ // Walk current to the 2nd to last object or until there
+ // is not a container.
+ for (i = 0; i < length && isContainer(current); i++) {
+ container = current;
+ current = getNext(container, parts[i]);
+ }
+
+ // If we found property break cycle
+ if (container !== undefined && current !== undefined) {
+ break
+ }
+ }
+
+ // Remove property from found container
+ if (add === false && current !== undefined) {
+ delete container[parts[i - 1]]
+ }
+
+ // When adding property add it to the first root
+ if (add === true && current === undefined) {
+ current = roots[0]
+
+ for (i = 0; i < length && isContainer(current); i++) {
+ current = getNext(current, parts[i], true);
+ }
+ }
+
+ return current;
+ },
+ // Capitalizes a string.
+
+ capitalize: function(s, cache) {
+ // Used to make newId.
+ return s.charAt(0).toUpperCase() + s.slice(1);
+ },
+
+ // Underscores a string.
+
+ underscore: function(s) {
+ return s
+ .replace(strColons, '/')
+ .replace(strWords, '$1_$2')
+ .replace(strLowUp, '$1_$2')
+ .replace(strDash, '_')
+ .toLowerCase();
+ },
+ // Micro-templating.
+
+ sub: function(str, data, remove) {
+ var obs = [];
+
+ str = str || '';
+
+ obs.push(str.replace(strReplacer, function(whole, inside) {
+
+ // Convert inside to type.
+ var ob = can.getObject(inside, data, remove === true ? false : undefined);
+
+ if (ob === undefined) {
+ obs = null;
+ return "";
+ }
+
+ // If a container, push into objs (which will return objects found).
+ if (isContainer(ob) && obs) {
+ obs.push(ob);
+ return "";
+ }
+
+ return "" + ob;
+ }));
+
+ return obs === null ? obs : (obs.length <= 1 ? obs[0] : obs);
+ },
+
+ // These regex's are used throughout the rest of can, so let's make
+ // them available.
+ replacer: strReplacer,
+ undHash: strUndHash
+ });
+ return can;
+ })(__m3);
+
+ // ## construct/construct.js
+ var __m1 = (function(can) {
+
+ // ## construct.js
+ // `can.Construct`
+ // _This is a modified version of
+ // [John Resig's class](http://ejohn.org/blog/simple-javascript-inheritance/).
+ // It provides class level inheritance and callbacks._
+
+ // A private flag used to initialize a new class instance without
+ // initializing it's bindings.
+ var initializing = 0;
+
+
+ can.Construct = function() {
+ if (arguments.length) {
+ return can.Construct.extend.apply(can.Construct, arguments);
+ }
+ };
+
+
+ can.extend(can.Construct, {
+
+ newInstance: function() {
+ // Get a raw instance object (`init` is not called).
+ var inst = this.instance(),
+ arg = arguments,
+ args;
+
+ // Call `setup` if there is a `setup`
+ if (inst.setup) {
+ args = inst.setup.apply(inst, arguments);
+ }
+
+ // Call `init` if there is an `init`
+ // If `setup` returned `args`, use those as the arguments
+ if (inst.init) {
+ inst.init.apply(inst, args || arguments);
+ }
+
+ return inst;
+ },
+ // Overwrites an object with methods. Used in the `super` plugin.
+ // `newProps` - New properties to add.
+ // `oldProps` - Where the old properties might be (used with `super`).
+ // `addTo` - What we are adding to.
+ _inherit: function(newProps, oldProps, addTo) {
+ can.extend(addTo || newProps, newProps || {})
+ },
+ // used for overwriting a single property.
+ // this should be used for patching other objects
+ // the super plugin overwrites this
+ _overwrite: function(what, oldProps, propName, val) {
+ what[propName] = val;
+ },
+ // Set `defaults` as the merger of the parent `defaults` and this
+ // object's `defaults`. If you overwrite this method, make sure to
+ // include option merging logic.
+
+ setup: function(base, fullName) {
+ this.defaults = can.extend(true, {}, base.defaults, this.defaults);
+ },
+ // Create's a new `class` instance without initializing by setting the
+ // `initializing` flag.
+ instance: function() {
+
+ // Prevents running `init`.
+ initializing = 1;
+
+ var inst = new this();
+
+ // Allow running `init`.
+ initializing = 0;
+
+ return inst;
+ },
+ // Extends classes.
+
+ extend: function(fullName, klass, proto) {
+ // Figure out what was passed and normalize it.
+ if (typeof fullName != 'string') {
+ proto = klass;
+ klass = fullName;
+ fullName = null;
+ }
+
+ if (!proto) {
+ proto = klass;
+ klass = null;
+ }
+ proto = proto || {};
+
+ var _super_class = this,
+ _super = this.prototype,
+ name, shortName, namespace, prototype;
+
+ // Instantiate a base class (but only create the instance,
+ // don't run the init constructor).
+ prototype = this.instance();
+
+ // Copy the properties over onto the new prototype.
+ can.Construct._inherit(proto, _super, prototype);
+
+ // The dummy class constructor.
+
+ function Constructor() {
+ // All construction is actually done in the init method.
+ if (!initializing) {
+ return this.constructor !== Constructor && arguments.length ?
+ // We are being called without `new` or we are extending.
+ arguments.callee.extend.apply(arguments.callee, arguments) :
+ // We are being called with `new`.
+ this.constructor.newInstance.apply(this.constructor, arguments);
+ }
+ }
+
+ // Copy old stuff onto class (can probably be merged w/ inherit)
+ for (name in _super_class) {
+ if (_super_class.hasOwnProperty(name)) {
+ Constructor[name] = _super_class[name];
+ }
+ }
+
+ // Copy new static properties on class.
+ can.Construct._inherit(klass, _super_class, Constructor);
+
+ // Setup namespaces.
+ if (fullName) {
+
+ var parts = fullName.split('.'),
+ shortName = parts.pop(),
+ current = can.getObject(parts.join('.'), window, true),
+ namespace = current,
+ _fullName = can.underscore(fullName.replace(/\./g, "_")),
+ _shortName = can.underscore(shortName);
+
+
+
+ current[shortName] = Constructor;
+ }
+
+ // Set things that shouldn't be overwritten.
+ can.extend(Constructor, {
+ constructor: Constructor,
+ prototype: prototype,
+
+ namespace: namespace,
+
+ _shortName: _shortName,
+
+ fullName: fullName,
+ _fullName: _fullName
+ });
+
+ // Dojo and YUI extend undefined
+ if (shortName !== undefined) {
+ Constructor.shortName = shortName;
+ }
+
+ // Make sure our prototype looks nice.
+ Constructor.prototype.constructor = Constructor;
+
+
+ // Call the class `setup` and `init`
+ var t = [_super_class].concat(can.makeArray(arguments)),
+ args = Constructor.setup.apply(Constructor, t);
+
+ if (Constructor.init) {
+ Constructor.init.apply(Constructor, args || t);
+ }
+
+
+ return Constructor;
+
+ }
+
+ });
+ return can.Construct;
+ })(__m2);
+
+ // ## util/bind/bind.js
+ var __m13 = (function(can) {
+
+
+ // ## Bind helpers
+ can.bindAndSetup = function() {
+ // Add the event to this object
+ can.addEvent.apply(this, arguments);
+ // If not initializing, and the first binding
+ // call bindsetup if the function exists.
+ if (!this._init) {
+ if (!this._bindings) {
+ this._bindings = 1;
+ // setup live-binding
+ this._bindsetup && this._bindsetup();
+
+ } else {
+ this._bindings++;
+ }
+
+ }
+
+ return this;
+ };
+
+ can.unbindAndTeardown = function(ev, handler) {
+ // Remove the event handler
+ can.removeEvent.apply(this, arguments);
+
+ this._bindings--;
+ // If there are no longer any bindings and
+ // there is a bindteardown method, call it.
+ if (!this._bindings) {
+ this._bindteardown && this._bindteardown();
+ }
+ return this;
+ }
+
+ return can;
+
+ })(__m3);
+
+ // ## observe/observe.js
+ var __m12 = (function(can, bind) {
+ // ## observe.js
+ // `can.Observe`
+ // _Provides the observable pattern for JavaScript Objects._
+ // Returns `true` if something is an object with properties of its own.
+ var canMakeObserve = function(obj) {
+ return obj && !can.isDeferred(obj) && (can.isArray(obj) || can.isPlainObject(obj) || (obj instanceof can.Observe));
+ },
+
+ // Removes all listeners.
+ unhookup = function(items, namespace) {
+ return can.each(items, function(item) {
+ if (item && item.unbind) {
+ item.unbind("change" + namespace);
+ }
+ });
+ },
+ // Listens to changes on `child` and "bubbles" the event up.
+ // `child` - The object to listen for changes on.
+ // `prop` - The property name is at on.
+ // `parent` - The parent object of prop.
+ // `ob` - (optional) The Observe object constructor
+ // `list` - (optional) The observable list constructor
+ hookupBubble = function(child, prop, parent, Ob, List) {
+ Ob = Ob || Observe;
+ List = List || Observe.List;
+
+ // If it's an `array` make a list, otherwise a child.
+ if (child instanceof Observe) {
+ // We have an `observe` already...
+ // Make sure it is not listening to this already
+ // It's only listening if it has bindings already.
+ parent._bindings && unhookup([child], parent._cid);
+ } else if (can.isArray(child)) {
+ child = new List(child);
+ } else {
+ child = new Ob(child);
+ }
+ // only listen if something is listening to you
+ if (parent._bindings) {
+ // Listen to all changes and `batchTrigger` upwards.
+ bindToChildAndBubbleToParent(child, prop, parent)
+ }
+
+
+ return child;
+ },
+ bindToChildAndBubbleToParent = function(child, prop, parent) {
+ child.bind("change" + parent._cid, function() {
+ // `batchTrigger` the type on this...
+ var args = can.makeArray(arguments),
+ ev = args.shift();
+ args[0] = (prop === "*" ? [parent.indexOf(child), args[0]] : [prop, args[0]]).join(".");
+
+ // track objects dispatched on this observe
+ ev.triggeredNS = ev.triggeredNS || {};
+
+ // if it has already been dispatched exit
+ if (ev.triggeredNS[parent._cid]) {
+ return;
+ }
+
+ ev.triggeredNS[parent._cid] = true;
+ // send change event with modified attr to parent
+ can.trigger(parent, ev, args);
+ // send modified attr event to parent
+ //can.trigger(parent, args[0], args);
+ });
+ }
+ // An `id` to track events for a given observe.
+ observeId = 0,
+ // A helper used to serialize an `Observe` or `Observe.List`.
+ // `observe` - The observable.
+ // `how` - To serialize with `attr` or `serialize`.
+ // `where` - To put properties, in an `{}` or `[]`.
+ serialize = function(observe, how, where) {
+ // Go through each property.
+ observe.each(function(val, name) {
+ // If the value is an `object`, and has an `attrs` or `serialize` function.
+ where[name] = canMakeObserve(val) && can.isFunction(val[how]) ?
+ // Call `attrs` or `serialize` to get the original data back.
+ val[how]() :
+ // Otherwise return the value.
+ val;
+ });
+ return where;
+ },
+ attrParts = function(attr, keepKey) {
+ if (keepKey) {
+ return [attr];
+ }
+ return can.isArray(attr) ? attr : ("" + attr).split(".");
+ },
+ // Which batch of events this is for -- might not want to send multiple
+ // messages on the same batch. This is mostly for event delegation.
+ batchNum = 1,
+ // how many times has start been called without a stop
+ transactions = 0,
+ // an array of events within a transaction
+ batchEvents = [],
+ stopCallbacks = [],
+ makeBindSetup = function(wildcard) {
+ return function() {
+ var parent = this;
+ this._each(function(child, prop) {
+ if (child && child.bind) {
+ bindToChildAndBubbleToParent(child, wildcard || prop, parent)
+ }
+ })
+ };
+ };
+
+
+ var Observe = can.Map = can.Observe = can.Construct({
+
+ // keep so it can be overwritten
+ bind: can.bindAndSetup,
+ unbind: can.unbindAndTeardown,
+ id: "id",
+ canMakeObserve: canMakeObserve,
+ // starts collecting events
+ // takes a callback for after they are updated
+ // how could you hook into after ejs
+
+ startBatch: function(batchStopHandler) {
+ transactions++;
+ batchStopHandler && stopCallbacks.push(batchStopHandler);
+ },
+
+ stopBatch: function(force, callStart) {
+ if (force) {
+ transactions = 0;
+ } else {
+ transactions--;
+ }
+
+ if (transactions == 0) {
+ var items = batchEvents.slice(0),
+ callbacks = stopCallbacks.slice(0);
+ batchEvents = [];
+ stopCallbacks = [];
+ batchNum++;
+ callStart && this.startBatch();
+ can.each(items, function(args) {
+ can.trigger.apply(can, args);
+ });
+ can.each(callbacks, function(cb) {
+ cb();
+ });
+ }
+ },
+
+ triggerBatch: function(item, event, args) {
+ // Don't send events if initalizing.
+ if (!item._init) {
+ if (transactions == 0) {
+ return can.trigger(item, event, args);
+ } else {
+ event = typeof event === "string" ? {
+ type: event
+ } :
+ event;
+ event.batchNum = batchNum;
+ batchEvents.push([
+ item,
+ event,
+ args
+ ]);
+ }
+ }
+ },
+
+ keys: function(observe) {
+ var keys = [];
+ Observe.__reading && Observe.__reading(observe, '__keys');
+ for (var keyName in observe._data) {
+ keys.push(keyName);
+ }
+ return keys;
+ }
+ },
+
+ {
+ setup: function(obj) {
+ // `_data` is where we keep the properties.
+ this._data = {};
+
+ // The namespace this `object` uses to listen to events.
+ can.cid(this, ".observe");
+ // Sets all `attrs`.
+ this._init = 1;
+ this.attr(obj);
+ this.bind('change' + this._cid, can.proxy(this._changes, this));
+ delete this._init;
+ },
+ _bindsetup: makeBindSetup(),
+ _bindteardown: function() {
+ var cid = this._cid;
+ this._each(function(child) {
+ unhookup([child], cid)
+ })
+ },
+ _changes: function(ev, attr, how, newVal, oldVal) {
+ Observe.triggerBatch(this, {
+ type: attr,
+ batchNum: ev.batchNum
+ }, [newVal, oldVal]);
+ },
+ _triggerChange: function(attr, how, newVal, oldVal) {
+ Observe.triggerBatch(this, "change", can.makeArray(arguments))
+ },
+ // no live binding iterator
+ _each: function(callback) {
+ var data = this.__get();
+ for (var prop in data) {
+ if (data.hasOwnProperty(prop)) {
+ callback(data[prop], prop)
+ }
+ }
+ },
+
+ attr: function(attr, val) {
+ // This is super obfuscated for space -- basically, we're checking
+ // if the type of the attribute is not a `number` or a `string`.
+ var type = typeof attr;
+ if (type !== "string" && type !== "number") {
+ return this._attrs(attr, val)
+ } else if (val === undefined) { // If we are getting a value.
+ // Let people know we are reading.
+ Observe.__reading && Observe.__reading(this, attr)
+ return this._get(attr)
+ } else {
+ // Otherwise we are setting.
+ this._set(attr, val);
+ return this;
+ }
+ },
+
+ each: function() {
+ Observe.__reading && Observe.__reading(this, '__keys');
+ return can.each.apply(undefined, [this.__get()].concat(can.makeArray(arguments)))
+ },
+
+ removeAttr: function(attr) {
+ // Info if this is List or not
+ var isList = this instanceof can.Observe.List,
+ // Convert the `attr` into parts (if nested).
+ parts = attrParts(attr),
+ // The actual property to remove.
+ prop = parts.shift(),
+ // The current value.
+ current = isList ? this[prop] : this._data[prop];
+
+ // If we have more parts, call `removeAttr` on that part.
+ if (parts.length) {
+ return current.removeAttr(parts)
+ } else {
+ if (isList) {
+ this.splice(prop, 1)
+ } else if (prop in this._data) {
+ // Otherwise, `delete`.
+ delete this._data[prop];
+ // Create the event.
+ if (!(prop in this.constructor.prototype)) {
+ delete this[prop]
+ }
+ // Let others know the number of keys have changed
+ Observe.triggerBatch(this, "__keys");
+ this._triggerChange(prop, "remove", undefined, current);
+
+ }
+ return current;
+ }
+ },
+ // Reads a property from the `object`.
+ _get: function(attr) {
+ var value = typeof attr === 'string' && !! ~attr.indexOf('.') && this.__get(attr);
+ if (value) {
+ return value;
+ }
+
+ // break up the attr (`"foo.bar"`) into `["foo","bar"]`
+ var parts = attrParts(attr),
+ // get the value of the first attr name (`"foo"`)
+ current = this.__get(parts.shift());
+ // if there are other attributes to read
+ return parts.length ?
+ // and current has a value
+ current ?
+ // lookup the remaining attrs on current
+ current._get(parts) :
+ // or if there's no current, return undefined
+ undefined :
+ // if there are no more parts, return current
+ current;
+ },
+ // Reads a property directly if an `attr` is provided, otherwise
+ // returns the "real" data object itself.
+ __get: function(attr) {
+ return attr ? this._data[attr] : this._data;
+ },
+ // Sets `attr` prop as value on this object where.
+ // `attr` - Is a string of properties or an array of property values.
+ // `value` - The raw value to set.
+ _set: function(attr, value, keepKey) {
+ // Convert `attr` to attr parts (if it isn't already).
+ var parts = attrParts(attr, keepKey),
+ // The immediate prop we are setting.
+ prop = parts.shift(),
+ // The current value.
+ current = this.__get(prop);
+
+ // If we have an `object` and remaining parts.
+ if (canMakeObserve(current) && parts.length) {
+ // That `object` should set it (this might need to call attr).
+ current._set(parts, value)
+ } else if (!parts.length) {
+ // We're in "real" set territory.
+ if (this.__convert) {
+ value = this.__convert(prop, value)
+ }
+ this.__set(prop, value, current)
+ } else {
+ throw "can.Observe: Object does not exist"
+ }
+ },
+ __set: function(prop, value, current) {
+
+ // Otherwise, we are setting it on this `object`.
+ // TODO: Check if value is object and transform
+ // are we changing the value.
+ if (value !== current) {
+ // Check if we are adding this for the first time --
+ // if we are, we need to create an `add` event.
+ var changeType = this.__get().hasOwnProperty(prop) ? "set" : "add";
+
+ // Set the value on data.
+ this.___set(prop,
+
+ // If we are getting an object.
+ canMakeObserve(value) ?
+
+ // Hook it up to send event.
+ hookupBubble(value, prop, this) :
+ // Value is normal.
+ value);
+
+ if (changeType == "add") {
+ // If there is no current value, let others know that
+ // the the number of keys have changed
+
+ Observe.triggerBatch(this, "__keys", undefined);
+
+ }
+ // `batchTrigger` the change event.
+ this._triggerChange(prop, changeType, value, current);
+
+ //Observe.triggerBatch(this, prop, [value, current]);
+ // If we can stop listening to our old value, do it.
+ current && unhookup([current], this._cid);
+ }
+
+ },
+ // Directly sets a property on this `object`.
+ ___set: function(prop, val) {
+ this._data[prop] = val;
+ // Add property directly for easy writing.
+ // Check if its on the `prototype` so we don't overwrite methods like `attrs`.
+ if (!(prop in this.constructor.prototype)) {
+ this[prop] = val
+ }
+ },
+
+
+ bind: can.bindAndSetup,
+
+ unbind: can.unbindAndTeardown,
+
+ serialize: function() {
+ return serialize(this, 'serialize', {});
+ },
+
+ _attrs: function(props, remove) {
+
+ if (props === undefined) {
+ return serialize(this, 'attr', {})
+ }
+
+ props = can.extend({}, props);
+ var prop,
+ self = this,
+ newVal;
+ Observe.startBatch();
+ this.each(function(curVal, prop) {
+ newVal = props[prop];
+
+ // If we are merging...
+ if (newVal === undefined) {
+ remove && self.removeAttr(prop);
+ return;
+ }
+
+ if (self.__convert) {
+ newVal = self.__convert(prop, newVal)
+ }
+
+ // if we're dealing with models, want to call _set to let converter run
+ if (newVal instanceof can.Observe) {
+ self.__set(prop, newVal, curVal)
+ // if its an object, let attr merge
+ } else if (canMakeObserve(curVal) && canMakeObserve(newVal) && curVal.attr) {
+ curVal.attr(newVal, remove)
+ // otherwise just set
+ } else if (curVal != newVal) {
+ self.__set(prop, newVal, curVal)
+ }
+
+ delete props[prop];
+ })
+ // Add remaining props.
+ for (var prop in props) {
+ newVal = props[prop];
+ this._set(prop, newVal, true)
+ }
+ Observe.stopBatch()
+ return this;
+ },
+
+
+ compute: function(prop) {
+ return can.compute(this, prop);
+ }
+ });
+ // Helpers for `observable` lists.
+ var splice = [].splice,
+
+ list = Observe(
+
+ {
+ setup: function(instances, options) {
+ this.length = 0;
+ can.cid(this, ".observe")
+ this._init = 1;
+ if (can.isDeferred(instances)) {
+ this.replace(instances)
+ } else {
+ this.push.apply(this, can.makeArray(instances || []));
+ }
+ // this change needs to be ignored
+ this.bind('change' + this._cid, can.proxy(this._changes, this));
+ can.extend(this, options);
+ delete this._init;
+ },
+ _triggerChange: function(attr, how, newVal, oldVal) {
+
+ Observe.prototype._triggerChange.apply(this, arguments)
+ // `batchTrigger` direct add and remove events...
+ if (!~attr.indexOf('.')) {
+
+ if (how === 'add') {
+ Observe.triggerBatch(this, how, [newVal, +attr]);
+ Observe.triggerBatch(this, 'length', [this.length]);
+ } else if (how === 'remove') {
+ Observe.triggerBatch(this, how, [oldVal, +attr]);
+ Observe.triggerBatch(this, 'length', [this.length]);
+ } else {
+ Observe.triggerBatch(this, how, [newVal, +attr])
+ }
+
+ }
+
+ },
+ __get: function(attr) {
+ return attr ? this[attr] : this;
+ },
+ ___set: function(attr, val) {
+ this[attr] = val;
+ if (+attr >= this.length) {
+ this.length = (+attr + 1)
+ }
+ },
+ _each: function(callback) {
+ var data = this.__get();
+ for (var i = 0; i < data.length; i++) {
+ callback(data[i], i)
+ }
+ },
+ _bindsetup: makeBindSetup("*"),
+ // Returns the serialized form of this list.
+
+ serialize: function() {
+ return serialize(this, 'serialize', []);
+ },
+
+ splice: function(index, howMany) {
+ var args = can.makeArray(arguments),
+ i;
+
+ for (i = 2; i < args.length; i++) {
+ var val = args[i];
+ if (canMakeObserve(val)) {
+ args[i] = hookupBubble(val, "*", this, this.constructor.Observe, this.constructor)
+ }
+ }
+ if (howMany === undefined) {
+ howMany = args[1] = this.length - index;
+ }
+ var removed = splice.apply(this, args);
+ can.Observe.startBatch();
+ if (howMany > 0) {
+ this._triggerChange("" + index, "remove", undefined, removed);
+ unhookup(removed, this._cid);
+ }
+ if (args.length > 2) {
+ this._triggerChange("" + index, "add", args.slice(2), removed);
+ }
+ can.Observe.stopBatch();
+ return removed;
+ },
+
+ _attrs: function(items, remove) {
+ if (items === undefined) {
+ return serialize(this, 'attr', []);
+ }
+
+ // Create a copy.
+ items = can.makeArray(items);
+
+ Observe.startBatch();
+ this._updateAttrs(items, remove);
+ Observe.stopBatch()
+ },
+
+ _updateAttrs: function(items, remove) {
+ var len = Math.min(items.length, this.length);
+
+ for (var prop = 0; prop < len; prop++) {
+ var curVal = this[prop],
+ newVal = items[prop];
+
+ if (canMakeObserve(curVal) && canMakeObserve(newVal)) {
+ curVal.attr(newVal, remove)
+ } else if (curVal != newVal) {
+ this._set(prop, newVal)
+ } else {
+
+ }
+ }
+ if (items.length > this.length) {
+ // Add in the remaining props.
+ this.push.apply(this, items.slice(this.length));
+ } else if (items.length < this.length && remove) {
+ this.splice(items.length)
+ }
+ }
+ }),
+
+ // Converts to an `array` of arguments.
+ getArgs = function(args) {
+ return args[0] && can.isArray(args[0]) ?
+ args[0] :
+ can.makeArray(args);
+ };
+ // Create `push`, `pop`, `shift`, and `unshift`
+ can.each({
+
+ push: "length",
+
+ unshift: 0
+ },
+ // Adds a method
+ // `name` - The method name.
+ // `where` - Where items in the `array` should be added.
+
+ function(where, name) {
+ var orig = [][name]
+ list.prototype[name] = function() {
+ // Get the items being added.
+ var args = [],
+ // Where we are going to add items.
+ len = where ? this.length : 0,
+ i = arguments.length,
+ res,
+ val,
+ constructor = this.constructor;
+
+ // Go through and convert anything to an `observe` that needs to be converted.
+ while (i--) {
+ val = arguments[i];
+ args[i] = canMakeObserve(val) ?
+ hookupBubble(val, "*", this, this.constructor.Observe, this.constructor) :
+ val;
+ }
+
+ // Call the original method.
+ res = orig.apply(this, args);
+
+ if (!this.comparator || args.length) {
+
+ this._triggerChange("" + len, "add", args, undefined);
+ }
+
+ return res;
+ }
+ });
+
+ can.each({
+
+ pop: "length",
+
+ shift: 0
+ },
+ // Creates a `remove` type method
+
+ function(where, name) {
+ list.prototype[name] = function() {
+
+ var args = getArgs(arguments),
+ len = where && this.length ? this.length - 1 : 0;
+
+ var res = [][name].apply(this, args)
+
+ // Create a change where the args are
+ // `len` - Where these items were removed.
+ // `remove` - Items removed.
+ // `undefined` - The new values (there are none).
+ // `res` - The old, removed values (should these be unbound).
+ this._triggerChange("" + len, "remove", undefined, [res])
+
+ if (res && res.unbind) {
+ res.unbind("change" + this._cid)
+ }
+ return res;
+ }
+ });
+
+ can.extend(list.prototype, {
+
+ indexOf: function(item) {
+ this.attr('length')
+ return can.inArray(item, this)
+ },
+
+
+ join: [].join,
+
+
+ reverse: [].reverse,
+
+
+ slice: function() {
+ var temp = Array.prototype.slice.apply(this, arguments);
+ return new this.constructor(temp);
+ },
+
+
+ concat: function() {
+ var args = [];
+ can.each(can.makeArray(arguments), function(arg, i) {
+ args[i] = arg instanceof can.Observe.List ? arg.serialize() : arg;
+ });
+ return new this.constructor(Array.prototype.concat.apply(this.serialize(), args));
+ },
+
+
+ forEach: function(cb, thisarg) {
+ can.each(this, cb, thisarg || this);
+ },
+
+
+ replace: function(newList) {
+ if (can.isDeferred(newList)) {
+ newList.then(can.proxy(this.replace, this));
+ } else {
+ this.splice.apply(this, [0, this.length].concat(can.makeArray(newList || [])));
+ }
+
+ return this;
+ }
+ });
+
+ can.List = Observe.List = list;
+ Observe.setup = function() {
+ can.Construct.setup.apply(this, arguments);
+ // I would prefer not to do it this way. It should
+ // be using the attributes plugin to do this type of conversion.
+ this.List = Observe.List({
+ Observe: this
+ }, {});
+ }
+ return Observe;
+ })(__m3, __m13, __m1);
+
+ // ## observe/compute/compute.js
+ var __m14 = (function(can, bind) {
+
+ // returns the
+ // - observes and attr methods are called by func
+ // - the value returned by func
+ // ex: `{value: 100, observed: [{obs: o, attr: "completed"}]}`
+ var getValueAndObserved = function(func, self) {
+
+ var oldReading;
+ if (can.Observe) {
+ // Set a callback on can.Observe to know
+ // when an attr is read.
+ // Keep a reference to the old reader
+ // if there is one. This is used
+ // for nested live binding.
+ oldReading = can.Observe.__reading;
+ can.Observe.__reading = function(obj, attr) {
+ // Add the observe and attr that was read
+ // to `observed`
+ observed.push({
+ obj: obj,
+ attr: attr + ""
+ });
+ };
+ }
+
+ var observed = [],
+ // Call the "wrapping" function to get the value. `observed`
+ // will have the observe/attribute pairs that were read.
+ value = func.call(self);
+
+ // Set back so we are no longer reading.
+ if (can.Observe) {
+ can.Observe.__reading = oldReading;
+ }
+ return {
+ value: value,
+ observed: observed
+ };
+ },
+ // Calls `callback(newVal, oldVal)` everytime an observed property
+ // called within `getterSetter` is changed and creates a new result of `getterSetter`.
+ // Also returns an object that can teardown all event handlers.
+ computeBinder = function(getterSetter, context, callback, computeState) {
+ // track what we are observing
+ var observing = {},
+ // a flag indicating if this observe/attr pair is already bound
+ matched = true,
+ // the data to return
+ data = {
+ // we will maintain the value while live-binding is taking place
+ value: undefined,
+ // a teardown method that stops listening
+ teardown: function() {
+ for (var name in observing) {
+ var ob = observing[name];
+ ob.observe.obj.unbind(ob.observe.attr, onchanged);
+ delete observing[name];
+ }
+ }
+ },
+ batchNum;
+
+ // when a property value is changed
+ var onchanged = function(ev) {
+ // If the compute is no longer bound (because the same change event led to an unbind)
+ // then do not call getValueAndBind, or we will leak bindings.
+ if (computeState && !computeState.bound) {
+ return;
+ }
+ if (ev.batchNum === undefined || ev.batchNum !== batchNum) {
+ // store the old value
+ var oldValue = data.value,
+ // get the new value
+ newvalue = getValueAndBind();
+
+ // update the value reference (in case someone reads)
+ data.value = newvalue;
+ // if a change happened
+ if (newvalue !== oldValue) {
+ callback(newvalue, oldValue);
+ }
+ batchNum = batchNum = ev.batchNum;
+ }
+
+
+ };
+
+ // gets the value returned by `getterSetter` and also binds to any attributes
+ // read by the call
+ var getValueAndBind = function() {
+ var info = getValueAndObserved(getterSetter, context),
+ newObserveSet = info.observed;
+
+ var value = info.value;
+ matched = !matched;
+
+ // go through every attribute read by this observe
+ can.each(newObserveSet, function(ob) {
+ // if the observe/attribute pair is being observed
+ if (observing[ob.obj._cid + "|" + ob.attr]) {
+ // mark at as observed
+ observing[ob.obj._cid + "|" + ob.attr].matched = matched;
+ } else {
+ // otherwise, set the observe/attribute on oldObserved, marking it as being observed
+ observing[ob.obj._cid + "|" + ob.attr] = {
+ matched: matched,
+ observe: ob
+ };
+ ob.obj.bind(ob.attr, onchanged);
+ }
+ });
+
+ // Iterate through oldObserved, looking for observe/attributes
+ // that are no longer being bound and unbind them
+ for (var name in observing) {
+ var ob = observing[name];
+ if (ob.matched !== matched) {
+ ob.observe.obj.unbind(ob.observe.attr, onchanged);
+ delete observing[name];
+ }
+ }
+ return value;
+ };
+ // set the initial value
+ data.value = getValueAndBind();
+
+ data.isListening = !can.isEmptyObject(observing);
+ return data;
+ }
+
+ // if no one is listening ... we can not calculate every time
+
+ can.compute = function(getterSetter, context, eventName) {
+ if (getterSetter && getterSetter.isComputed) {
+ return getterSetter;
+ }
+ // stores the result of computeBinder
+ var computedData,
+ // how many listeners to this this compute
+ bindings = 0,
+ // the computed object
+ computed,
+ // an object that keeps track if the computed is bound
+ // onchanged needs to know this. It's possible a change happens and results in
+ // something that unbinds the compute, it needs to not to try to recalculate who it
+ // is listening to
+ computeState = {
+ bound: false,
+ // true if this compute is calculated from other computes and observes
+ hasDependencies: false
+ },
+ // The following functions are overwritten depending on how compute() is called
+ // a method to setup listening
+ on = function() {},
+ // a method to teardown listening
+ off = function() {},
+ // the current cached value (only valid if bound = true)
+ value,
+ // how to read the value
+ get = function() {
+ return value
+ },
+ // sets the value
+ set = function(newVal) {
+ value = newVal;
+ },
+ // this compute can be a dependency of other computes
+ canReadForChangeEvent = true;
+
+ computed = function(newVal) {
+ // setting ...
+ if (arguments.length) {
+ // save a reference to the old value
+ var old = value;
+
+ // setter may return a value if
+ // setter is for a value maintained exclusively by this compute
+ var setVal = set.call(context, newVal, old);
+
+ // if this has dependencies return the current value
+ if (computed.hasDependencies) {
+ return get.call(context);
+ }
+
+ if (setVal === undefined) {
+ // it's possible, like with the DOM, setting does not
+ // fire a change event, so we must read
+ value = get.call(context);
+ } else {
+ value = setVal;
+ }
+ // fire the change
+ if (old !== value) {
+ can.Observe.triggerBatch(computed, "change", [value, old]);
+ }
+ return value;
+ } else {
+ // Let others know to listen to changes in this compute
+ if (can.Observe.__reading && canReadForChangeEvent) {
+ can.Observe.__reading(computed, 'change');
+ }
+ // if we are bound, use the cached value
+ if (computeState.bound) {
+ return value;
+ } else {
+ return get.call(context);
+ }
+ }
+ }
+ if (typeof getterSetter === "function") {
+ set = getterSetter;
+ get = getterSetter;
+ canReadForChangeEvent = eventName === false ? false : true;
+ computed.hasDependencies = false;
+ on = function(update) {
+ computedData = computeBinder(getterSetter, context || this, update, computeState);
+ computed.hasDependencies = computedData.isListening
+ value = computedData.value;
+ }
+ off = function() {
+ computedData.teardown();
+ }
+ } else if (context) {
+
+ if (typeof context == "string") {
+ // `can.compute(obj, "propertyName", [eventName])`
+
+ var propertyName = context,
+ isObserve = getterSetter instanceof can.Observe;
+ if (isObserve) {
+ computed.hasDependencies = true;
+ }
+ get = function() {
+ if (isObserve) {
+ return getterSetter.attr(propertyName);
+ } else {
+ return getterSetter[propertyName];
+ }
+ }
+ set = function(newValue) {
+ if (isObserve) {
+ getterSetter.attr(propertyName, newValue)
+ } else {
+ getterSetter[propertyName] = newValue;
+ }
+ }
+ var handler;
+ on = function(update) {
+ handler = function() {
+ update(get(), value)
+ };
+ can.bind.call(getterSetter, eventName || propertyName, handler)
+
+ // use getValueAndObserved because
+ // we should not be indicating that some parent
+ // reads this property if it happens to be binding on it
+ value = getValueAndObserved(get).value
+ }
+ off = function() {
+ can.unbind.call(getterSetter, eventName || propertyName, handler)
+ }
+
+ } else {
+ // `can.compute(initialValue, setter)`
+ if (typeof context === "function") {
+ value = getterSetter;
+ set = context;
+ } else {
+ // `can.compute(initialValue,{get:, set:, on:, off:})`
+ value = getterSetter;
+ var options = context;
+ get = options.get || get;
+ set = options.set || set;
+ on = options.on || on;
+ off = options.off || off;
+ }
+
+ }
+
+
+
+ } else {
+ // `can.compute(5)`
+ value = getterSetter;
+ }
+
+ computed.isComputed = true;
+
+ can.cid(computed, "compute")
+
+ var updater = function(newValue, oldValue) {
+ value = newValue;
+ // might need a way to look up new and oldVal
+ can.Observe.triggerBatch(computed, "change", [newValue, oldValue])
+ }
+
+ return can.extend(computed, {
+ _bindsetup: function() {
+ computeState.bound = true;
+ // setup live-binding
+ on.call(this, updater)
+ },
+ _bindteardown: function() {
+ off.call(this, updater)
+ computeState.bound = false;
+ },
+
+ bind: can.bindAndSetup,
+
+ unbind: can.unbindAndTeardown
+ });
+ };
+ can.compute.binder = computeBinder;
+ return can.compute;
+ })(__m3, __m13);
+
+ // ## model/model.js
+ var __m15 = (function(can) {
+
+ // ## model.js
+ // `can.Model`
+ // _A `can.Observe` that connects to a RESTful interface._
+ // Generic deferred piping function
+
+ var pipe = function(def, model, func) {
+ var d = new can.Deferred();
+ def.then(function() {
+ var args = can.makeArray(arguments);
+ args[0] = model[func](args[0]);
+ d.resolveWith(d, args);
+ }, function() {
+ d.rejectWith(this, arguments);
+ });
+
+ if (typeof def.abort === 'function') {
+ d.abort = function() {
+ return def.abort();
+ }
+ }
+
+ return d;
+ },
+ modelNum = 0,
+ ignoreHookup = /change.observe\d+/,
+ getId = function(inst) {
+ // Instead of using attr, use __get for performance.
+ // Need to set reading
+ can.Observe.__reading && can.Observe.__reading(inst, inst.constructor.id)
+ return inst.__get(inst.constructor.id);
+ },
+ // Ajax `options` generator function
+ ajax = function(ajaxOb, data, type, dataType, success, error) {
+
+ var params = {};
+
+ // If we get a string, handle it.
+ if (typeof ajaxOb == "string") {
+ // If there's a space, it's probably the type.
+ var parts = ajaxOb.split(/\s+/);
+ params.url = parts.pop();
+ if (parts.length) {
+ params.type = parts.pop();
+ }
+ } else {
+ can.extend(params, ajaxOb);
+ }
+
+ // If we are a non-array object, copy to a new attrs.
+ params.data = typeof data == "object" && !can.isArray(data) ?
+ can.extend(params.data || {}, data) : data;
+
+ // Get the url with any templated values filled out.
+ params.url = can.sub(params.url, params.data, true);
+
+ return can.ajax(can.extend({
+ type: type || "post",
+ dataType: dataType || "json",
+ success: success,
+ error: error
+ }, params));
+ },
+ makeRequest = function(self, type, success, error, method) {
+ var args;
+ // if we pass an array as `self` it it means we are coming from
+ // the queued request, and we're passing already serialized data
+ // self's signature will be: [self, serializedData]
+ if (can.isArray(self)) {
+ args = self[1];
+ self = self[0];
+ } else {
+ args = self.serialize();
+ }
+ args = [args];
+ var deferred,
+ // The model.
+ model = self.constructor,
+ jqXHR;
+
+ // `destroy` does not need data.
+ if (type == 'destroy') {
+ args.shift();
+ }
+ // `update` and `destroy` need the `id`.
+ if (type !== 'create') {
+ args.unshift(getId(self));
+ }
+
+
+ jqXHR = model[type].apply(model, args);
+
+ deferred = jqXHR.pipe(function(data) {
+ self[method || type + "d"](data, jqXHR);
+ return self;
+ });
+
+ // Hook up `abort`
+ if (jqXHR.abort) {
+ deferred.abort = function() {
+ jqXHR.abort();
+ };
+ }
+
+ deferred.then(success, error);
+ return deferred;
+ },
+
+ // This object describes how to make an ajax request for each ajax method.
+ // The available properties are:
+ // `url` - The default url to use as indicated as a property on the model.
+ // `type` - The default http request type
+ // `data` - A method that takes the `arguments` and returns `data` used for ajax.
+
+ ajaxMethods = {
+
+ create: {
+ url: "_shortName",
+ type: "post"
+ },
+
+ update: {
+ data: function(id, attrs) {
+ attrs = attrs || {};
+ var identity = this.id;
+ if (attrs[identity] && attrs[identity] !== id) {
+ attrs["new" + can.capitalize(id)] = attrs[identity];
+ delete attrs[identity];
+ }
+ attrs[identity] = id;
+ return attrs;
+ },
+ type: "put"
+ },
+
+ destroy: {
+ type: "delete",
+ data: function(id) {
+ var args = {};
+ args.id = args[this.id] = id;
+ return args;
+ }
+ },
+
+ findAll: {
+ url: "_shortName"
+ },
+
+ findOne: {}
+ },
+ // Makes an ajax request `function` from a string.
+ // `ajaxMethod` - The `ajaxMethod` object defined above.
+ // `str` - The string the user provided. Ex: `findAll: "/recipes.json"`.
+ ajaxMaker = function(ajaxMethod, str) {
+ // Return a `function` that serves as the ajax method.
+ return function(data) {
+ // If the ajax method has it's own way of getting `data`, use that.
+ data = ajaxMethod.data ?
+ ajaxMethod.data.apply(this, arguments) :
+ // Otherwise use the data passed in.
+ data;
+ // Return the ajax method with `data` and the `type` provided.
+ return ajax(str || this[ajaxMethod.url || "_url"], data, ajaxMethod.type || "get")
+ }
+ }
+
+
+
+ can.Model = can.Observe({
+ fullName: "can.Model",
+ _reqs: 0,
+ setup: function(base) {
+ // create store here if someone wants to use model without inheriting from it
+ this.store = {};
+ can.Observe.setup.apply(this, arguments);
+ // Set default list as model list
+ if (!can.Model) {
+ return;
+ }
+ this.List = ML({
+ Observe: this
+ }, {});
+ var self = this,
+ clean = can.proxy(this._clean, self);
+
+
+ // go through ajax methods and set them up
+ can.each(ajaxMethods, function(method, name) {
+ // if an ajax method is not a function, it's either
+ // a string url like findAll: "/recipes" or an
+ // ajax options object like {url: "/recipes"}
+ if (!can.isFunction(self[name])) {
+ // use ajaxMaker to convert that into a function
+ // that returns a deferred with the data
+ self[name] = ajaxMaker(method, self[name]);
+ }
+ // check if there's a make function like makeFindAll
+ // these take deferred function and can do special
+ // behavior with it (like look up data in a store)
+ if (self["make" + can.capitalize(name)]) {
+ // pass the deferred method to the make method to get back
+ // the "findAll" method.
+ var newMethod = self["make" + can.capitalize(name)](self[name]);
+ can.Construct._overwrite(self, base, name, function() {
+ // increment the numer of requests
+ can.Model._reqs++;
+ var def = newMethod.apply(this, arguments);
+ var then = def.then(clean, clean);
+ then.abort = def.abort;
+
+ // attach abort to our then and return it
+ return then;
+ })
+ }
+ });
+
+ if (self.fullName == "can.Model" || !self.fullName) {
+ self.fullName = "Model" + (++modelNum);
+ }
+ // Add ajax converters.
+ can.Model._reqs = 0;
+ this._url = this._shortName + "/{" + this.id + "}"
+ },
+ _ajax: ajaxMaker,
+ _makeRequest: makeRequest,
+ _clean: function() {
+ can.Model._reqs--;
+ if (!can.Model._reqs) {
+ for (var id in this.store) {
+ if (!this.store[id]._bindings) {
+ delete this.store[id];
+ }
+ }
+ }
+ return arguments[0];
+ },
+
+ models: function(instancesRawData, oldList) {
+ // until "end of turn", increment reqs counter so instances will be added to the store
+ can.Model._reqs++;
+ if (!instancesRawData) {
+ return;
+ }
+
+ if (instancesRawData instanceof this.List) {
+ return instancesRawData;
+ }
+
+ // Get the list type.
+ var self = this,
+ tmp = [],
+ res = oldList instanceof can.Observe.List ? oldList : new(self.List || ML),
+ // Did we get an `array`?
+ arr = can.isArray(instancesRawData),
+
+ // Did we get a model list?
+ ml = (instancesRawData instanceof ML),
+
+ // Get the raw `array` of objects.
+ raw = arr ?
+
+ // If an `array`, return the `array`.
+ instancesRawData :
+
+ // Otherwise if a model list.
+ (ml ?
+
+ // Get the raw objects from the list.
+ instancesRawData.serialize() :
+
+ // Get the object's data.
+ instancesRawData.data),
+ i = 0;
+
+
+
+ if (res.length) {
+ res.splice(0);
+ }
+
+ can.each(raw, function(rawPart) {
+ tmp.push(self.model(rawPart));
+ });
+
+ // We only want one change event so push everything at once
+ res.push.apply(res, tmp);
+
+ if (!arr) { // Push other stuff onto `array`.
+ can.each(instancesRawData, function(val, prop) {
+ if (prop !== 'data') {
+ res.attr(prop, val);
+ }
+ })
+ }
+ // at "end of turn", clean up the store
+ setTimeout(can.proxy(this._clean, this), 1);
+ return res;
+ },
+
+ model: function(attributes) {
+ if (!attributes) {
+ return;
+ }
+ if (attributes instanceof this) {
+ attributes = attributes.serialize();
+ }
+ var id = attributes[this.id],
+ model = (id || id === 0) && this.store[id] ?
+ this.store[id].attr(attributes, this.removeAttr || false) : new this(attributes);
+ if (can.Model._reqs) {
+ this.store[attributes[this.id]] = model;
+ }
+ return model;
+ }
+ },
+
+
+ {
+
+ isNew: function() {
+ var id = getId(this);
+ return !(id || id === 0); // If `null` or `undefined`
+ },
+
+ save: function(success, error) {
+ return makeRequest(this, this.isNew() ? 'create' : 'update', success, error);
+ },
+
+ destroy: function(success, error) {
+ if (this.isNew()) {
+ var self = this;
+ var def = can.Deferred();
+ def.then(success, error);
+ return def.done(function(data) {
+ self.destroyed(data)
+ }).resolve(self);
+ }
+ return makeRequest(this, 'destroy', success, error, 'destroyed');
+ },
+
+ _bindsetup: function() {
+ this.constructor.store[this.__get(this.constructor.id)] = this;
+ return can.Observe.prototype._bindsetup.apply(this, arguments);
+ },
+
+ _bindteardown: function() {
+ delete this.constructor.store[getId(this)];
+ return can.Observe.prototype._bindteardown.apply(this, arguments)
+ },
+ // Change `id`.
+ ___set: function(prop, val) {
+ can.Observe.prototype.___set.call(this, prop, val)
+ // If we add an `id`, move it to the store.
+ if (prop === this.constructor.id && this._bindings) {
+ this.constructor.store[getId(this)] = this;
+ }
+ }
+ });
+
+ can.each({
+ makeFindAll: "models",
+ makeFindOne: "model",
+ makeCreate: "model",
+ makeUpdate: "model"
+ }, function(method, name) {
+ can.Model[name] = function(oldMethod) {
+ return function() {
+ var args = can.makeArray(arguments),
+ oldArgs = can.isFunction(args[1]) ? args.splice(0, 1) : args.splice(0, 2),
+ def = pipe(oldMethod.apply(this, oldArgs), this, method);
+ def.then(args[0], args[1]);
+ // return the original promise
+ return def;
+ };
+ };
+ });
+
+ can.each([
+
+ "created",
+
+ "updated",
+
+ "destroyed"
+ ], function(funcName) {
+ can.Model.prototype[funcName] = function(attrs) {
+ var stub,
+ constructor = this.constructor;
+
+ // Update attributes if attributes have been passed
+ stub = attrs && typeof attrs == 'object' && this.attr(attrs.attr ? attrs.attr() : attrs);
+
+ // triggers change event that bubble's like
+ // handler( 'change','1.destroyed' ). This is used
+ // to remove items on destroyed from Model Lists.
+ // but there should be a better way.
+ can.trigger(this, "change", funcName)
+
+
+ // Call event on the instance's Class
+ can.trigger(constructor, funcName, this);
+ };
+ });
+
+ // Model lists are just like `Observe.List` except that when their items are
+ // destroyed, it automatically gets removed from the list.
+
+ var ML = can.Model.List = can.Observe.List({
+ setup: function(params) {
+ if (can.isPlainObject(params) && !can.isArray(params)) {
+ can.Observe.List.prototype.setup.apply(this);
+ this.replace(this.constructor.Observe.findAll(params))
+ } else {
+ can.Observe.List.prototype.setup.apply(this, arguments);
+ }
+ },
+ _changes: function(ev, attr) {
+ can.Observe.List.prototype._changes.apply(this, arguments);
+ if (/\w+\.destroyed/.test(attr)) {
+ var index = this.indexOf(ev.target);
+ if (index != -1) {
+ this.splice(index, 1);
+ }
+ }
+ }
+ })
+
+ return can.Model;
+ })(__m3, __m12);
+
+ // ## view/view.js
+ var __m16 = (function(can) {
+ // ## view.js
+ // `can.view`
+ // _Templating abstraction._
+
+ var isFunction = can.isFunction,
+ makeArray = can.makeArray,
+ // Used for hookup `id`s.
+ hookupId = 1,
+
+ $view = can.view = can.template = function(view, data, helpers, callback) {
+ // If helpers is a `function`, it is actually a callback.
+ if (isFunction(helpers)) {
+ callback = helpers;
+ helpers = undefined;
+ }
+
+ var pipe = function(result) {
+ return $view.frag(result);
+ },
+ // In case we got a callback, we need to convert the can.view.render
+ // result to a document fragment
+ wrapCallback = isFunction(callback) ? function(frag) {
+ callback(pipe(frag));
+ } : null,
+ // Get the result.
+ result = $view.render(view, data, helpers, wrapCallback),
+ deferred = can.Deferred();
+
+ if (isFunction(result)) {
+ return result;
+ }
+
+ if (can.isDeferred(result)) {
+ result.then(function(result, data) {
+ deferred.resolve.call(deferred, pipe(result), data);
+ }, function() {
+ deferred.fail.apply(deferred, arguments);
+ });
+ return deferred;
+ }
+
+ // Convert it into a dom frag.
+ return pipe(result);
+ };
+
+ can.extend($view, {
+ // creates a frag and hooks it up all at once
+ frag: function(result, parentNode) {
+ return $view.hookup($view.fragment(result), parentNode);
+ },
+
+ // simply creates a frag
+ // this is used internally to create a frag
+ // insert it
+ // then hook it up
+ fragment: function(result) {
+ var frag = can.buildFragment(result, document.body);
+ // If we have an empty frag...
+ if (!frag.childNodes.length) {
+ frag.appendChild(document.createTextNode(''));
+ }
+ return frag;
+ },
+
+ // Convert a path like string into something that's ok for an `element` ID.
+ toId: function(src) {
+ return can.map(src.toString().split(/\/|\./g), function(part) {
+ // Dont include empty strings in toId functions
+ if (part) {
+ return part;
+ }
+ }).join("_");
+ },
+
+ hookup: function(fragment, parentNode) {
+ var hookupEls = [],
+ id,
+ func;
+
+ // Get all `childNodes`.
+ can.each(fragment.childNodes ? can.makeArray(fragment.childNodes) : fragment, function(node) {
+ if (node.nodeType === 1) {
+ hookupEls.push(node);
+ hookupEls.push.apply(hookupEls, can.makeArray(node.getElementsByTagName('*')));
+ }
+ });
+
+ // Filter by `data-view-id` attribute.
+ can.each(hookupEls, function(el) {
+ if (el.getAttribute && (id = el.getAttribute('data-view-id')) && (func = $view.hookups[id])) {
+ func(el, parentNode, id);
+ delete $view.hookups[id];
+ el.removeAttribute('data-view-id');
+ }
+ });
+
+ return fragment;
+ },
+
+
+ hookups: {},
+
+
+ hook: function(cb) {
+ $view.hookups[++hookupId] = cb;
+ return " data-view-id='" + hookupId + "'";
+ },
+
+
+ cached: {},
+
+ cachedRenderers: {},
+
+
+ cache: true,
+
+
+ register: function(info) {
+ this.types["." + info.suffix] = info;
+ },
+
+ types: {},
+
+
+ ext: ".ejs",
+
+
+ registerScript: function() {},
+
+
+ preload: function() {},
+
+
+ render: function(view, data, helpers, callback) {
+ // If helpers is a `function`, it is actually a callback.
+ if (isFunction(helpers)) {
+ callback = helpers;
+ helpers = undefined;
+ }
+
+ // See if we got passed any deferreds.
+ var deferreds = getDeferreds(data);
+
+ if (deferreds.length) { // Does data contain any deferreds?
+ // The deferred that resolves into the rendered content...
+ var deferred = new can.Deferred(),
+ dataCopy = can.extend({}, data);
+
+ // Add the view request to the list of deferreds.
+ deferreds.push(get(view, true))
+
+ // Wait for the view and all deferreds to finish...
+ can.when.apply(can, deferreds).then(function(resolved) {
+ // Get all the resolved deferreds.
+ var objs = makeArray(arguments),
+ // Renderer is the last index of the data.
+ renderer = objs.pop(),
+ // The result of the template rendering with data.
+ result;
+
+ // Make data look like the resolved deferreds.
+ if (can.isDeferred(data)) {
+ dataCopy = usefulPart(resolved);
+ } else {
+ // Go through each prop in data again and
+ // replace the defferreds with what they resolved to.
+ for (var prop in data) {
+ if (can.isDeferred(data[prop])) {
+ dataCopy[prop] = usefulPart(objs.shift());
+ }
+ }
+ }
+
+ // Get the rendered result.
+ result = renderer(dataCopy, helpers);
+
+ // Resolve with the rendered view.
+ deferred.resolve(result, dataCopy);
+
+ // If there's a `callback`, call it back with the result.
+ callback && callback(result, dataCopy);
+ }, function() {
+ deferred.reject.apply(deferred, arguments)
+ });
+ // Return the deferred...
+ return deferred;
+ } else {
+ // No deferreds! Render this bad boy.
+ var response,
+ // If there's a `callback` function
+ async = isFunction(callback),
+ // Get the `view` type
+ deferred = get(view, async);
+
+ // If we are `async`...
+ if (async) {
+ // Return the deferred
+ response = deferred;
+ // And fire callback with the rendered result.
+ deferred.then(function(renderer) {
+ callback(data ? renderer(data, helpers) : renderer);
+ })
+ } else {
+ // if the deferred is resolved, call the cached renderer instead
+ // this is because it's possible, with recursive deferreds to
+ // need to render a view while its deferred is _resolving_. A _resolving_ deferred
+ // is a deferred that was just resolved and is calling back it's success callbacks.
+ // If a new success handler is called while resoliving, it does not get fired by
+ // jQuery's deferred system. So instead of adding a new callback
+ // we use the cached renderer.
+ // We also add __view_id on the deferred so we can look up it's cached renderer.
+ // In the future, we might simply store either a deferred or the cached result.
+ if (deferred.state() === "resolved" && deferred.__view_id) {
+ var currentRenderer = $view.cachedRenderers[deferred.__view_id];
+ return data ? currentRenderer(data, helpers) : currentRenderer;
+ } else {
+ // Otherwise, the deferred is complete, so
+ // set response to the result of the rendering.
+ deferred.then(function(renderer) {
+ response = data ? renderer(data, helpers) : renderer;
+ });
+ }
+ }
+
+ return response;
+ }
+ },
+
+
+ registerView: function(id, text, type, def) {
+ // Get the renderer function.
+ var func = (type || $view.types[$view.ext]).renderer(id, text);
+ def = def || new can.Deferred();
+
+ // Cache if we are caching.
+ if ($view.cache) {
+ $view.cached[id] = def;
+ def.__view_id = id;
+ $view.cachedRenderers[id] = func;
+ }
+
+ // Return the objects for the response's `dataTypes`
+ // (in this case view).
+ return def.resolve(func);
+ }
+ });
+
+ // Makes sure there's a template, if not, have `steal` provide a warning.
+ var checkText = function(text, url) {
+ if (!text.length) {
+
+ throw "can.view: No template or empty template:" + url;
+ }
+ },
+ // `Returns a `view` renderer deferred.
+ // `url` - The url to the template.
+ // `async` - If the ajax request should be asynchronous.
+ // Returns a deferred.
+ get = function(url, async) {
+ var suffix = url.match(/\.[\w\d]+$/),
+ type,
+ // If we are reading a script element for the content of the template,
+ // `el` will be set to that script element.
+ el,
+ // A unique identifier for the view (used for caching).
+ // This is typically derived from the element id or
+ // the url for the template.
+ id,
+ // The ajax request used to retrieve the template content.
+ jqXHR;
+
+ //If the url has a #, we assume we want to use an inline template
+ //from a script element and not current page's HTML
+ if (url.match(/^#/)) {
+ url = url.substr(1);
+ }
+ // If we have an inline template, derive the suffix from the `text/???` part.
+ // This only supports `<script>` tags.
+ if (el = document.getElementById(url)) {
+ suffix = "." + el.type.match(/\/(x\-)?(.+)/)[2];
+ }
+
+ // If there is no suffix, add one.
+ if (!suffix && !$view.cached[url]) {
+ url += (suffix = $view.ext);
+ }
+
+ if (can.isArray(suffix)) {
+ suffix = suffix[0]
+ }
+
+ // Convert to a unique and valid id.
+ id = $view.toId(url);
+
+ // If an absolute path, use `steal` to get it.
+ // You should only be using `//` if you are using `steal`.
+ if (url.match(/^\/\//)) {
+ var sub = url.substr(2);
+ url = !window.steal ?
+ sub :
+ steal.config().root.mapJoin("" + steal.id(sub));
+ }
+
+ // Set the template engine type.
+ type = $view.types[suffix];
+
+ // If it is cached,
+ if ($view.cached[id]) {
+ // Return the cached deferred renderer.
+ return $view.cached[id];
+
+ // Otherwise if we are getting this from a `<script>` element.
+ } else if (el) {
+ // Resolve immediately with the element's `innerHTML`.
+ return $view.registerView(id, el.innerHTML, type);
+ } else {
+ // Make an ajax request for text.
+ var d = new can.Deferred();
+ can.ajax({
+ async: async,
+ url: url,
+ dataType: "text",
+ error: function(jqXHR) {
+ checkText("", url);
+ d.reject(jqXHR);
+ },
+ success: function(text) {
+ // Make sure we got some text back.
+ checkText(text, url);
+ $view.registerView(id, text, type, d)
+ }
+ });
+ return d;
+ }
+ },
+ // Gets an `array` of deferreds from an `object`.
+ // This only goes one level deep.
+ getDeferreds = function(data) {
+ var deferreds = [];
+
+ // pull out deferreds
+ if (can.isDeferred(data)) {
+ return [data]
+ } else {
+ for (var prop in data) {
+ if (can.isDeferred(data[prop])) {
+ deferreds.push(data[prop]);
+ }
+ }
+ }
+ return deferreds;
+ },
+ // Gets the useful part of a resolved deferred.
+ // This is for `model`s and `can.ajax` that resolve to an `array`.
+ usefulPart = function(resolved) {
+ return can.isArray(resolved) && resolved[1] === 'success' ? resolved[0] : resolved
+ };
+
+ //!steal-pluginify-remove-start
+ if (window.steal) {
+ steal.type("view js", function(options, success, error) {
+ var type = $view.types["." + options.type],
+ id = $view.toId(options.id);
+
+ options.text = "steal('" + (type.plugin || "can/view/" + options.type) + "',function(can){return " + "can.view.preload('" + id + "'," + options.text + ");\n})";
+ success();
+ })
+ }
+ //!steal-pluginify-remove-end
+
+ can.extend($view, {
+ register: function(info) {
+ this.types["." + info.suffix] = info;
+
+ //!steal-pluginify-remove-start
+ if (window.steal) {
+ steal.type(info.suffix + " view js", function(options, success, error) {
+ var type = $view.types["." + options.type],
+ id = $view.toId(options.id + '');
+
+ options.text = type.script(id, options.text)
+ success();
+ })
+ };
+ //!steal-pluginify-remove-end
+
+ $view[info.suffix] = function(id, text) {
+ if (!text) {
+ // Return a nameless renderer
+ var renderer = function() {
+ return $view.frag(renderer.render.apply(this, arguments));
+ }
+ renderer.render = function() {
+ var renderer = info.renderer(null, id);
+ return renderer.apply(renderer, arguments);
+ }
+ return renderer;
+ }
+
+ $view.preload(id, info.renderer(id, text));
+ return can.view(id);
+ }
+ },
+ registerScript: function(type, id, src) {
+ return "can.view.preload('" + id + "'," + $view.types["." + type].script(id, src) + ");";
+ },
+ preload: function(id, renderer) {
+ $view.cached[id] = new can.Deferred().resolve(function(data, helpers) {
+ return renderer.call(data, data, helpers);
+ });
+
+ function frag() {
+ return $view.frag(renderer.apply(this, arguments));
+ }
+ // expose the renderer for mustache
+ frag.render = renderer;
+ return frag;
+ }
+
+ });
+
+ return can;
+ })(__m3);
+
+ // ## view/elements.js
+ var __m19 = (function() {
+
+ var elements = {
+ tagToContentPropMap: {
+ option: "textContent" in document.createElement("option") ? "textContent" : "innerText",
+ textarea: "value"
+ },
+
+ attrMap: {
+ "class": "className",
+ "value": "value",
+ "innerText": "innerText",
+ "textContent": "textContent",
+ "checked": true,
+ "disabled": true,
+ "readonly": true,
+ "required": true
+ },
+ // elements whos default value we should set
+ defaultValue: ["input", "textarea"],
+ // a map of parent element to child elements
+ tagMap: {
+ "": "span",
+ table: "tbody",
+ tr: "td",
+ ol: "li",
+ ul: "li",
+ tbody: "tr",
+ thead: "tr",
+ tfoot: "tr",
+ select: "option",
+ optgroup: "option"
+ },
+ // a tag's parent element
+ reverseTagMap: {
+ tr: "tbody",
+ option: "select",
+ td: "tr",
+ th: "tr",
+ li: "ul"
+ },
+
+ getParentNode: function(el, defaultParentNode) {
+ return defaultParentNode && el.parentNode.nodeType === 11 ? defaultParentNode : el.parentNode;
+ },
+ // set an attribute on an element
+ setAttr: function(el, attrName, val) {
+ var tagName = el.nodeName.toString().toLowerCase(),
+ prop = elements.attrMap[attrName];
+ // if this is a special property
+ if (prop === true) {
+ el[attrName] = true;
+ } else if (prop) {
+ // set the value as true / false
+ el[prop] = val;
+ if (prop === "value" && can.inArray(tagName, elements.defaultValue) >= 0) {
+ el.defaultValue = val;
+ }
+ } else {
+ el.setAttribute(attrName, val);
+ }
+ },
+ // gets the value of an attribute
+ getAttr: function(el, attrName) {
+ // Default to a blank string for IE7/8
+ return (elements.attrMap[attrName] && el[elements.attrMap[attrName]] ?
+ el[elements.attrMap[attrName]] :
+ el.getAttribute(attrName)) || '';
+ },
+ // removes the attribute
+ removeAttr: function(el, attrName) {
+ if (elements.attrMap[attrName] === true) {
+ el[attrName] = false;
+ } else {
+ el.removeAttribute(attrName);
+ }
+ },
+ contentText: function(text) {
+ if (typeof text == 'string') {
+ return text;
+ }
+ // If has no value, return an empty string.
+ if (!text && text !== 0) {
+ return '';
+ }
+ return "" + text;
+ }
+ };
+
+ return elements;
+ })();
+
+ // ## view/scanner.js
+ var __m18 = (function(can, elements) {
+
+ var newLine = /(\r|\n)+/g,
+ // Escapes characters starting with `\`.
+ clean = function(content) {
+ return content
+ .split('\\').join("\\\\")
+ .split("\n").join("\\n")
+ .split('"').join('\\"')
+ .split("\t").join("\\t");
+ },
+ // Returns a tagName to use as a temporary placeholder for live content
+ // looks forward ... could be slow, but we only do it when necessary
+ getTag = function(tagName, tokens, i) {
+ // if a tagName is provided, use that
+ if (tagName) {
+ return tagName;
+ } else {
+ // otherwise go searching for the next two tokens like "<",TAG
+ while (i < tokens.length) {
+ if (tokens[i] == "<" && elements.reverseTagMap[tokens[i + 1]]) {
+ return elements.reverseTagMap[tokens[i + 1]];
+ }
+ i++;
+ }
+ }
+ return '';
+ },
+ bracketNum = function(content) {
+ return (--content.split("{").length) - (--content.split("}").length);
+ },
+ myEval = function(script) {
+ eval(script);
+ },
+ attrReg = /([^\s]+)[\s]*=[\s]*$/,
+ // Commands for caching.
+ startTxt = 'var ___v1ew = [];',
+ finishTxt = "return ___v1ew.join('')",
+ put_cmd = "___v1ew.push(",
+ insert_cmd = put_cmd,
+ // Global controls (used by other functions to know where we are).
+ // Are we inside a tag?
+ htmlTag = null,
+ // Are we within a quote within a tag?
+ quote = null,
+ // What was the text before the current quote? (used to get the `attr` name)
+ beforeQuote = null,
+ // Whether a rescan is in progress
+ rescan = null,
+ // Used to mark where the element is.
+ status = function() {
+ // `t` - `1`.
+ // `h` - `0`.
+ // `q` - String `beforeQuote`.
+ return quote ? "'" + beforeQuote.match(attrReg)[1] + "'" : (htmlTag ? 1 : 0);
+ };
+
+ can.view.Scanner = Scanner = function(options) {
+ // Set options on self
+ can.extend(this, {
+ text: {},
+ tokens: []
+ }, options);
+
+ // Cache a token lookup
+ this.tokenReg = [];
+ this.tokenSimple = {
+ "<": "<",
+ ">": ">",
+ '"': '"',
+ "'": "'"
+ };
+ this.tokenComplex = [];
+ this.tokenMap = {};
+ for (var i = 0, token; token = this.tokens[i]; i++) {
+
+
+ // Save complex mappings (custom regexp)
+ if (token[2]) {
+ this.tokenReg.push(token[2]);
+ this.tokenComplex.push({
+ abbr: token[1],
+ re: new RegExp(token[2]),
+ rescan: token[3]
+ });
+ }
+ // Save simple mappings (string only, no regexp)
+ else {
+ this.tokenReg.push(token[1]);
+ this.tokenSimple[token[1]] = token[0];
+ }
+ this.tokenMap[token[0]] = token[1];
+ }
+
+ // Cache the token registry.
+ this.tokenReg = new RegExp("(" + this.tokenReg.slice(0).concat(["<", ">", '"', "'"]).join("|") + ")", "g");
+ };
+
+ Scanner.prototype = {
+
+ helpers: [
+
+ {
+ name: /\s*\(([\$\w]+)\)\s*->([^\n]*)/,
+ fn: function(content) {
+ var quickFunc = /\s*\(([\$\w]+)\)\s*->([^\n]*)/,
+ parts = content.match(quickFunc);
+
+ return "can.proxy(function(__){var " + parts[1] + "=can.$(__);" + parts[2] + "}, this);";
+ }
+ }
+ ],
+
+ scan: function(source, name) {
+ var tokens = [],
+ last = 0,
+ simple = this.tokenSimple,
+ complex = this.tokenComplex;
+
+ source = source.replace(newLine, "\n");
+ if (this.transform) {
+ source = this.transform(source);
+ }
+ source.replace(this.tokenReg, function(whole, part) {
+ // offset is the second to last argument
+ var offset = arguments[arguments.length - 2];
+
+ // if the next token starts after the last token ends
+ // push what's in between
+ if (offset > last) {
+ tokens.push(source.substring(last, offset));
+ }
+
+ // push the simple token (if there is one)
+ if (simple[whole]) {
+ tokens.push(whole);
+ }
+ // otherwise lookup complex tokens
+ else {
+ for (var i = 0, token; token = complex[i]; i++) {
+ if (token.re.test(whole)) {
+ tokens.push(token.abbr);
+ // Push a rescan function if one exists
+ if (token.rescan) {
+ tokens.push(token.rescan(part));
+ }
+ break;
+ }
+ }
+ }
+
+ // update the position of the last part of the last token
+ last = offset + part.length;
+ });
+
+ // if there's something at the end, add it
+ if (last < source.length) {
+ tokens.push(source.substr(last));
+ }
+
+ var content = '',
+ buff = [startTxt + (this.text.start || '')],
+ // Helper `function` for putting stuff in the view concat.
+ put = function(content, bonus) {
+ buff.push(put_cmd, '"', clean(content), '"' + (bonus || '') + ');');
+ },
+ // A stack used to keep track of how we should end a bracket
+ // `}`.
+ // Once we have a `<%= %>` with a `leftBracket`,
+ // we store how the file should end here (either `))` or `;`).
+ endStack = [],
+ // The last token, used to remember which tag we are in.
+ lastToken,
+ // The corresponding magic tag.
+ startTag = null,
+ // Was there a magic tag inside an html tag?
+ magicInTag = false,
+ // The current tag name.
+ tagName = '',
+ // stack of tagNames
+ tagNames = [],
+ // Pop from tagNames?
+ popTagName = false,
+ // Declared here.
+ bracketCount,
+ i = 0,
+ token,
+ tmap = this.tokenMap;
+
+ // Reinitialize the tag state goodness.
+ htmlTag = quote = beforeQuote = null;
+
+ for (;
+ (token = tokens[i++]) !== undefined;) {
+ if (startTag === null) {
+ switch (token) {
+ case tmap.left:
+ case tmap.escapeLeft:
+ case tmap.returnLeft:
+ magicInTag = htmlTag && 1;
+ case tmap.commentLeft:
+ // A new line -- just add whatever content within a clean.
+ // Reset everything.
+ startTag = token;
+ if (content.length) {
+ put(content);
+ }
+ content = '';
+ break;
+ case tmap.escapeFull:
+ // This is a full line escape (a line that contains only whitespace and escaped logic)
+ // Break it up into escape left and right
+ magicInTag = htmlTag && 1;
+ rescan = 1;
+ startTag = tmap.escapeLeft;
+ if (content.length) {
+ put(content);
+ }
+ rescan = tokens[i++];
+ content = rescan.content || rescan;
+ if (rescan.before) {
+ put(rescan.before);
+ }
+ tokens.splice(i, 0, tmap.right);
+ break;
+ case tmap.commentFull:
+ // Ignore full line comments.
+ break;
+ case tmap.templateLeft:
+ content += tmap.left;
+ break;
+ case '<':
+ // Make sure we are not in a comment.
+ if (tokens[i].indexOf("!--") !== 0) {
+ htmlTag = 1;
+ magicInTag = 0;
+ }
+ content += token;
+ break;
+ case '>':
+ htmlTag = 0;
+ // content.substr(-1) doesn't work in IE7/8
+ var emptyElement = content.substr(content.length - 1) == "/" || content.substr(content.length - 2) == "--";
+ // if there was a magic tag
+ // or it's an element that has text content between its tags,
+ // but content is not other tags add a hookup
+ // TODO: we should only add `can.EJS.pending()` if there's a magic tag
+ // within the html tags.
+ if (magicInTag || !popTagName && elements.tagToContentPropMap[tagNames[tagNames.length - 1]]) {
+ // make sure / of /> is on the left of pending
+ if (emptyElement) {
+ put(content.substr(0, content.length - 1), ",can.view.pending(),\"/>\"");
+ } else {
+ put(content, ",can.view.pending(),\">\"");
+ }
+ content = '';
+ magicInTag = 0;
+ } else {
+ content += token;
+ }
+ // if it's a tag like <input/>
+ if (emptyElement || popTagName) {
+ // remove the current tag in the stack
+ tagNames.pop();
+ // set the current tag to the previous parent
+ tagName = tagNames[tagNames.length - 1];
+ // Don't pop next time
+ popTagName = false;
+ }
+ break;
+ case "'":
+ case '"':
+ // If we are in an html tag, finding matching quotes.
+ if (htmlTag) {
+ // We have a quote and it matches.
+ if (quote && quote === token) {
+ // We are exiting the quote.
+ quote = null;
+ // Otherwise we are creating a quote.
+ // TODO: does this handle `\`?
+ } else if (quote === null) {
+ quote = token;
+ beforeQuote = lastToken;
+ }
+ }
+ default:
+ // Track the current tag
+ if (lastToken === '<') {
+ tagName = token.split(/\s/)[0];
+ if (tagName.indexOf("/") === 0 && tagNames[tagNames.length - 1] === tagName.substr(1)) {
+ // set tagName to the last tagName
+ // if there are no more tagNames, we'll rely on getTag.
+ tagName = tagNames[tagNames.length - 1];
+ popTagName = true;
+ } else {
+ tagNames.push(tagName);
+ }
+ }
+ content += token;
+ break;
+ }
+ } else {
+ // We have a start tag.
+ switch (token) {
+ case tmap.right:
+ case tmap.returnRight:
+ switch (startTag) {
+ case tmap.left:
+ // Get the number of `{ minus }`
+ bracketCount = bracketNum(content);
+
+ // We are ending a block.
+ if (bracketCount == 1) {
+
+ // We are starting on.
+ buff.push(insert_cmd, "can.view.txt(0,'" + getTag(tagName, tokens, i) + "'," + status() + ",this,function(){", startTxt, content);
+
+ endStack.push({
+ before: "",
+ after: finishTxt + "}));\n"
+ });
+ } else {
+
+ // How are we ending this statement?
+ last = // If the stack has value and we are ending a block...
+ endStack.length && bracketCount == -1 ? // Use the last item in the block stack.
+ endStack.pop() : // Or use the default ending.
+ {
+ after: ";"
+ };
+
+ // If we are ending a returning block,
+ // add the finish text which returns the result of the
+ // block.
+ if (last.before) {
+ buff.push(last.before);
+ }
+ // Add the remaining content.
+ buff.push(content, ";", last.after);
+ }
+ break;
+ case tmap.escapeLeft:
+ case tmap.returnLeft:
+ // We have an extra `{` -> `block`.
+ // Get the number of `{ minus }`.
+ bracketCount = bracketNum(content);
+ // If we have more `{`, it means there is a block.
+ if (bracketCount) {
+ // When we return to the same # of `{` vs `}` end with a `doubleParent`.
+ endStack.push({
+ before: finishTxt,
+ after: "}));"
+ });
+ }
+
+ var escaped = startTag === tmap.escapeLeft ? 1 : 0,
+ commands = {
+ insert: insert_cmd,
+ tagName: getTag(tagName, tokens, i),
+ status: status()
+ };
+
+ for (var ii = 0; ii < this.helpers.length; ii++) {
+ // Match the helper based on helper
+ // regex name value
+ var helper = this.helpers[ii];
+ if (helper.name.test(content)) {
+ content = helper.fn(content, commands);
+
+ // dont escape partials
+ if (helper.name.source == /^>[\s]*\w*/.source) {
+ escaped = 0;
+ }
+ break;
+ }
+ }
+
+ // Handle special cases
+ if (typeof content == 'object') {
+ if (content.raw) {
+ buff.push(content.raw);
+ }
+ } else {
+ // If we have `<%== a(function(){ %>` then we want
+ // `can.EJS.text(0,this, function(){ return a(function(){ var _v1ew = [];`.
+ buff.push(insert_cmd, "can.view.txt(" + escaped + ",'" + tagName + "'," + status() + ",this,function(){ " + (this.text.escape || '') + "return ", content,
+ // If we have a block.
+ bracketCount ?
+ // Start with startTxt `"var _v1ew = [];"`.
+ startTxt :
+ // If not, add `doubleParent` to close push and text.
+ "}));");
+ }
+
+ if (rescan && rescan.after && rescan.after.length) {
+ put(rescan.after.length);
+ rescan = null;
+ }
+ break;
+ }
+ startTag = null;
+ content = '';
+ break;
+ case tmap.templateLeft:
+ content += tmap.left;
+ break;
+ default:
+ content += token;
+ break;
+ }
+ }
+ lastToken = token;
+ }
+
+ // Put it together...
+ if (content.length) {
+ // Should be `content.dump` in Ruby.
+ put(content);
+ }
+ buff.push(";");
+
+ var template = buff.join(''),
+ out = {
+ out: 'with(_VIEW) { with (_CONTEXT) {' + template + " " + finishTxt + "}}"
+ };
+ // Use `eval` instead of creating a function, because it is easier to debug.
+ myEval.call(out, 'this.fn = (function(_CONTEXT,_VIEW){' + out.out + '});\r\n//@ sourceURL=' + name + ".js");
+
+ return out;
+ }
+ };
+
+ return Scanner;
+ })(__m16, __m19);
+
+ // ## view/node_lists.js
+ var __m22 = (function(can) {
+
+ // text node expando test
+ var canExpando = true;
+ try {
+ document.createTextNode('')._ = 0;
+ } catch (ex) {
+ canExpando = false;
+ }
+
+ // a mapping of element ids to nodeList ids
+ var nodeMap = {},
+ // a mapping of ids to text nodes
+ textNodeMap = {},
+ // a mapping of nodeList ids to nodeList
+ nodeListMap = {},
+ expando = "ejs_" + Math.random(),
+ _id = 0,
+ id = function(node) {
+ if (canExpando || node.nodeType !== 3) {
+ if (node[expando]) {
+ return node[expando];
+ } else {
+ return node[expando] = (node.nodeName ? "element_" : "obj_") + (++_id);
+ }
+ } else {
+ for (var textNodeID in textNodeMap) {
+ if (textNodeMap[textNodeID] === node) {
+ return textNodeID;
+ }
+ }
+
+ textNodeMap["text_" + (++_id)] = node;
+ return "text_" + _id;
+ }
+ },
+ // removes a nodeListId from a node's nodeListIds
+ removeNodeListId = function(node, nodeListId) {
+ var nodeListIds = nodeMap[id(node)];
+ if (nodeListIds) {
+ var index = can.inArray(nodeListId, nodeListIds);
+
+ if (index >= 0) {
+ nodeListIds.splice(index, 1);
+ }
+ if (!nodeListIds.length) {
+ delete nodeMap[id(node)];
+ }
+ }
+ },
+ addNodeListId = function(node, nodeListId) {
+ var nodeListIds = nodeMap[id(node)];
+ if (!nodeListIds) {
+ nodeListIds = nodeMap[id(node)] = [];
+ }
+ nodeListIds.push(nodeListId);
+ };
+
+ var nodeLists = {
+ id: id,
+ // replaces the contents of one node list with the nodes in another list
+ replace: function(oldNodeList, newNodes) {
+ // for each node in the node list
+ oldNodeList = can.makeArray(oldNodeList);
+
+ // try every set
+ //can.each( oldNodeList, function(node){
+ var node = oldNodeList[0]
+ // for each nodeList the node is in
+ can.each(can.makeArray(nodeMap[id(node)]), function(nodeListId) {
+
+ // if startNode to endNode is
+ // within list, replace that list
+ // I think the problem is not the WHOLE part is being
+ // matched
+ var nodeList = nodeListMap[nodeListId],
+ startIndex = can.inArray(node, nodeList),
+ endIndex = can.inArray(oldNodeList[oldNodeList.length - 1], nodeList);
+
+
+ // remove this nodeListId from each node
+ if (startIndex >= 0 && endIndex >= 0) {
+ for (var i = startIndex; i <= endIndex; i++) {
+ var n = nodeList[i];
+ removeNodeListId(n, nodeListId);
+ }
+ // swap in new nodes into the nodeLIst
+ nodeList.splice.apply(nodeList, [startIndex, endIndex - startIndex + 1].concat(newNodes));
+
+ // tell these new nodes they belong to the nodeList
+ can.each(newNodes, function(node) {
+ addNodeListId(node, nodeListId);
+ });
+ } else {
+ nodeLists.unregister(nodeList);
+ }
+ });
+ //});
+ },
+ // registers a list of nodes
+ register: function(nodeList) {
+ var nLId = id(nodeList);
+ nodeListMap[nLId] = nodeList;
+
+ can.each(nodeList, function(node) {
+ addNodeListId(node, nLId);
+ });
+
+ },
+ // removes mappings
+ unregister: function(nodeList) {
+ var nLId = id(nodeList);
+ can.each(nodeList, function(node) {
+ removeNodeListId(node, nLId);
+ });
+ delete nodeListMap[nLId];
+ },
+ nodeMap: nodeMap,
+ nodeListMap: nodeListMap
+ }
+ var ids = function(nodeList) {
+ return nodeList.map(function(n) {
+ return id(n) + ":" + (n.innerHTML || n.nodeValue)
+ })
+ }
+ return nodeLists;
+
+ })(__m3);
+
+ // ## view/live.js
+ var __m21 = (function(can, elements, view, nodeLists) {
+ // ## live.js
+ // The live module provides live binding for computes
+ // and can.Observe.List.
+ // Currently, it's API is designed for `can/view/render`, but
+ // it could easily be used for other purposes.
+
+ // ### Helper methods
+ // #### setup
+ // `setup(HTMLElement, bind(data), unbind(data)) -> data`
+ // Calls bind right away, but will call unbind
+ // if the element is "destroyed" (removed from the DOM).
+ var setup = function(el, bind, unbind) {
+ var teardown = function() {
+ unbind(data)
+ can.unbind.call(el, 'destroyed', teardown);
+ },
+ data = {
+ teardownCheck: function(parent) {
+ if (!parent) {
+ teardown();
+ }
+ }
+ }
+
+ can.bind.call(el, 'destroyed', teardown);
+ bind(data)
+ return data;
+ },
+ // #### listen
+ // Calls setup, but presets bind and unbind to
+ // operate on a compute
+ listen = function(el, compute, change) {
+ return setup(el, function() {
+ compute.bind("change", change);
+ }, function(data) {
+ compute.unbind("change", change);
+ if (data.nodeList) {
+ nodeLists.unregister(data.nodeList);
+ }
+ });
+ },
+ // #### getAttributeParts
+ // Breaks up a string like foo='bar' into ["foo","'bar'""]
+ getAttributeParts = function(newVal) {
+ return (newVal || "").replace(/['"]/g, '').split('=')
+ }
+ // #### insertElementsAfter
+ // Appends elements after the last item in oldElements.
+ insertElementsAfter = function(oldElements, newFrag) {
+ var last = oldElements[oldElements.length - 1];
+
+ // Insert it in the `document` or `documentFragment`
+ if (last.nextSibling) {
+ last.parentNode.insertBefore(newFrag, last.nextSibling);
+ } else {
+ last.parentNode.appendChild(newFrag);
+ }
+ };
+
+ var live = {
+ nodeLists: nodeLists,
+ list: function(el, list, func, context, parentNode) {
+ // A mapping of the index to an array
+ // of elements that represent the item.
+ // Each array is registered so child or parent
+ // live structures can update the elements
+ var nodesMap = [],
+
+ add = function(ev, items, index) {
+
+ // Collect new html and mappings
+ var frag = document.createDocumentFragment(),
+ newMappings = [];
+ can.each(items, function(item) {
+ var itemHTML = func.call(context, item),
+ itemFrag = can.view.frag(itemHTML, parentNode);
+
+ newMappings.push(can.makeArray(itemFrag.childNodes));
+ frag.appendChild(itemFrag);
+ })
+
+ // Inserting at the end of the list
+ if (!nodesMap[index]) {
+ insertElementsAfter(
+ index == 0 ? [text] :
+ nodesMap[index - 1], frag)
+ } else {
+ var el = nodesMap[index][0];
+ el.parentNode.insertBefore(frag, el)
+ }
+ // register each item
+ can.each(newMappings, function(nodeList) {
+ nodeLists.register(nodeList)
+ });
+ [].splice.apply(nodesMap, [index, 0].concat(newMappings));
+ },
+ remove = function(ev, items, index) {
+ var removedMappings = nodesMap.splice(index, items.length),
+ itemsToRemove = [];
+
+ can.each(removedMappings, function(nodeList) {
+ // add items that we will remove all at once
+ [].push.apply(itemsToRemove, nodeList)
+ // Update any parent lists to remove these items
+ nodeLists.replace(nodeList, []);
+ // unregister the list
+ nodeLists.unregister(nodeList);
+
+ });
+ can.remove(can.$(itemsToRemove));
+ },
+ parentNode = elements.getParentNode(el, parentNode),
+ text = document.createTextNode("");
+
+ // Setup binding and teardown to add and remove events
+ setup(parentNode, function() {
+ list.bind("add", add).bind("remove", remove)
+ }, function() {
+ list.unbind("add", add).unbind("remove", remove);
+ can.each(nodesMap, function(nodeList) {
+ nodeLists.unregister(nodeList);
+ })
+ })
+
+ insertElementsAfter([el], text);
+ can.remove(can.$(el));
+ add({}, list, 0);
+
+ },
+ html: function(el, compute, parentNode) {
+ var parentNode = elements.getParentNode(el, parentNode),
+
+ data = listen(parentNode, compute, function(ev, newVal, oldVal) {
+ var attached = nodes[0].parentNode;
+ // update the nodes in the DOM with the new rendered value
+ if (attached) {
+ makeAndPut(newVal);
+ }
+ data.teardownCheck(nodes[0].parentNode);
+ });
+
+ var nodes,
+ makeAndPut = function(val) {
+ // create the fragment, but don't hook it up
+ // we need to insert it into the document first
+ var frag = can.view.frag(val, parentNode),
+ // keep a reference to each node
+ newNodes = can.makeArray(frag.childNodes);
+ // Insert it in the `document` or `documentFragment`
+ insertElementsAfter(nodes || [el], frag)
+ // nodes hasn't been set yet
+ if (!nodes) {
+ can.remove(can.$(el));
+ nodes = newNodes;
+ // set the teardown nodeList
+ data.nodeList = nodes;
+ nodeLists.register(nodes);
+ } else {
+ // Update node Array's to point to new nodes
+ // and then remove the old nodes.
+ // It has to be in this order for Mootools
+ // and IE because somehow, after an element
+ // is removed from the DOM, it loses its
+ // expando values.
+ var nodesToRemove = can.makeArray(nodes);
+ nodeLists.replace(nodes, newNodes);
+ can.remove(can.$(nodesToRemove));
+ }
+ };
+ makeAndPut(compute(), [el]);
+
+ },
+ text: function(el, compute, parentNode) {
+ var parent = elements.getParentNode(el, parentNode);
+
+ // setup listening right away so we don't have to re-calculate value
+ var data = listen(el.parentNode !== parent ? el.parentNode : parent, compute, function(ev, newVal, oldVal) {
+ // Sometimes this is 'unknown' in IE and will throw an exception if it is
+ if (typeof node.nodeValue != 'unknown') {
+ node.nodeValue = "" + newVal;
+ }
+ data.teardownCheck(node.parentNode);
+ });
+
+ var node = document.createTextNode(compute());
+
+ if (el.parentNode !== parent) {
+ parent = el.parentNode;
+ parent.insertBefore(node, el);
+ parent.removeChild(el);
+ } else {
+ parent.insertBefore(node, el);
+ parent.removeChild(el);
+ }
+ },
+ attributes: function(el, compute, currentValue) {
+ var setAttrs = function(newVal) {
+ var parts = getAttributeParts(newVal),
+ newAttrName = parts.shift();
+
+ // Remove if we have a change and used to have an `attrName`.
+ if ((newAttrName != attrName) && attrName) {
+ elements.removeAttr(el, attrName);
+ }
+ // Set if we have a new `attrName`.
+ if (newAttrName) {
+ elements.setAttr(el, newAttrName, parts.join('='));
+ attrName = newAttrName;
+ }
+ }
+
+ listen(el, compute, function(ev, newVal) {
+ setAttrs(newVal)
+ })
+ // current value has been set
+ if (arguments.length >= 3) {
+ var attrName = getAttributeParts(currentValue)[0]
+ } else {
+ setAttrs(compute())
+ }
+ },
+ attributePlaceholder: '__!!__',
+ attributeReplace: /__!!__/g,
+ attribute: function(el, attributeName, compute) {
+ listen(el, compute, function(ev, newVal) {
+ elements.setAttr(el, attributeName, hook.render());
+ })
+
+ var wrapped = can.$(el),
+ hooks;
+
+ // Get the list of hookups or create one for this element.
+ // Hooks is a map of attribute names to hookup `data`s.
+ // Each hookup data has:
+ // `render` - A `function` to render the value of the attribute.
+ // `funcs` - A list of hookup `function`s on that attribute.
+ // `batchNum` - The last event `batchNum`, used for performance.
+ hooks = can.data(wrapped, 'hooks');
+ if (!hooks) {
+ can.data(wrapped, 'hooks', hooks = {});
+ }
+
+ // Get the attribute value.
+ var attr = elements.getAttr(el, attributeName),
+ // Split the attribute value by the template.
+ // Only split out the first __!!__ so if we have multiple hookups in the same attribute,
+ // they will be put in the right spot on first render
+ parts = attr.split(live.attributePlaceholder),
+ goodParts = [],
+ hook;
+ goodParts.push(parts.shift(),
+ parts.join(live.attributePlaceholder));
+
+ // If we already had a hookup for this attribute...
+ if (hooks[attributeName]) {
+ // Just add to that attribute's list of `function`s.
+ hooks[attributeName].computes.push(compute);
+ } else {
+ // Create the hookup data.
+ hooks[attributeName] = {
+ render: function() {
+ var i = 0,
+ // attr doesn't have a value in IE
+ newAttr = attr ? attr.replace(live.attributeReplace, function() {
+ return elements.contentText(hook.computes[i++]());
+ }) : elements.contentText(hook.computes[i++]());
+ return newAttr;
+ },
+ computes: [compute],
+ batchNum: undefined
+ };
+ }
+
+ // Save the hook for slightly faster performance.
+ hook = hooks[attributeName];
+
+ // Insert the value in parts.
+ goodParts.splice(1, 0, compute());
+
+ // Set the attribute.
+ elements.setAttr(el, attributeName, goodParts.join(""));
+
+ }
+ }
+ return live;
+
+ })(__m3, __m19, __m16, __m22);
+
+ // ## view/render.js
+ var __m20 = (function(can, elements, live) {
+
+ var pendingHookups = [],
+ tagChildren = function(tagName) {
+ var newTag = elements.tagMap[tagName] || "span";
+ if (newTag === "span") {
+ //innerHTML in IE doesn't honor leading whitespace after empty elements
+ return "@@!!@@";
+ }
+ return "<" + newTag + ">" + tagChildren(newTag) + "</" + newTag + ">";
+ },
+ contentText = function(input, tag) {
+
+ // If it's a string, return.
+ if (typeof input == 'string') {
+ return input;
+ }
+ // If has no value, return an empty string.
+ if (!input && input !== 0) {
+ return '';
+ }
+
+ // If it's an object, and it has a hookup method.
+ var hook = (input.hookup &&
+
+ // Make a function call the hookup method.
+
+ function(el, id) {
+ input.hookup.call(input, el, id);
+ }) ||
+
+ // Or if it's a `function`, just use the input.
+ (typeof input == 'function' && input);
+
+ // Finally, if there is a `function` to hookup on some dom,
+ // add it to pending hookups.
+ if (hook) {
+ if (tag) {
+ return "<" + tag + " " + can.view.hook(hook) + "></" + tag + ">"
+ } else {
+ pendingHookups.push(hook);
+ }
+
+ return '';
+ }
+
+ // Finally, if all else is `false`, `toString()` it.
+ return "" + input;
+ },
+ // Returns escaped/sanatized content for anything other than a live-binding
+ contentEscape = function(txt) {
+ return (typeof txt == 'string' || typeof txt == 'number') ?
+ can.esc(txt) :
+ contentText(txt);
+ };
+
+ var current;
+
+ can.extend(can.view, {
+ live: live,
+ setupLists: function() {
+
+ var old = can.view.lists,
+ data;
+
+ can.view.lists = function(list, renderer) {
+ data = {
+ list: list,
+ renderer: renderer
+ }
+ }
+ return function() {
+ can.view.lists = old;
+ return data;
+ }
+ },
+ pending: function() {
+ // TODO, make this only run for the right tagName
+ var hooks = pendingHookups.slice(0);
+ lastHookups = hooks;
+ pendingHookups = [];
+ return can.view.hook(function(el) {
+ can.each(hooks, function(fn) {
+ fn(el);
+ });
+ });
+ },
+
+
+ txt: function(escape, tagName, status, self, func) {
+ var listTeardown = can.view.setupLists(),
+ emptyHandler = function() {},
+ unbind = function() {
+ compute.unbind("change", emptyHandler)
+ };
+
+ var compute = can.compute(func, self, false);
+ // bind to get and temporarily cache the value
+ compute.bind("change", emptyHandler);
+ // call the "wrapping" function and get the binding information
+ var tag = (elements.tagMap[tagName] || "span"),
+ listData = listTeardown(),
+ value = compute();
+
+
+ if (listData) {
+ return "<" + tag + can.view.hook(function(el, parentNode) {
+ live.list(el, listData.list, listData.renderer, self, parentNode);
+ }) + "></" + tag + ">";
+ }
+
+ // If we had no observes just return the value returned by func.
+ if (!compute.hasDependencies) {
+ unbind();
+ return (escape || status !== 0 ? contentEscape : contentText)(value, status === 0 && tag);
+ }
+
+ // the property (instead of innerHTML elements) to adjust. For
+ // example options should use textContent
+ var contentProp = elements.tagToContentPropMap[tagName];
+
+
+ // The magic tag is outside or between tags.
+ if (status === 0 && !contentProp) {
+ // Return an element tag with a hookup in place of the content
+ return "<" + tag + can.view.hook(
+ escape ?
+ // If we are escaping, replace the parentNode with
+ // a text node who's value is `func`'s return value.
+
+ function(el, parentNode) {
+ live.text(el, compute, parentNode);
+ unbind();
+ } :
+ // If we are not escaping, replace the parentNode with a
+ // documentFragment created as with `func`'s return value.
+
+ function(el, parentNode) {
+ live.html(el, compute, parentNode);
+ unbind();
+ //children have to be properly nested HTML for buildFragment to work properly
+ }) + ">" + tagChildren(tag) + "</" + tag + ">";
+ // In a tag, but not in an attribute
+ } else if (status === 1) {
+ // remember the old attr name
+ pendingHookups.push(function(el) {
+ live.attributes(el, compute, compute());
+ unbind();
+ });
+ return compute();
+ } else { // In an attribute...
+ var attributeName = status === 0 ? contentProp : status;
+ // if the magic tag is inside the element, like `<option><% TAG %></option>`,
+ // we add this hookup to the last element (ex: `option`'s) hookups.
+ // Otherwise, the magic tag is in an attribute, just add to the current element's
+ // hookups.
+ (status === 0 ? lastHookups : pendingHookups).push(function(el) {
+ live.attribute(el, attributeName, compute);
+ unbind();
+ });
+ return live.attributePlaceholder;
+ }
+ }
+ });
+
+ return can;
+ })(__m16, __m19, __m21, __m2);
+
+ // ## view/ejs/ejs.js
+ var __m17 = (function(can) {
+ // ## ejs.js
+ // `can.EJS`
+ // _Embedded JavaScript Templates._
+
+ // Helper methods.
+ var extend = can.extend,
+ EJS = function(options) {
+ // Supports calling EJS without the constructor
+ // This returns a function that renders the template.
+ if (this.constructor != EJS) {
+ var ejs = new EJS(options);
+ return function(data, helpers) {
+ return ejs.render(data, helpers);
+ };
+ }
+ // If we get a `function` directly, it probably is coming from
+ // a `steal`-packaged view.
+ if (typeof options == "function") {
+ this.template = {
+ fn: options
+ };
+ return;
+ }
+ // Set options on self.
+ extend(this, options);
+ this.template = this.scanner.scan(this.text, this.name);
+ };
+
+ can.EJS = EJS;
+
+
+ EJS.prototype.
+
+ render = function(object, extraHelpers) {
+ object = object || {};
+ return this.template.fn.call(object, object, new EJS.Helpers(object, extraHelpers || {}));
+ };
+
+ extend(EJS.prototype, {
+
+ scanner: new can.view.Scanner({
+
+ tokens: [
+ ["templateLeft", "<%%"], // Template
+ ["templateRight", "%>"], // Right Template
+ ["returnLeft", "<%=="], // Return Unescaped
+ ["escapeLeft", "<%="], // Return Escaped
+ ["commentLeft", "<%#"], // Comment
+ ["left", "<%"], // Run --- this is hack for now
+ ["right", "%>"], // Right -> All have same FOR Mustache ...
+ ["returnRight", "%>"]
+ ],
+
+
+ transform: function(source) {
+ return source.replace(/<%([\s\S]+?)%>/gm, function(whole, part) {
+ var brackets = [],
+ foundBracketPair,
+ i;
+
+ // Look for brackets (for removing self-contained blocks)
+ part.replace(/[{}]/gm, function(bracket, offset) {
+ brackets.push([bracket, offset]);
+ });
+
+ // Remove bracket pairs from the list of replacements
+ do {
+ foundBracketPair = false;
+ for (i = brackets.length - 2; i >= 0; i--) {
+ if (brackets[i][0] == '{' && brackets[i + 1][0] == '}') {
+ brackets.splice(i, 2);
+ foundBracketPair = true;
+ break;
+ }
+ }
+ } while (foundBracketPair);
+
+ // Unmatched brackets found, inject EJS tags
+ if (brackets.length >= 2) {
+ var result = ['<%'],
+ bracket,
+ last = 0;
+ for (i = 0; bracket = brackets[i]; i++) {
+ result.push(part.substring(last, last = bracket[1]));
+ if ((bracket[0] == '{' && i < brackets.length - 1) || (bracket[0] == '}' && i > 0)) {
+ result.push(bracket[0] == '{' ? '{ %><% ' : ' %><% }');
+ } else {
+ result.push(bracket[0]);
+ }
+ ++last;
+ }
+ result.push(part.substring(last), '%>');
+ return result.join('');
+ }
+ // Otherwise return the original
+ else {
+ return '<%' + part + '%>';
+ }
+ });
+ }
+ })
+ });
+
+ EJS.Helpers = function(data, extras) {
+ this._data = data;
+ this._extras = extras;
+ extend(this, extras);
+ };
+
+
+ EJS.Helpers.prototype = {
+ // TODO Deprecated!!
+ list: function(list, cb) {
+
+ can.each(list, function(item, i) {
+ cb(item, i, list)
+ })
+ },
+ each: function(list, cb) {
+ // Normal arrays don't get live updated
+ if (can.isArray(list)) {
+ this.list(list, cb);
+ } else {
+ can.view.lists(list, cb);
+ }
+ }
+ };
+
+ // Options for `steal`'s build.
+ can.view.register({
+ suffix: "ejs",
+ // returns a `function` that renders the view.
+ script: function(id, src) {
+ return "can.EJS(function(_CONTEXT,_VIEW) { " + new EJS({
+ text: src,
+ name: id
+ }).template.out + " })";
+ },
+ renderer: function(id, text) {
+ return EJS({
+ text: text,
+ name: id
+ });
+ }
+ });
+
+ return can;
+ })(__m3, __m16, __m2, __m14, __m18, __m20);
+
+ // ## control/control.js
+ var __m23 = (function(can) {
+ // ## control.js
+ // `can.Control`
+ // _Controller_
+
+ // Binds an element, returns a function that unbinds.
+ var bind = function(el, ev, callback) {
+
+ can.bind.call(el, ev, callback);
+
+ return function() {
+ can.unbind.call(el, ev, callback);
+ };
+ },
+ isFunction = can.isFunction,
+ extend = can.extend,
+ each = can.each,
+ slice = [].slice,
+ paramReplacer = /\{([^\}]+)\}/g,
+ special = can.getObject("$.event.special", [can]) || {},
+
+ // Binds an element, returns a function that unbinds.
+ delegate = function(el, selector, ev, callback) {
+ can.delegate.call(el, selector, ev, callback);
+ return function() {
+ can.undelegate.call(el, selector, ev, callback);
+ };
+ },
+
+ // Calls bind or unbind depending if there is a selector.
+ binder = function(el, ev, callback, selector) {
+ return selector ?
+ delegate(el, can.trim(selector), ev, callback) :
+ bind(el, ev, callback);
+ },
+
+ basicProcessor;
+
+ var Control = can.Control = can.Construct(
+
+ {
+ // Setup pre-processes which methods are event listeners.
+
+ setup: function() {
+
+ // Allow contollers to inherit "defaults" from super-classes as it
+ // done in `can.Construct`
+ can.Construct.setup.apply(this, arguments);
+
+ // If you didn't provide a name, or are `control`, don't do anything.
+ if (can.Control) {
+
+ // Cache the underscored names.
+ var control = this,
+ funcName;
+
+ // Calculate and cache actions.
+ control.actions = {};
+ for (funcName in control.prototype) {
+ if (control._isAction(funcName)) {
+ control.actions[funcName] = control._action(funcName);
+ }
+ }
+ }
+ },
+
+ // Moves `this` to the first argument, wraps it with `jQuery` if it's an element
+ _shifter: function(context, name) {
+
+ var method = typeof name == "string" ? context[name] : name;
+
+ if (!isFunction(method)) {
+ method = context[method];
+ }
+
+ return function() {
+ context.called = name;
+ return method.apply(context, [this.nodeName ? can.$(this) : this].concat(slice.call(arguments, 0)));
+ };
+ },
+
+ // Return `true` if is an action.
+
+ _isAction: function(methodName) {
+
+ var val = this.prototype[methodName],
+ type = typeof val;
+ // if not the constructor
+ return (methodName !== 'constructor') &&
+ // and is a function or links to a function
+ (type == "function" || (type == "string" && isFunction(this.prototype[val]))) &&
+ // and is in special, a processor, or has a funny character
+ !! (special[methodName] || processors[methodName] || /[^\w]/.test(methodName));
+ },
+ // Takes a method name and the options passed to a control
+ // and tries to return the data necessary to pass to a processor
+ // (something that binds things).
+
+ _action: function(methodName, options) {
+
+ // If we don't have options (a `control` instance), we'll run this
+ // later.
+ paramReplacer.lastIndex = 0;
+ if (options || !paramReplacer.test(methodName)) {
+ // If we have options, run sub to replace templates `{}` with a
+ // value from the options or the window
+ var convertedName = options ? can.sub(methodName, [options, window]) : methodName;
+ if (!convertedName) {
+ return null;
+ }
+ // If a `{}` template resolves to an object, `convertedName` will be
+ // an array
+ var arr = can.isArray(convertedName),
+
+ // Get the name
+ name = arr ? convertedName[1] : convertedName,
+
+ // Grab the event off the end
+ parts = name.split(/\s+/g),
+ event = parts.pop();
+
+ return {
+ processor: processors[event] || basicProcessor,
+ parts: [name, parts.join(" "), event],
+ delegate: arr ? convertedName[0] : undefined
+ };
+ }
+ },
+ // An object of `{eventName : function}` pairs that Control uses to
+ // hook up events auto-magically.
+
+ processors: {},
+ // A object of name-value pairs that act as default values for a
+ // control instance
+ defaults: {}
+
+ }, {
+
+ // Sets `this.element`, saves the control in `data, binds event
+ // handlers.
+
+ setup: function(element, options) {
+
+ var cls = this.constructor,
+ pluginname = cls.pluginName || cls._fullName,
+ arr;
+
+ // Want the raw element here.
+ this.element = can.$(element)
+
+ if (pluginname && pluginname !== 'can_control') {
+ // Set element and `className` on element.
+ this.element.addClass(pluginname);
+ }
+
+ (arr = can.data(this.element, "controls")) || can.data(this.element, "controls", arr = []);
+ arr.push(this);
+
+ // Option merging.
+
+ this.options = extend({}, cls.defaults, options);
+
+ // Bind all event handlers.
+ this.on();
+
+ // Gets passed into `init`.
+
+ return [this.element, this.options];
+ },
+
+ on: function(el, selector, eventName, func) {
+ if (!el) {
+
+ // Adds bindings.
+ this.off();
+
+ // Go through the cached list of actions and use the processor
+ // to bind
+ var cls = this.constructor,
+ bindings = this._bindings,
+ actions = cls.actions,
+ element = this.element,
+ destroyCB = can.Control._shifter(this, "destroy"),
+ funcName, ready;
+
+ for (funcName in actions) {
+ // Only push if we have the action and no option is `undefined`
+ if (actions.hasOwnProperty(funcName) &&
+ (ready = actions[funcName] || cls._action(funcName, this.options))) {
+ bindings.push(ready.processor(ready.delegate || element,
+ ready.parts[2], ready.parts[1], funcName, this));
+ }
+ }
+
+
+ // Setup to be destroyed...
+ // don't bind because we don't want to remove it.
+ can.bind.call(element, "destroyed", destroyCB);
+ bindings.push(function(el) {
+ can.unbind.call(el, "destroyed", destroyCB);
+ });
+ return bindings.length;
+ }
+
+ if (typeof el == 'string') {
+ func = eventName;
+ eventName = selector;
+ selector = el;
+ el = this.element;
+ }
+
+ if (func === undefined) {
+ func = eventName;
+ eventName = selector;
+ selector = null;
+ }
+
+ if (typeof func == 'string') {
+ func = can.Control._shifter(this, func);
+ }
+
+ this._bindings.push(binder(el, eventName, func, selector));
+
+ return this._bindings.length;
+ },
+ // Unbinds all event handlers on the controller.
+
+ off: function() {
+ var el = this.element[0]
+ each(this._bindings || [], function(value) {
+ value(el);
+ });
+ // Adds bindings.
+ this._bindings = [];
+ },
+ // Prepares a `control` for garbage collection
+
+ destroy: function() {
+ //Control already destroyed
+ if (this.element === null) {
+
+ return;
+ }
+ var Class = this.constructor,
+ pluginName = Class.pluginName || Class._fullName,
+ controls;
+
+ // Unbind bindings.
+ this.off();
+
+ if (pluginName && pluginName !== 'can_control') {
+ // Remove the `className`.
+ this.element.removeClass(pluginName);
+ }
+
+ // Remove from `data`.
+ controls = can.data(this.element, "controls");
+ controls.splice(can.inArray(this, controls), 1);
+
+ can.trigger(this, "destroyed"); // In case we want to know if the `control` is removed.
+
+ this.element = null;
+ }
+ });
+
+ var processors = can.Control.processors,
+ // Processors do the binding.
+ // They return a function that unbinds when called.
+ // The basic processor that binds events.
+ basicProcessor = function(el, event, selector, methodName, control) {
+ return binder(el, event, can.Control._shifter(control, methodName), selector);
+ };
+
+ // Set common events to be processed as a `basicProcessor`
+ each(["change", "click", "contextmenu", "dblclick", "keydown", "keyup",
+ "keypress", "mousedown", "mousemove", "mouseout", "mouseover",
+ "mouseup", "reset", "resize", "scroll", "select", "submit", "focusin",
+ "focusout", "mouseenter", "mouseleave",
+ // #104 - Add touch events as default processors
+ // TOOD feature detect?
+ "touchstart", "touchmove", "touchcancel", "touchend", "touchleave"
+ ], function(v) {
+ processors[v] = basicProcessor;
+ });
+
+ return Control;
+ })(__m3, __m1);
+
+ // ## util/string/deparam/deparam.js
+ var __m25 = (function(can) {
+
+ // ## deparam.js
+ // `can.deparam`
+ // _Takes a string of name value pairs and returns a Object literal that represents those params._
+ var digitTest = /^\d+$/,
+ keyBreaker = /([^\[\]]+)|(\[\])/g,
+ paramTest = /([^?#]*)(#.*)?$/,
+ prep = function(str) {
+ return decodeURIComponent(str.replace(/\+/g, " "));
+ };
+
+
+ can.extend(can, {
+
+ deparam: function(params) {
+
+ var data = {},
+ pairs, lastPart;
+
+ if (params && paramTest.test(params)) {
+
+ pairs = params.split('&'),
+
+ can.each(pairs, function(pair) {
+
+ var parts = pair.split('='),
+ key = prep(parts.shift()),
+ value = prep(parts.join("=")),
+ current = data;
+
+ if (key) {
+ parts = key.match(keyBreaker);
+
+ for (var j = 0, l = parts.length - 1; j < l; j++) {
+ if (!current[parts[j]]) {
+ // If what we are pointing to looks like an `array`
+ current[parts[j]] = digitTest.test(parts[j + 1]) || parts[j + 1] == "[]" ? [] : {};
+ }
+ current = current[parts[j]];
+ }
+ lastPart = parts.pop();
+ if (lastPart == "[]") {
+ current.push(value);
+ } else {
+ current[lastPart] = value;
+ }
+ }
+ });
+ }
+ return data;
+ }
+ });
+ return can;
+ })(__m3, __m2);
+
+ // ## route/route.js
+ var __m24 = (function(can) {
+
+ // ## route.js
+ // `can.route`
+ // _Helps manage browser history (and client state) by synchronizing the
+ // `window.location.hash` with a `can.Observe`._
+ // Helper methods used for matching routes.
+ var
+ // `RegExp` used to match route variables of the type ':name'.
+ // Any word character or a period is matched.
+ matcher = /\:([\w\.]+)/g,
+ // Regular expression for identifying &key=value lists.
+ paramsMatcher = /^(?:&[^=]+=[^&]*)+/,
+ // Converts a JS Object into a list of parameters that can be
+ // inserted into an html element tag.
+ makeProps = function(props) {
+ var tags = [];
+ can.each(props, function(val, name) {
+ tags.push((name === 'className' ? 'class' : name) + '="' +
+ (name === "href" ? val : can.esc(val)) + '"');
+ });
+ return tags.join(" ");
+ },
+ // Checks if a route matches the data provided. If any route variable
+ // is not present in the data, the route does not match. If all route
+ // variables are present in the data, the number of matches is returned
+ // to allow discerning between general and more specific routes.
+ matchesData = function(route, data) {
+ var count = 0,
+ i = 0,
+ defaults = {};
+ // look at default values, if they match ...
+ for (var name in route.defaults) {
+ if (route.defaults[name] === data[name]) {
+ // mark as matched
+ defaults[name] = 1;
+ count++;
+ }
+ }
+ for (; i < route.names.length; i++) {
+ if (!data.hasOwnProperty(route.names[i])) {
+ return -1;
+ }
+ if (!defaults[route.names[i]]) {
+ count++;
+ }
+
+ }
+
+ return count;
+ },
+ onready = !0,
+ location = window.location,
+ wrapQuote = function(str) {
+ return (str + '').replace(/([.?*+\^$\[\]\\(){}|\-])/g, "\\$1");
+ },
+ each = can.each,
+ extend = can.extend;
+
+ can.route = function(url, defaults) {
+ defaults = defaults || {};
+ // Extract the variable names and replace with `RegExp` that will match
+ // an atual URL with values.
+ var names = [],
+ test = url.replace(matcher, function(whole, name, i) {
+ names.push(name);
+ var next = "\\" + (url.substr(i + whole.length, 1) || can.route._querySeparator);
+ // a name without a default value HAS to have a value
+ // a name that has a default value can be empty
+ // The `\\` is for string-escaping giving single `\` for `RegExp` escaping.
+ return "([^" + next + "]" + (defaults[name] ? "*" : "+") + ")";
+ });
+
+ // Add route in a form that can be easily figured out.
+ can.route.routes[url] = {
+ // A regular expression that will match the route when variable values
+ // are present; i.e. for `:page/:type` the `RegExp` is `/([\w\.]*)/([\w\.]*)/` which
+ // will match for any value of `:page` and `:type` (word chars or period).
+ test: new RegExp("^" + test + "($|" + wrapQuote(can.route._querySeparator) + ")"),
+ // The original URL, same as the index for this entry in routes.
+ route: url,
+ // An `array` of all the variable names in this route.
+ names: names,
+ // Default values provided for the variables.
+ defaults: defaults,
+ // The number of parts in the URL separated by `/`.
+ length: url.split('/').length
+ };
+ return can.route;
+ };
+
+
+ extend(can.route, {
+
+ _querySeparator: '&',
+ _paramsMatcher: paramsMatcher,
+
+
+ param: function(data, _setRoute) {
+ // Check if the provided data keys match the names in any routes;
+ // Get the one with the most matches.
+ var route,
+ // Need to have at least 1 match.
+ matches = 0,
+ matchCount,
+ routeName = data.route,
+ propCount = 0;
+
+ delete data.route;
+
+ each(data, function() {
+ propCount++;
+ });
+ // Otherwise find route.
+ each(can.route.routes, function(temp, name) {
+ // best route is the first with all defaults matching
+
+
+ matchCount = matchesData(temp, data);
+ if (matchCount > matches) {
+ route = temp;
+ matches = matchCount;
+ }
+ if (matchCount >= propCount) {
+ return false;
+ }
+ });
+ // If we have a route name in our `can.route` data, and it's
+ // just as good as what currently matches, use that
+ if (can.route.routes[routeName] && matchesData(can.route.routes[routeName], data) === matches) {
+ route = can.route.routes[routeName];
+ }
+ // If this is match...
+ if (route) {
+ var cpy = extend({}, data),
+ // Create the url by replacing the var names with the provided data.
+ // If the default value is found an empty string is inserted.
+ res = route.route.replace(matcher, function(whole, name) {
+ delete cpy[name];
+ return data[name] === route.defaults[name] ? "" : encodeURIComponent(data[name]);
+ }),
+ after;
+ // Remove matching default values
+ each(route.defaults, function(val, name) {
+ if (cpy[name] === val) {
+ delete cpy[name];
+ }
+ });
+
+ // The remaining elements of data are added as
+ // `&` separated parameters to the url.
+ after = can.param(cpy);
+ // if we are paraming for setting the hash
+ // we also want to make sure the route value is updated
+ if (_setRoute) {
+ can.route.attr('route', route.route);
+ }
+ return res + (after ? can.route._querySeparator + after : "");
+ }
+ // If no route was found, there is no hash URL, only paramters.
+ return can.isEmptyObject(data) ? "" : can.route._querySeparator + can.param(data);
+ },
+
+ deparam: function(url) {
+ // See if the url matches any routes by testing it against the `route.test` `RegExp`.
+ // By comparing the URL length the most specialized route that matches is used.
+ var route = {
+ length: -1
+ };
+ each(can.route.routes, function(temp, name) {
+ if (temp.test.test(url) && temp.length > route.length) {
+ route = temp;
+ }
+ });
+ // If a route was matched.
+ if (route.length > -1) {
+
+ var // Since `RegExp` backreferences are used in `route.test` (parens)
+ // the parts will contain the full matched string and each variable (back-referenced) value.
+ parts = url.match(route.test),
+ // Start will contain the full matched string; parts contain the variable values.
+ start = parts.shift(),
+ // The remainder will be the `&key=value` list at the end of the URL.
+ remainder = url.substr(start.length - (parts[parts.length - 1] === can.route._querySeparator ? 1 : 0)),
+ // If there is a remainder and it contains a `&key=value` list deparam it.
+ obj = (remainder && can.route._paramsMatcher.test(remainder)) ? can.deparam(remainder.slice(1)) : {};
+
+ // Add the default values for this route.
+ obj = extend(true, {}, route.defaults, obj);
+ // Overwrite each of the default values in `obj` with those in
+ // parts if that part is not empty.
+ each(parts, function(part, i) {
+ if (part && part !== can.route._querySeparator) {
+ obj[route.names[i]] = decodeURIComponent(part);
+ }
+ });
+ obj.route = route.route;
+ return obj;
+ }
+ // If no route was matched, it is parsed as a `&key=value` list.
+ if (url.charAt(0) !== can.route._querySeparator) {
+ url = can.route._querySeparator + url;
+ }
+ return can.route._paramsMatcher.test(url) ? can.deparam(url.slice(1)) : {};
+ },
+
+ data: new can.Observe({}),
+
+ routes: {},
+
+ ready: function(val) {
+ if (val === false) {
+ onready = val;
+ }
+ if (val === true || onready === true) {
+ can.route._setup();
+ setState();
+ }
+ return can.route;
+ },
+
+ url: function(options, merge) {
+ if (merge) {
+ options = extend({}, curParams, options)
+ }
+ return "#!" + can.route.param(options);
+ },
+
+ link: function(name, options, props, merge) {
+ return "<a " + makeProps(
+ extend({
+ href: can.route.url(options, merge)
+ }, props)) + ">" + name + "</a>";
+ },
+
+ current: function(options) {
+ return location.hash == "#!" + can.route.param(options)
+ },
+ _setup: function() {
+ // If the hash changes, update the `can.route.data`.
+ can.bind.call(window, 'hashchange', setState);
+ },
+ _getHash: function() {
+ return location.href.split(/#!?/)[1] || "";
+ },
+ _setHash: function(serialized) {
+ var path = (can.route.param(serialized, true));
+ location.hash = "#!" + path;
+ return path;
+ }
+ });
+
+
+ // The functions in the following list applied to `can.route` (e.g. `can.route.attr('...')`) will
+ // instead act on the `can.route.data` observe.
+ each(['bind', 'unbind', 'delegate', 'undelegate', 'attr', 'removeAttr'], function(name) {
+ can.route[name] = function() {
+ // `delegate` and `undelegate` require
+ // the `can/observe/delegate` plugin
+ if (!can.route.data[name]) {
+ return;
+ }
+
+ return can.route.data[name].apply(can.route.data, arguments);
+ }
+ })
+
+ var // A ~~throttled~~ debounced function called multiple times will only fire once the
+ // timer runs down. Each call resets the timer.
+ timer,
+ // Intermediate storage for `can.route.data`.
+ curParams,
+ // Deparameterizes the portion of the hash of interest and assign the
+ // values to the `can.route.data` removing existing values no longer in the hash.
+ // setState is called typically by hashchange which fires asynchronously
+ // So it's possible that someone started changing the data before the
+ // hashchange event fired. For this reason, it will not set the route data
+ // if the data is changing or the hash already matches the hash that was set.
+ setState = can.route.setState = function() {
+ var hash = can.route._getHash();
+ curParams = can.route.deparam(hash);
+
+ // if the hash data is currently changing, or
+ // the hash is what we set it to anyway, do NOT change the hash
+ if (!changingData || hash !== lastHash) {
+ can.route.attr(curParams, true);
+ }
+ },
+ // The last hash caused by a data change
+ lastHash,
+ // Are data changes pending that haven't yet updated the hash
+ changingData;
+
+ // If the `can.route.data` changes, update the hash.
+ // Using `.serialize()` retrieves the raw data contained in the `observable`.
+ // This function is ~~throttled~~ debounced so it only updates once even if multiple values changed.
+ // This might be able to use batchNum and avoid this.
+ can.route.bind("change", function(ev, attr) {
+ // indicate that data is changing
+ changingData = 1;
+ clearTimeout(timer);
+ timer = setTimeout(function() {
+ // indicate that the hash is set to look like the data
+ changingData = 0;
+ var serialized = can.route.data.serialize();
+
+ lastHash = can.route._setHash(serialized);
+ }, 1);
+ });
+ // `onready` event...
+ can.bind.call(document, "ready", can.route.ready);
+
+ // Libraries other than jQuery don't execute the document `ready` listener
+ // if we are already DOM ready
+ if ((document.readyState === 'complete' || document.readyState === "interactive") && onready) {
+ can.route.ready();
+ }
+
+ // extend route to have a similar property
+ // that is often checked in mustache to determine
+ // an object's observability
+ can.route.constructor.canMakeObserve = can.Observe.canMakeObserve;
+
+ return can.route;
+ })(__m3, __m12, __m25);
+
+ // ## control/route/route.js
+ var __m26 = (function(can) {
+
+ // ## control/route.js
+ // _Controller route integration._
+
+ can.Control.processors.route = function(el, event, selector, funcName, controller) {
+ selector = selector || "";
+ can.route(selector);
+ var batchNum,
+ check = function(ev, attr, how) {
+ if (can.route.attr('route') === (selector) &&
+ (ev.batchNum === undefined || ev.batchNum !== batchNum)) {
+
+ batchNum = ev.batchNum;
+
+ var d = can.route.attr();
+ delete d.route;
+ if (can.isFunction(controller[funcName])) {
+ controller[funcName](d);
+ } else {
+ controller[controller[funcName]](d);
+ }
+
+ }
+ };
+ can.route.bind('change', check);
+ return function() {
+ can.route.unbind('change', check);
+ };
+ };
+
+ return can;
+ })(__m3, __m24, __m23);
+
+ window['can'] = __m4;
+ })();
\ No newline at end of file
Added: tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.zepto.js
===================================================================
--- tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.zepto.js (rev 0)
+++ tags/nuiton-js-canjs-1.1.6-1/src/main/resources/nuiton-js-canjs/can.zepto.js 2013-06-17 15:45:26 UTC (rev 92)
@@ -0,0 +1,4863 @@
+/*!
+ * CanJS - 1.1.6
+ * http://canjs.us/
+ * Copyright (c) 2013 Bitovi
+ * Wed, 29 May 2013 18:59:27 GMT
+ * Licensed MIT
+ * Includes: can/construct,can/observe,can/observe/compute,can/model,can/view,can/view/ejs,can/control,can/route,can/control/route
+ * Download from: http://canjs.com
+ */
+(function(undefined) {
+
+ // ## util/can.js
+ var __m4 = (function() {
+ var can = window.can || {};
+ if (typeof GLOBALCAN === 'undefined' || GLOBALCAN !== false) {
+ window.can = can;
+ }
+
+ can.isDeferred = function(obj) {
+ var isFunction = this.isFunction;
+ // Returns `true` if something looks like a deferred.
+ return obj && isFunction(obj.then) && isFunction(obj.pipe);
+ };
+
+ var cid = 0;
+ can.cid = function(object, name) {
+ if (object._cid) {
+ return object._cid
+ } else {
+ return object._cid = (name || "") + (++cid)
+ }
+ }
+ can.VERSION = '@EDGE';
+ return can;
+ })();
+
+ // ## util/object/isplain/isplain.js
+ var __m6 = (function(can) {
+ var core_hasOwn = Object.prototype.hasOwnProperty,
+ isWindow = function(obj) {
+ return obj != null && obj == obj.window;
+ },
+ isPlainObject = function(obj) {
+ // Must be an Object.
+ // Because of IE, we also have to check the presence of the constructor property.
+ // Make sure that DOM nodes and window objects don't pass through, as well
+ if (!obj || (typeof obj !== "object") || obj.nodeType || isWindow(obj)) {
+ return false;
+ }
+
+ try {
+ // Not own constructor property must be Object
+ if (obj.constructor && !core_hasOwn.call(obj, "constructor") && !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) {
+ return false;
+ }
+ } catch (e) {
+ // IE8,9 Will throw exceptions on certain host objects #9897
+ return false;
+ }
+
+ // Own properties are enumerated firstly, so to speed up,
+ // if last one is own, then all properties are own.
+
+ var key;
+ for (key in obj) {}
+
+ return key === undefined || core_hasOwn.call(obj, key);
+ }
+
+ can.isPlainObject = isPlainObject;
+ return can;
+ })(__m4);
+
+ // ## util/event.js
+ var __m7 = (function(can) {
+
+ // event.js
+ // ---------
+ // _Basic event wrapper._
+ can.addEvent = function(event, fn) {
+ if (!this.__bindEvents) {
+ this.__bindEvents = {};
+ }
+ var eventName = event.split(".")[0];
+
+ if (!this.__bindEvents[eventName]) {
+ this.__bindEvents[eventName] = [];
+ }
+ this.__bindEvents[eventName].push({
+ handler: fn,
+ name: event
+ });
+ return this;
+ };
+ can.removeEvent = function(event, fn) {
+ if (!this.__bindEvents) {
+ return;
+ }
+ var i = 0,
+ events = this.__bindEvents[event.split(".")[0]],
+ ev;
+ while (i < events.length) {
+ ev = events[i]
+ if ((fn && ev.handler === fn) || (!fn && ev.name === event)) {
+ events.splice(i, 1);
+ } else {
+ i++;
+ }
+ }
+ return this;
+ };
+ can.dispatch = function(event) {
+ if (!this.__bindEvents) {
+ return;
+ }
+
+ var eventName = event.type.split(".")[0],
+ handlers = (this.__bindEvents[eventName] || []).slice(0),
+ self = this,
+ args = [event].concat(event.data || []);
+
+ can.each(handlers, function(ev) {
+ event.data = args.slice(1);
+ ev.handler.apply(self, args);
+ });
+ }
+
+ return can;
+
+ })(__m4);
+
+ // ## util/fragment.js
+ var __m8 = (function(can) {
+
+ // fragment.js
+ // ---------
+ // _DOM Fragment support._
+ var fragmentRE = /^\s*<(\w+)[^>]*>/,
+ fragment = function(html, name) {
+ if (name === undefined) {
+ name = fragmentRE.test(html) && RegExp.$1;
+ }
+
+ if (html && can.isFunction(html.replace)) {
+ // Fix "XHTML"-style tags in all browsers
+ html = html.replace(/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, "<$1></$2>");
+ }
+
+ var container = document.createElement('div'),
+ temp = document.createElement('div')
+
+ // IE's parser will strip any `<tr><td>` tags when `innerHTML`
+ // is called on a `tbody`. To get around this, we construct a
+ // valid table with a `tbody` that has the `innerHTML` we want.
+ // Then the container is the `firstChild` of the `tbody`.
+ // [source](http://www.ericvasilik.com/2006/07/code-karma.html).
+ if (name === "tbody" || name === "tfoot" || name === "thead") {
+ temp.innerHTML = "<table>" + html + "</table>";
+ container = temp.firstChild.nodeType === 3 ? temp.lastChild : temp.firstChild;
+ } else if (name === "tr") {
+ temp.innerHTML = "<table><tbody>" + html + "</tbody></table>";
+ container = temp.firstChild.nodeType === 3 ? temp.lastChild : temp.firstChild.firstChild;
+ } else if (name === "td" || name === "th") {
+ temp.innerHTML = "<table><tbody><tr>" + html + "</tr></tbody></table>";
+ container = temp.firstChild.nodeType === 3 ? temp.lastChild : temp.firstChild.firstChild.firstChild;
+ } else if (name === 'option') {
+ temp.innerHTML = "<select>" + html + "</select>";
+ container = temp.firstChild.nodeType === 3 ? temp.lastChild : temp.firstChild;
+ } else {
+ container.innerHTML = '' + html;
+ }
+
+ // IE8 barfs if you pass slice a `childNodes` object, so make a copy.
+ var tmp = {},
+ children = container.childNodes;
+ tmp.length = children.length;
+ for (var i = 0; i < children.length; i++) {
+ tmp[i] = children[i];
+ }
+ return [].slice.call(tmp);
+ }
+
+ can.buildFragment = function(html, nodes) {
+ var parts = fragment(html),
+ frag = document.createDocumentFragment();
+
+ can.each(parts, function(part) {
+ frag.appendChild(part);
+ })
+ return frag;
+ };
+
+ return can;
+ })(__m4);
+
+ // ## util/deferred.js
+ var __m9 = (function(can) {
+
+ // deferred.js
+ // ---------
+ // _Lightweight, jQuery style deferreds._
+ // extend is usually provided by the wrapper but to avoid steal.then calls
+ // we define a simple extend here as well
+ var extend = function(target, src) {
+ for (var key in src) {
+ if (src.hasOwnProperty(key)) {
+ target[key] = src[key];
+ }
+ }
+ },
+ Deferred = function(func) {
+ if (!(this instanceof Deferred))
+ return new Deferred();
+
+ this._doneFuncs = [];
+ this._failFuncs = [];
+ this._resultArgs = null;
+ this._status = "";
+
+ // Check for option `function` -- call it with this as context and as first
+ // parameter, as specified in jQuery API.
+ func && func.call(this, this);
+ };
+
+ can.Deferred = Deferred;
+ can.when = Deferred.when = function() {
+ var args = can.makeArray(arguments);
+ if (args.length < 2) {
+ var obj = args[0];
+ if (obj && (can.isFunction(obj.isResolved) && can.isFunction(obj.isRejected))) {
+ return obj;
+ } else {
+ return Deferred().resolve(obj);
+ }
+ } else {
+
+ var df = Deferred(),
+ done = 0,
+ // Resolve params -- params of each resolve, we need to track them down
+ // to be able to pass them in the correct order if the master
+ // needs to be resolved.
+ rp = [];
+
+ can.each(args, function(arg, j) {
+ arg.done(function() {
+ rp[j] = (arguments.length < 2) ? arguments[0] : arguments;
+ if (++done == args.length) {
+ df.resolve.apply(df, rp);
+ }
+ }).fail(function() {
+ df.reject((arguments.length === 1) ? arguments[0] : arguments);
+ });
+ });
+
+ return df;
+
+ }
+ }
+
+ var resolveFunc = function(type, _status) {
+ return function(context) {
+ var args = this._resultArgs = (arguments.length > 1) ? arguments[1] : [];
+ return this.exec(context, this[type], args, _status);
+ }
+ },
+ doneFunc = function(type, _status) {
+ return function() {
+ var self = this;
+ // In Safari, the properties of the `arguments` object are not enumerable,
+ // so we have to convert arguments to an `Array` that allows `can.each` to loop over them.
+ can.each(Array.prototype.slice.call(arguments), function(v, i, args) {
+ if (!v)
+ return;
+ if (v.constructor === Array) {
+ args.callee.apply(self, v)
+ } else {
+ // Immediately call the `function` if the deferred has been resolved.
+ if (self._status === _status)
+ v.apply(self, self._resultArgs || []);
+
+ self[type].push(v);
+ }
+ });
+ return this;
+ }
+ };
+
+ extend(Deferred.prototype, {
+ pipe: function(done, fail) {
+ var d = can.Deferred();
+ this.done(function() {
+ d.resolve(done.apply(this, arguments));
+ });
+
+ this.fail(function() {
+ if (fail) {
+ d.reject(fail.apply(this, arguments));
+ } else {
+ d.reject.apply(d, arguments);
+ }
+ });
+ return d;
+ },
+ resolveWith: resolveFunc("_doneFuncs", "rs"),
+ rejectWith: resolveFunc("_failFuncs", "rj"),
+ done: doneFunc("_doneFuncs", "rs"),
+ fail: doneFunc("_failFuncs", "rj"),
+ always: function() {
+ var args = can.makeArray(arguments);
+ if (args.length && args[0])
+ this.done(args[0]).fail(args[0]);
+
+ return this;
+ },
+
+ then: function() {
+ var args = can.makeArray(arguments);
+ // Fail `function`(s)
+ if (args.length > 1 && args[1])
+ this.fail(args[1]);
+
+ // Done `function`(s)
+ if (args.length && args[0])
+ this.done(args[0]);
+
+ return this;
+ },
+
+ state: function() {
+ switch (this._status) {
+ case 'rs':
+ return 'resolved';
+ case 'rj':
+ return 'rejected';
+ default:
+ return 'pending';
+ }
+ },
+
+ isResolved: function() {
+ return this._status === "rs";
+ },
+
+ isRejected: function() {
+ return this._status === "rj";
+ },
+
+ reject: function() {
+ return this.rejectWith(this, arguments);
+ },
+
+ resolve: function() {
+ return this.resolveWith(this, arguments);
+ },
+
+ exec: function(context, dst, args, st) {
+ if (this._status !== "")
+ return this;
+
+ this._status = st;
+
+ can.each(dst, function(d) {
+ d.apply(context, args);
+ });
+
+ return this;
+ }
+ });
+
+ return can;
+ })(__m4);
+
+ // ## util/array/each.js
+ var __m10 = (function(can) {
+ can.each = function(elements, callback, context) {
+ var i = 0,
+ key;
+ if (elements) {
+ if (typeof elements.length === 'number' && elements.pop) {
+ if (elements.attr) {
+ elements.attr('length');
+ }
+ for (key = elements.length; i < key; i++) {
+ if (callback.call(context || elements[i], elements[i], i, elements) === false) {
+ break;
+ }
+ }
+ } else if (elements.hasOwnProperty) {
+ for (key in elements) {
+ if (elements.hasOwnProperty(key)) {
+ if (callback.call(context || elements[key], elements[key], key, elements) === false) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ return elements;
+ };
+
+ return can;
+ })(__m4);
+
+ // ## util/zepto/zepto.js
+ var __m3 = (function(can) {
+ var $ = Zepto;
+
+ // data.js
+ // ---------
+ // _jQuery-like data methods._
+ var data = {}, dataAttr = $.fn.data,
+ uuid = $.uuid = +new Date(),
+ exp = $.expando = 'Zepto' + uuid;
+
+ function getData(node, name) {
+ var id = node[exp],
+ store = id && data[id];
+ return name === undefined ? store || setData(node) :
+ (store && store[name]) || dataAttr.call($(node), name);
+ }
+
+ function setData(node, name, value) {
+ var id = node[exp] || (node[exp] = ++uuid),
+ store = data[id] || (data[id] = {});
+ if (name !== undefined) store[name] = value;
+ return store;
+ };
+
+ $.fn.data = function(name, value) {
+ return value === undefined ?
+ this.length == 0 ? undefined : getData(this[0], name) :
+ this.each(function(idx) {
+ setData(this, name, $.isFunction(value) ?
+ value.call(this, idx, getData(this, name)) : value);
+ });
+ };
+ $.cleanData = function(elems) {
+ for (var i = 0, elem;
+ (elem = elems[i]) !== undefined; i++) {
+ can.trigger(elem, "destroyed", [], false)
+ var id = elem[exp]
+ delete data[id];
+ }
+ }
+
+ // zepto.js
+ // ---------
+ // _Zepto node list._
+
+ var oldEach = can.each;
+ // Extend what you can out of Zepto.
+ $.extend(can, Zepto);
+ can.each = oldEach;
+
+ var arrHas = function(obj, name) {
+ return obj[0] && obj[0][name] || obj[name]
+ }
+
+ // Do what's similar for jQuery.
+ can.trigger = function(obj, event, args, bubble) {
+ if (obj.trigger) {
+ obj.trigger(event, args)
+ } else if (arrHas(obj, "dispatchEvent")) {
+ if (bubble === false) {
+ $([obj]).triggerHandler(event, args)
+ } else {
+ $([obj]).trigger(event, args)
+ }
+
+ } else {
+ if (typeof event == "string") {
+ event = {
+ type: event
+ }
+ }
+ event.target = event.target || obj;
+ event.data = args;
+ can.dispatch.call(obj, event)
+ }
+
+ }
+
+ can.$ = Zepto;
+
+ can.bind = function(ev, cb) {
+ // If we can bind to it...
+ if (this.bind) {
+ this.bind(ev, cb)
+ } else if (arrHas(this, "addEventListener")) {
+ $([this]).bind(ev, cb)
+ } else {
+ can.addEvent.call(this, ev, cb)
+ }
+ return this;
+ }
+ can.unbind = function(ev, cb) {
+ // If we can bind to it...
+ if (this.unbind) {
+ this.unbind(ev, cb)
+ } else if (arrHas(this, "addEventListener")) {
+ $([this]).unbind(ev, cb)
+ } else {
+ can.removeEvent.call(this, ev, cb)
+ }
+ return this;
+ }
+ can.delegate = function(selector, ev, cb) {
+ if (this.delegate) {
+ this.delegate(selector, ev, cb)
+ } else {
+ $([this]).delegate(selector, ev, cb)
+ }
+ }
+ can.undelegate = function(selector, ev, cb) {
+ if (this.undelegate) {
+ this.undelegate(selector, ev, cb)
+ } else {
+ $([this]).undelegate(selector, ev, cb)
+ }
+ }
+
+ $.each(["append", "filter", "addClass", "remove", "data"], function(i, name) {
+ can[name] = function(wrapped) {
+ return wrapped[name].apply(wrapped, can.makeArray(arguments).slice(1))
+ }
+ })
+
+ can.makeArray = function(arr) {
+ var ret = []
+ can.each(arr, function(a, i) {
+ ret[i] = a
+ })
+ return ret;
+ };
+
+ can.proxy = function(f, ctx) {
+ return function() {
+ return f.apply(ctx, arguments)
+ }
+ }
+
+ // Make ajax.
+ var XHR = $.ajaxSettings.xhr;
+ $.ajaxSettings.xhr = function() {
+ var xhr = XHR()
+ var open = xhr.open;
+ xhr.open = function(type, url, async) {
+ open.call(this, type, url, ASYNC === undefined ? true : ASYNC)
+ }
+ return xhr;
+ }
+ var ASYNC;
+ var AJAX = $.ajax;
+ var updateDeferred = function(xhr, d) {
+ for (var prop in xhr) {
+ if (typeof d[prop] == 'function') {
+ d[prop] = function() {
+ xhr[prop].apply(xhr, arguments)
+ }
+ } else {
+ d[prop] = prop[xhr]
+ }
+ }
+ }
+ can.ajax = function(options) {
+
+ var success = options.success,
+ error = options.error;
+ var d = can.Deferred();
+
+ options.success = function(data) {
+
+ updateDeferred(xhr, d);
+ d.resolve.call(d, data);
+ success && success.apply(this, arguments);
+ }
+ options.error = function() {
+ updateDeferred(xhr, d);
+ d.reject.apply(d, arguments);
+ error && error.apply(this, arguments);
+ }
+ if (options.async === false) {
+ ASYNC = false
+ }
+ var xhr = AJAX(options);
+ ASYNC = undefined;
+ updateDeferred(xhr, d);
+ return d;
+ };
+
+ // Make destroyed and empty work.
+ $.fn.empty = function() {
+ return this.each(function() {
+ $.cleanData(this.getElementsByTagName('*'))
+ this.innerHTML = ''
+ })
+ }
+
+ $.fn.remove = function() {
+ $.cleanData(this);
+ this.each(function() {
+ if (this.parentNode != null) {
+ // might be a text node
+ this.getElementsByTagName && $.cleanData(this.getElementsByTagName('*'))
+ this.parentNode.removeChild(this);
+ }
+ });
+ return this;
+ }
+
+ can.trim = function(str) {
+ return str.trim();
+ }
+ can.isEmptyObject = function(object) {
+ var name;
+ for (name in object) {};
+ return name === undefined;
+ }
+
+ // Make extend handle `true` for deep.
+ can.extend = function(first) {
+ if (first === true) {
+ var args = can.makeArray(arguments);
+ args.shift();
+ return $.extend.apply($, args)
+ }
+ return $.extend.apply($, arguments)
+ }
+
+ can.get = function(wrapped, index) {
+ return wrapped[index];
+ }
+
+ return can;
+ })(__m4, Zepto, __m6, __m7, __m8, __m9, __m10);
+
+ // ## util/string/string.js
+ var __m2 = (function(can) {
+ // ##string.js
+ // _Miscellaneous string utility functions._
+
+ // Several of the methods in this plugin use code adapated from Prototype
+ // Prototype JavaScript framework, version 1.6.0.1.
+ // © 2005-2007 Sam Stephenson
+ var strUndHash = /_|-/,
+ strColons = /\=\=/,
+ strWords = /([A-Z]+)([A-Z][a-z])/g,
+ strLowUp = /([a-z\d])([A-Z])/g,
+ strDash = /([a-z\d])([A-Z])/g,
+ strReplacer = /\{([^\}]+)\}/g,
+ strQuote = /"/g,
+ strSingleQuote = /'/g,
+
+ // Returns the `prop` property from `obj`.
+ // If `add` is true and `prop` doesn't exist in `obj`, create it as an
+ // empty object.
+ getNext = function(obj, prop, add) {
+ var result = obj[prop];
+
+ if (result === undefined && add === true) {
+ result = obj[prop] = {}
+ }
+ return result
+ },
+
+ // Returns `true` if the object can have properties (no `null`s).
+ isContainer = function(current) {
+ return (/^f|^o/).test(typeof current);
+ };
+
+ can.extend(can, {
+ // Escapes strings for HTML.
+
+ esc: function(content) {
+ // Convert bad values into empty strings
+ var isInvalid = content === null || content === undefined || (isNaN(content) && ("" + content === 'NaN'));
+ return ("" + (isInvalid ? '' : content))
+ .replace(/&/g, '&')
+ .replace(/</g, '<')
+ .replace(/>/g, '>')
+ .replace(strQuote, '"')
+ .replace(strSingleQuote, "'");
+ },
+
+
+ getObject: function(name, roots, add) {
+
+ // The parts of the name we are looking up
+ // `['App','Models','Recipe']`
+ var parts = name ? name.split('.') : [],
+ length = parts.length,
+ current,
+ r = 0,
+ i, container, rootsLength;
+
+ // Make sure roots is an `array`.
+ roots = can.isArray(roots) ? roots : [roots || window];
+
+ rootsLength = roots.length
+
+ if (!length) {
+ return roots[0];
+ }
+
+ // For each root, mark it as current.
+ for (r; r < rootsLength; r++) {
+ current = roots[r];
+ container = undefined;
+
+ // Walk current to the 2nd to last object or until there
+ // is not a container.
+ for (i = 0; i < length && isContainer(current); i++) {
+ container = current;
+ current = getNext(container, parts[i]);
+ }
+
+ // If we found property break cycle
+ if (container !== undefined && current !== undefined) {
+ break
+ }
+ }
+
+ // Remove property from found container
+ if (add === false && current !== undefined) {
+ delete container[parts[i - 1]]
+ }
+
+ // When adding property add it to the first root
+ if (add === true && current === undefined) {
+ current = roots[0]
+
+ for (i = 0; i < length && isContainer(current); i++) {
+ current = getNext(current, parts[i], true);
+ }
+ }
+
+ return current;
+ },
+ // Capitalizes a string.
+
+ capitalize: function(s, cache) {
+ // Used to make newId.
+ return s.charAt(0).toUpperCase() + s.slice(1);
+ },
+
+ // Underscores a string.
+
+ underscore: function(s) {
+ return s
+ .replace(strColons, '/')
+ .replace(strWords, '$1_$2')
+ .replace(strLowUp, '$1_$2')
+ .replace(strDash, '_')
+ .toLowerCase();
+ },
+ // Micro-templating.
+
+ sub: function(str, data, remove) {
+ var obs = [];
+
+ str = str || '';
+
+ obs.push(str.replace(strReplacer, function(whole, inside) {
+
+ // Convert inside to type.
+ var ob = can.getObject(inside, data, remove === true ? false : undefined);
+
+ if (ob === undefined) {
+ obs = null;
+ return "";
+ }
+
+ // If a container, push into objs (which will return objects found).
+ if (isContainer(ob) && obs) {
+ obs.push(ob);
+ return "";
+ }
+
+ return "" + ob;
+ }));
+
+ return obs === null ? obs : (obs.length <= 1 ? obs[0] : obs);
+ },
+
+ // These regex's are used throughout the rest of can, so let's make
+ // them available.
+ replacer: strReplacer,
+ undHash: strUndHash
+ });
+ return can;
+ })(__m3);
+
+ // ## construct/construct.js
+ var __m1 = (function(can) {
+
+ // ## construct.js
+ // `can.Construct`
+ // _This is a modified version of
+ // [John Resig's class](http://ejohn.org/blog/simple-javascript-inheritance/).
+ // It provides class level inheritance and callbacks._
+
+ // A private flag used to initialize a new class instance without
+ // initializing it's bindings.
+ var initializing = 0;
+
+
+ can.Construct = function() {
+ if (arguments.length) {
+ return can.Construct.extend.apply(can.Construct, arguments);
+ }
+ };
+
+
+ can.extend(can.Construct, {
+
+ newInstance: function() {
+ // Get a raw instance object (`init` is not called).
+ var inst = this.instance(),
+ arg = arguments,
+ args;
+
+ // Call `setup` if there is a `setup`
+ if (inst.setup) {
+ args = inst.setup.apply(inst, arguments);
+ }
+
+ // Call `init` if there is an `init`
+ // If `setup` returned `args`, use those as the arguments
+ if (inst.init) {
+ inst.init.apply(inst, args || arguments);
+ }
+
+ return inst;
+ },
+ // Overwrites an object with methods. Used in the `super` plugin.
+ // `newProps` - New properties to add.
+ // `oldProps` - Where the old properties might be (used with `super`).
+ // `addTo` - What we are adding to.
+ _inherit: function(newProps, oldProps, addTo) {
+ can.extend(addTo || newProps, newProps || {})
+ },
+ // used for overwriting a single property.
+ // this should be used for patching other objects
+ // the super plugin overwrites this
+ _overwrite: function(what, oldProps, propName, val) {
+ what[propName] = val;
+ },
+ // Set `defaults` as the merger of the parent `defaults` and this
+ // object's `defaults`. If you overwrite this method, make sure to
+ // include option merging logic.
+
+ setup: function(base, fullName) {
+ this.defaults = can.extend(true, {}, base.defaults, this.defaults);
+ },
+ // Create's a new `class` instance without initializing by setting the
+ // `initializing` flag.
+ instance: function() {
+
+ // Prevents running `init`.
+ initializing = 1;
+
+ var inst = new this();
+
+ // Allow running `init`.
+ initializing = 0;
+
+ return inst;
+ },
+ // Extends classes.
+
+ extend: function(fullName, klass, proto) {
+ // Figure out what was passed and normalize it.
+ if (typeof fullName != 'string') {
+ proto = klass;
+ klass = fullName;
+ fullName = null;
+ }
+
+ if (!proto) {
+ proto = klass;
+ klass = null;
+ }
+ proto = proto || {};
+
+ var _super_class = this,
+ _super = this.prototype,
+ name, shortName, namespace, prototype;
+
+ // Instantiate a base class (but only create the instance,
+ // don't run the init constructor).
+ prototype = this.instance();
+
+ // Copy the properties over onto the new prototype.
+ can.Construct._inherit(proto, _super, prototype);
+
+ // The dummy class constructor.
+
+ function Constructor() {
+ // All construction is actually done in the init method.
+ if (!initializing) {
+ return this.constructor !== Constructor && arguments.length ?
+ // We are being called without `new` or we are extending.
+ arguments.callee.extend.apply(arguments.callee, arguments) :
+ // We are being called with `new`.
+ this.constructor.newInstance.apply(this.constructor, arguments);
+ }
+ }
+
+ // Copy old stuff onto class (can probably be merged w/ inherit)
+ for (name in _super_class) {
+ if (_super_class.hasOwnProperty(name)) {
+ Constructor[name] = _super_class[name];
+ }
+ }
+
+ // Copy new static properties on class.
+ can.Construct._inherit(klass, _super_class, Constructor);
+
+ // Setup namespaces.
+ if (fullName) {
+
+ var parts = fullName.split('.'),
+ shortName = parts.pop(),
+ current = can.getObject(parts.join('.'), window, true),
+ namespace = current,
+ _fullName = can.underscore(fullName.replace(/\./g, "_")),
+ _shortName = can.underscore(shortName);
+
+
+
+ current[shortName] = Constructor;
+ }
+
+ // Set things that shouldn't be overwritten.
+ can.extend(Constructor, {
+ constructor: Constructor,
+ prototype: prototype,
+
+ namespace: namespace,
+
+ _shortName: _shortName,
+
+ fullName: fullName,
+ _fullName: _fullName
+ });
+
+ // Dojo and YUI extend undefined
+ if (shortName !== undefined) {
+ Constructor.shortName = shortName;
+ }
+
+ // Make sure our prototype looks nice.
+ Constructor.prototype.constructor = Constructor;
+
+
+ // Call the class `setup` and `init`
+ var t = [_super_class].concat(can.makeArray(arguments)),
+ args = Constructor.setup.apply(Constructor, t);
+
+ if (Constructor.init) {
+ Constructor.init.apply(Constructor, args || t);
+ }
+
+
+ return Constructor;
+
+ }
+
+ });
+ return can.Construct;
+ })(__m2);
+
+ // ## util/bind/bind.js
+ var __m12 = (function(can) {
+
+
+ // ## Bind helpers
+ can.bindAndSetup = function() {
+ // Add the event to this object
+ can.addEvent.apply(this, arguments);
+ // If not initializing, and the first binding
+ // call bindsetup if the function exists.
+ if (!this._init) {
+ if (!this._bindings) {
+ this._bindings = 1;
+ // setup live-binding
+ this._bindsetup && this._bindsetup();
+
+ } else {
+ this._bindings++;
+ }
+
+ }
+
+ return this;
+ };
+
+ can.unbindAndTeardown = function(ev, handler) {
+ // Remove the event handler
+ can.removeEvent.apply(this, arguments);
+
+ this._bindings--;
+ // If there are no longer any bindings and
+ // there is a bindteardown method, call it.
+ if (!this._bindings) {
+ this._bindteardown && this._bindteardown();
+ }
+ return this;
+ }
+
+ return can;
+
+ })(__m3);
+
+ // ## observe/observe.js
+ var __m11 = (function(can, bind) {
+ // ## observe.js
+ // `can.Observe`
+ // _Provides the observable pattern for JavaScript Objects._
+ // Returns `true` if something is an object with properties of its own.
+ var canMakeObserve = function(obj) {
+ return obj && !can.isDeferred(obj) && (can.isArray(obj) || can.isPlainObject(obj) || (obj instanceof can.Observe));
+ },
+
+ // Removes all listeners.
+ unhookup = function(items, namespace) {
+ return can.each(items, function(item) {
+ if (item && item.unbind) {
+ item.unbind("change" + namespace);
+ }
+ });
+ },
+ // Listens to changes on `child` and "bubbles" the event up.
+ // `child` - The object to listen for changes on.
+ // `prop` - The property name is at on.
+ // `parent` - The parent object of prop.
+ // `ob` - (optional) The Observe object constructor
+ // `list` - (optional) The observable list constructor
+ hookupBubble = function(child, prop, parent, Ob, List) {
+ Ob = Ob || Observe;
+ List = List || Observe.List;
+
+ // If it's an `array` make a list, otherwise a child.
+ if (child instanceof Observe) {
+ // We have an `observe` already...
+ // Make sure it is not listening to this already
+ // It's only listening if it has bindings already.
+ parent._bindings && unhookup([child], parent._cid);
+ } else if (can.isArray(child)) {
+ child = new List(child);
+ } else {
+ child = new Ob(child);
+ }
+ // only listen if something is listening to you
+ if (parent._bindings) {
+ // Listen to all changes and `batchTrigger` upwards.
+ bindToChildAndBubbleToParent(child, prop, parent)
+ }
+
+
+ return child;
+ },
+ bindToChildAndBubbleToParent = function(child, prop, parent) {
+ child.bind("change" + parent._cid, function() {
+ // `batchTrigger` the type on this...
+ var args = can.makeArray(arguments),
+ ev = args.shift();
+ args[0] = (prop === "*" ? [parent.indexOf(child), args[0]] : [prop, args[0]]).join(".");
+
+ // track objects dispatched on this observe
+ ev.triggeredNS = ev.triggeredNS || {};
+
+ // if it has already been dispatched exit
+ if (ev.triggeredNS[parent._cid]) {
+ return;
+ }
+
+ ev.triggeredNS[parent._cid] = true;
+ // send change event with modified attr to parent
+ can.trigger(parent, ev, args);
+ // send modified attr event to parent
+ //can.trigger(parent, args[0], args);
+ });
+ }
+ // An `id` to track events for a given observe.
+ observeId = 0,
+ // A helper used to serialize an `Observe` or `Observe.List`.
+ // `observe` - The observable.
+ // `how` - To serialize with `attr` or `serialize`.
+ // `where` - To put properties, in an `{}` or `[]`.
+ serialize = function(observe, how, where) {
+ // Go through each property.
+ observe.each(function(val, name) {
+ // If the value is an `object`, and has an `attrs` or `serialize` function.
+ where[name] = canMakeObserve(val) && can.isFunction(val[how]) ?
+ // Call `attrs` or `serialize` to get the original data back.
+ val[how]() :
+ // Otherwise return the value.
+ val;
+ });
+ return where;
+ },
+ attrParts = function(attr, keepKey) {
+ if (keepKey) {
+ return [attr];
+ }
+ return can.isArray(attr) ? attr : ("" + attr).split(".");
+ },
+ // Which batch of events this is for -- might not want to send multiple
+ // messages on the same batch. This is mostly for event delegation.
+ batchNum = 1,
+ // how many times has start been called without a stop
+ transactions = 0,
+ // an array of events within a transaction
+ batchEvents = [],
+ stopCallbacks = [],
+ makeBindSetup = function(wildcard) {
+ return function() {
+ var parent = this;
+ this._each(function(child, prop) {
+ if (child && child.bind) {
+ bindToChildAndBubbleToParent(child, wildcard || prop, parent)
+ }
+ })
+ };
+ };
+
+
+ var Observe = can.Map = can.Observe = can.Construct({
+
+ // keep so it can be overwritten
+ bind: can.bindAndSetup,
+ unbind: can.unbindAndTeardown,
+ id: "id",
+ canMakeObserve: canMakeObserve,
+ // starts collecting events
+ // takes a callback for after they are updated
+ // how could you hook into after ejs
+
+ startBatch: function(batchStopHandler) {
+ transactions++;
+ batchStopHandler && stopCallbacks.push(batchStopHandler);
+ },
+
+ stopBatch: function(force, callStart) {
+ if (force) {
+ transactions = 0;
+ } else {
+ transactions--;
+ }
+
+ if (transactions == 0) {
+ var items = batchEvents.slice(0),
+ callbacks = stopCallbacks.slice(0);
+ batchEvents = [];
+ stopCallbacks = [];
+ batchNum++;
+ callStart && this.startBatch();
+ can.each(items, function(args) {
+ can.trigger.apply(can, args);
+ });
+ can.each(callbacks, function(cb) {
+ cb();
+ });
+ }
+ },
+
+ triggerBatch: function(item, event, args) {
+ // Don't send events if initalizing.
+ if (!item._init) {
+ if (transactions == 0) {
+ return can.trigger(item, event, args);
+ } else {
+ event = typeof event === "string" ? {
+ type: event
+ } :
+ event;
+ event.batchNum = batchNum;
+ batchEvents.push([
+ item,
+ event,
+ args
+ ]);
+ }
+ }
+ },
+
+ keys: function(observe) {
+ var keys = [];
+ Observe.__reading && Observe.__reading(observe, '__keys');
+ for (var keyName in observe._data) {
+ keys.push(keyName);
+ }
+ return keys;
+ }
+ },
+
+ {
+ setup: function(obj) {
+ // `_data` is where we keep the properties.
+ this._data = {};
+
+ // The namespace this `object` uses to listen to events.
+ can.cid(this, ".observe");
+ // Sets all `attrs`.
+ this._init = 1;
+ this.attr(obj);
+ this.bind('change' + this._cid, can.proxy(this._changes, this));
+ delete this._init;
+ },
+ _bindsetup: makeBindSetup(),
+ _bindteardown: function() {
+ var cid = this._cid;
+ this._each(function(child) {
+ unhookup([child], cid)
+ })
+ },
+ _changes: function(ev, attr, how, newVal, oldVal) {
+ Observe.triggerBatch(this, {
+ type: attr,
+ batchNum: ev.batchNum
+ }, [newVal, oldVal]);
+ },
+ _triggerChange: function(attr, how, newVal, oldVal) {
+ Observe.triggerBatch(this, "change", can.makeArray(arguments))
+ },
+ // no live binding iterator
+ _each: function(callback) {
+ var data = this.__get();
+ for (var prop in data) {
+ if (data.hasOwnProperty(prop)) {
+ callback(data[prop], prop)
+ }
+ }
+ },
+
+ attr: function(attr, val) {
+ // This is super obfuscated for space -- basically, we're checking
+ // if the type of the attribute is not a `number` or a `string`.
+ var type = typeof attr;
+ if (type !== "string" && type !== "number") {
+ return this._attrs(attr, val)
+ } else if (val === undefined) { // If we are getting a value.
+ // Let people know we are reading.
+ Observe.__reading && Observe.__reading(this, attr)
+ return this._get(attr)
+ } else {
+ // Otherwise we are setting.
+ this._set(attr, val);
+ return this;
+ }
+ },
+
+ each: function() {
+ Observe.__reading && Observe.__reading(this, '__keys');
+ return can.each.apply(undefined, [this.__get()].concat(can.makeArray(arguments)))
+ },
+
+ removeAttr: function(attr) {
+ // Info if this is List or not
+ var isList = this instanceof can.Observe.List,
+ // Convert the `attr` into parts (if nested).
+ parts = attrParts(attr),
+ // The actual property to remove.
+ prop = parts.shift(),
+ // The current value.
+ current = isList ? this[prop] : this._data[prop];
+
+ // If we have more parts, call `removeAttr` on that part.
+ if (parts.length) {
+ return current.removeAttr(parts)
+ } else {
+ if (isList) {
+ this.splice(prop, 1)
+ } else if (prop in this._data) {
+ // Otherwise, `delete`.
+ delete this._data[prop];
+ // Create the event.
+ if (!(prop in this.constructor.prototype)) {
+ delete this[prop]
+ }
+ // Let others know the number of keys have changed
+ Observe.triggerBatch(this, "__keys");
+ this._triggerChange(prop, "remove", undefined, current);
+
+ }
+ return current;
+ }
+ },
+ // Reads a property from the `object`.
+ _get: function(attr) {
+ var value = typeof attr === 'string' && !! ~attr.indexOf('.') && this.__get(attr);
+ if (value) {
+ return value;
+ }
+
+ // break up the attr (`"foo.bar"`) into `["foo","bar"]`
+ var parts = attrParts(attr),
+ // get the value of the first attr name (`"foo"`)
+ current = this.__get(parts.shift());
+ // if there are other attributes to read
+ return parts.length ?
+ // and current has a value
+ current ?
+ // lookup the remaining attrs on current
+ current._get(parts) :
+ // or if there's no current, return undefined
+ undefined :
+ // if there are no more parts, return current
+ current;
+ },
+ // Reads a property directly if an `attr` is provided, otherwise
+ // returns the "real" data object itself.
+ __get: function(attr) {
+ return attr ? this._data[attr] : this._data;
+ },
+ // Sets `attr` prop as value on this object where.
+ // `attr` - Is a string of properties or an array of property values.
+ // `value` - The raw value to set.
+ _set: function(attr, value, keepKey) {
+ // Convert `attr` to attr parts (if it isn't already).
+ var parts = attrParts(attr, keepKey),
+ // The immediate prop we are setting.
+ prop = parts.shift(),
+ // The current value.
+ current = this.__get(prop);
+
+ // If we have an `object` and remaining parts.
+ if (canMakeObserve(current) && parts.length) {
+ // That `object` should set it (this might need to call attr).
+ current._set(parts, value)
+ } else if (!parts.length) {
+ // We're in "real" set territory.
+ if (this.__convert) {
+ value = this.__convert(prop, value)
+ }
+ this.__set(prop, value, current)
+ } else {
+ throw "can.Observe: Object does not exist"
+ }
+ },
+ __set: function(prop, value, current) {
+
+ // Otherwise, we are setting it on this `object`.
+ // TODO: Check if value is object and transform
+ // are we changing the value.
+ if (value !== current) {
+ // Check if we are adding this for the first time --
+ // if we are, we need to create an `add` event.
+ var changeType = this.__get().hasOwnProperty(prop) ? "set" : "add";
+
+ // Set the value on data.
+ this.___set(prop,
+
+ // If we are getting an object.
+ canMakeObserve(value) ?
+
+ // Hook it up to send event.
+ hookupBubble(value, prop, this) :
+ // Value is normal.
+ value);
+
+ if (changeType == "add") {
+ // If there is no current value, let others know that
+ // the the number of keys have changed
+
+ Observe.triggerBatch(this, "__keys", undefined);
+
+ }
+ // `batchTrigger` the change event.
+ this._triggerChange(prop, changeType, value, current);
+
+ //Observe.triggerBatch(this, prop, [value, current]);
+ // If we can stop listening to our old value, do it.
+ current && unhookup([current], this._cid);
+ }
+
+ },
+ // Directly sets a property on this `object`.
+ ___set: function(prop, val) {
+ this._data[prop] = val;
+ // Add property directly for easy writing.
+ // Check if its on the `prototype` so we don't overwrite methods like `attrs`.
+ if (!(prop in this.constructor.prototype)) {
+ this[prop] = val
+ }
+ },
+
+
+ bind: can.bindAndSetup,
+
+ unbind: can.unbindAndTeardown,
+
+ serialize: function() {
+ return serialize(this, 'serialize', {});
+ },
+
+ _attrs: function(props, remove) {
+
+ if (props === undefined) {
+ return serialize(this, 'attr', {})
+ }
+
+ props = can.extend({}, props);
+ var prop,
+ self = this,
+ newVal;
+ Observe.startBatch();
+ this.each(function(curVal, prop) {
+ newVal = props[prop];
+
+ // If we are merging...
+ if (newVal === undefined) {
+ remove && self.removeAttr(prop);
+ return;
+ }
+
+ if (self.__convert) {
+ newVal = self.__convert(prop, newVal)
+ }
+
+ // if we're dealing with models, want to call _set to let converter run
+ if (newVal instanceof can.Observe) {
+ self.__set(prop, newVal, curVal)
+ // if its an object, let attr merge
+ } else if (canMakeObserve(curVal) && canMakeObserve(newVal) && curVal.attr) {
+ curVal.attr(newVal, remove)
+ // otherwise just set
+ } else if (curVal != newVal) {
+ self.__set(prop, newVal, curVal)
+ }
+
+ delete props[prop];
+ })
+ // Add remaining props.
+ for (var prop in props) {
+ newVal = props[prop];
+ this._set(prop, newVal, true)
+ }
+ Observe.stopBatch()
+ return this;
+ },
+
+
+ compute: function(prop) {
+ return can.compute(this, prop);
+ }
+ });
+ // Helpers for `observable` lists.
+ var splice = [].splice,
+
+ list = Observe(
+
+ {
+ setup: function(instances, options) {
+ this.length = 0;
+ can.cid(this, ".observe")
+ this._init = 1;
+ if (can.isDeferred(instances)) {
+ this.replace(instances)
+ } else {
+ this.push.apply(this, can.makeArray(instances || []));
+ }
+ // this change needs to be ignored
+ this.bind('change' + this._cid, can.proxy(this._changes, this));
+ can.extend(this, options);
+ delete this._init;
+ },
+ _triggerChange: function(attr, how, newVal, oldVal) {
+
+ Observe.prototype._triggerChange.apply(this, arguments)
+ // `batchTrigger` direct add and remove events...
+ if (!~attr.indexOf('.')) {
+
+ if (how === 'add') {
+ Observe.triggerBatch(this, how, [newVal, +attr]);
+ Observe.triggerBatch(this, 'length', [this.length]);
+ } else if (how === 'remove') {
+ Observe.triggerBatch(this, how, [oldVal, +attr]);
+ Observe.triggerBatch(this, 'length', [this.length]);
+ } else {
+ Observe.triggerBatch(this, how, [newVal, +attr])
+ }
+
+ }
+
+ },
+ __get: function(attr) {
+ return attr ? this[attr] : this;
+ },
+ ___set: function(attr, val) {
+ this[attr] = val;
+ if (+attr >= this.length) {
+ this.length = (+attr + 1)
+ }
+ },
+ _each: function(callback) {
+ var data = this.__get();
+ for (var i = 0; i < data.length; i++) {
+ callback(data[i], i)
+ }
+ },
+ _bindsetup: makeBindSetup("*"),
+ // Returns the serialized form of this list.
+
+ serialize: function() {
+ return serialize(this, 'serialize', []);
+ },
+
+ splice: function(index, howMany) {
+ var args = can.makeArray(arguments),
+ i;
+
+ for (i = 2; i < args.length; i++) {
+ var val = args[i];
+ if (canMakeObserve(val)) {
+ args[i] = hookupBubble(val, "*", this, this.constructor.Observe, this.constructor)
+ }
+ }
+ if (howMany === undefined) {
+ howMany = args[1] = this.length - index;
+ }
+ var removed = splice.apply(this, args);
+ can.Observe.startBatch();
+ if (howMany > 0) {
+ this._triggerChange("" + index, "remove", undefined, removed);
+ unhookup(removed, this._cid);
+ }
+ if (args.length > 2) {
+ this._triggerChange("" + index, "add", args.slice(2), removed);
+ }
+ can.Observe.stopBatch();
+ return removed;
+ },
+
+ _attrs: function(items, remove) {
+ if (items === undefined) {
+ return serialize(this, 'attr', []);
+ }
+
+ // Create a copy.
+ items = can.makeArray(items);
+
+ Observe.startBatch();
+ this._updateAttrs(items, remove);
+ Observe.stopBatch()
+ },
+
+ _updateAttrs: function(items, remove) {
+ var len = Math.min(items.length, this.length);
+
+ for (var prop = 0; prop < len; prop++) {
+ var curVal = this[prop],
+ newVal = items[prop];
+
+ if (canMakeObserve(curVal) && canMakeObserve(newVal)) {
+ curVal.attr(newVal, remove)
+ } else if (curVal != newVal) {
+ this._set(prop, newVal)
+ } else {
+
+ }
+ }
+ if (items.length > this.length) {
+ // Add in the remaining props.
+ this.push.apply(this, items.slice(this.length));
+ } else if (items.length < this.length && remove) {
+ this.splice(items.length)
+ }
+ }
+ }),
+
+ // Converts to an `array` of arguments.
+ getArgs = function(args) {
+ return args[0] && can.isArray(args[0]) ?
+ args[0] :
+ can.makeArray(args);
+ };
+ // Create `push`, `pop`, `shift`, and `unshift`
+ can.each({
+
+ push: "length",
+
+ unshift: 0
+ },
+ // Adds a method
+ // `name` - The method name.
+ // `where` - Where items in the `array` should be added.
+
+ function(where, name) {
+ var orig = [][name]
+ list.prototype[name] = function() {
+ // Get the items being added.
+ var args = [],
+ // Where we are going to add items.
+ len = where ? this.length : 0,
+ i = arguments.length,
+ res,
+ val,
+ constructor = this.constructor;
+
+ // Go through and convert anything to an `observe` that needs to be converted.
+ while (i--) {
+ val = arguments[i];
+ args[i] = canMakeObserve(val) ?
+ hookupBubble(val, "*", this, this.constructor.Observe, this.constructor) :
+ val;
+ }
+
+ // Call the original method.
+ res = orig.apply(this, args);
+
+ if (!this.comparator || args.length) {
+
+ this._triggerChange("" + len, "add", args, undefined);
+ }
+
+ return res;
+ }
+ });
+
+ can.each({
+
+ pop: "length",
+
+ shift: 0
+ },
+ // Creates a `remove` type method
+
+ function(where, name) {
+ list.prototype[name] = function() {
+
+ var args = getArgs(arguments),
+ len = where && this.length ? this.length - 1 : 0;
+
+ var res = [][name].apply(this, args)
+
+ // Create a change where the args are
+ // `len` - Where these items were removed.
+ // `remove` - Items removed.
+ // `undefined` - The new values (there are none).
+ // `res` - The old, removed values (should these be unbound).
+ this._triggerChange("" + len, "remove", undefined, [res])
+
+ if (res && res.unbind) {
+ res.unbind("change" + this._cid)
+ }
+ return res;
+ }
+ });
+
+ can.extend(list.prototype, {
+
+ indexOf: function(item) {
+ this.attr('length')
+ return can.inArray(item, this)
+ },
+
+
+ join: [].join,
+
+
+ reverse: [].reverse,
+
+
+ slice: function() {
+ var temp = Array.prototype.slice.apply(this, arguments);
+ return new this.constructor(temp);
+ },
+
+
+ concat: function() {
+ var args = [];
+ can.each(can.makeArray(arguments), function(arg, i) {
+ args[i] = arg instanceof can.Observe.List ? arg.serialize() : arg;
+ });
+ return new this.constructor(Array.prototype.concat.apply(this.serialize(), args));
+ },
+
+
+ forEach: function(cb, thisarg) {
+ can.each(this, cb, thisarg || this);
+ },
+
+
+ replace: function(newList) {
+ if (can.isDeferred(newList)) {
+ newList.then(can.proxy(this.replace, this));
+ } else {
+ this.splice.apply(this, [0, this.length].concat(can.makeArray(newList || [])));
+ }
+
+ return this;
+ }
+ });
+
+ can.List = Observe.List = list;
+ Observe.setup = function() {
+ can.Construct.setup.apply(this, arguments);
+ // I would prefer not to do it this way. It should
+ // be using the attributes plugin to do this type of conversion.
+ this.List = Observe.List({
+ Observe: this
+ }, {});
+ }
+ return Observe;
+ })(__m3, __m12, __m1);
+
+ // ## observe/compute/compute.js
+ var __m13 = (function(can, bind) {
+
+ // returns the
+ // - observes and attr methods are called by func
+ // - the value returned by func
+ // ex: `{value: 100, observed: [{obs: o, attr: "completed"}]}`
+ var getValueAndObserved = function(func, self) {
+
+ var oldReading;
+ if (can.Observe) {
+ // Set a callback on can.Observe to know
+ // when an attr is read.
+ // Keep a reference to the old reader
+ // if there is one. This is used
+ // for nested live binding.
+ oldReading = can.Observe.__reading;
+ can.Observe.__reading = function(obj, attr) {
+ // Add the observe and attr that was read
+ // to `observed`
+ observed.push({
+ obj: obj,
+ attr: attr + ""
+ });
+ };
+ }
+
+ var observed = [],
+ // Call the "wrapping" function to get the value. `observed`
+ // will have the observe/attribute pairs that were read.
+ value = func.call(self);
+
+ // Set back so we are no longer reading.
+ if (can.Observe) {
+ can.Observe.__reading = oldReading;
+ }
+ return {
+ value: value,
+ observed: observed
+ };
+ },
+ // Calls `callback(newVal, oldVal)` everytime an observed property
+ // called within `getterSetter` is changed and creates a new result of `getterSetter`.
+ // Also returns an object that can teardown all event handlers.
+ computeBinder = function(getterSetter, context, callback, computeState) {
+ // track what we are observing
+ var observing = {},
+ // a flag indicating if this observe/attr pair is already bound
+ matched = true,
+ // the data to return
+ data = {
+ // we will maintain the value while live-binding is taking place
+ value: undefined,
+ // a teardown method that stops listening
+ teardown: function() {
+ for (var name in observing) {
+ var ob = observing[name];
+ ob.observe.obj.unbind(ob.observe.attr, onchanged);
+ delete observing[name];
+ }
+ }
+ },
+ batchNum;
+
+ // when a property value is changed
+ var onchanged = function(ev) {
+ // If the compute is no longer bound (because the same change event led to an unbind)
+ // then do not call getValueAndBind, or we will leak bindings.
+ if (computeState && !computeState.bound) {
+ return;
+ }
+ if (ev.batchNum === undefined || ev.batchNum !== batchNum) {
+ // store the old value
+ var oldValue = data.value,
+ // get the new value
+ newvalue = getValueAndBind();
+
+ // update the value reference (in case someone reads)
+ data.value = newvalue;
+ // if a change happened
+ if (newvalue !== oldValue) {
+ callback(newvalue, oldValue);
+ }
+ batchNum = batchNum = ev.batchNum;
+ }
+
+
+ };
+
+ // gets the value returned by `getterSetter` and also binds to any attributes
+ // read by the call
+ var getValueAndBind = function() {
+ var info = getValueAndObserved(getterSetter, context),
+ newObserveSet = info.observed;
+
+ var value = info.value;
+ matched = !matched;
+
+ // go through every attribute read by this observe
+ can.each(newObserveSet, function(ob) {
+ // if the observe/attribute pair is being observed
+ if (observing[ob.obj._cid + "|" + ob.attr]) {
+ // mark at as observed
+ observing[ob.obj._cid + "|" + ob.attr].matched = matched;
+ } else {
+ // otherwise, set the observe/attribute on oldObserved, marking it as being observed
+ observing[ob.obj._cid + "|" + ob.attr] = {
+ matched: matched,
+ observe: ob
+ };
+ ob.obj.bind(ob.attr, onchanged);
+ }
+ });
+
+ // Iterate through oldObserved, looking for observe/attributes
+ // that are no longer being bound and unbind them
+ for (var name in observing) {
+ var ob = observing[name];
+ if (ob.matched !== matched) {
+ ob.observe.obj.unbind(ob.observe.attr, onchanged);
+ delete observing[name];
+ }
+ }
+ return value;
+ };
+ // set the initial value
+ data.value = getValueAndBind();
+
+ data.isListening = !can.isEmptyObject(observing);
+ return data;
+ }
+
+ // if no one is listening ... we can not calculate every time
+
+ can.compute = function(getterSetter, context, eventName) {
+ if (getterSetter && getterSetter.isComputed) {
+ return getterSetter;
+ }
+ // stores the result of computeBinder
+ var computedData,
+ // how many listeners to this this compute
+ bindings = 0,
+ // the computed object
+ computed,
+ // an object that keeps track if the computed is bound
+ // onchanged needs to know this. It's possible a change happens and results in
+ // something that unbinds the compute, it needs to not to try to recalculate who it
+ // is listening to
+ computeState = {
+ bound: false,
+ // true if this compute is calculated from other computes and observes
+ hasDependencies: false
+ },
+ // The following functions are overwritten depending on how compute() is called
+ // a method to setup listening
+ on = function() {},
+ // a method to teardown listening
+ off = function() {},
+ // the current cached value (only valid if bound = true)
+ value,
+ // how to read the value
+ get = function() {
+ return value
+ },
+ // sets the value
+ set = function(newVal) {
+ value = newVal;
+ },
+ // this compute can be a dependency of other computes
+ canReadForChangeEvent = true;
+
+ computed = function(newVal) {
+ // setting ...
+ if (arguments.length) {
+ // save a reference to the old value
+ var old = value;
+
+ // setter may return a value if
+ // setter is for a value maintained exclusively by this compute
+ var setVal = set.call(context, newVal, old);
+
+ // if this has dependencies return the current value
+ if (computed.hasDependencies) {
+ return get.call(context);
+ }
+
+ if (setVal === undefined) {
+ // it's possible, like with the DOM, setting does not
+ // fire a change event, so we must read
+ value = get.call(context);
+ } else {
+ value = setVal;
+ }
+ // fire the change
+ if (old !== value) {
+ can.Observe.triggerBatch(computed, "change", [value, old]);
+ }
+ return value;
+ } else {
+ // Let others know to listen to changes in this compute
+ if (can.Observe.__reading && canReadForChangeEvent) {
+ can.Observe.__reading(computed, 'change');
+ }
+ // if we are bound, use the cached value
+ if (computeState.bound) {
+ return value;
+ } else {
+ return get.call(context);
+ }
+ }
+ }
+ if (typeof getterSetter === "function") {
+ set = getterSetter;
+ get = getterSetter;
+ canReadForChangeEvent = eventName === false ? false : true;
+ computed.hasDependencies = false;
+ on = function(update) {
+ computedData = computeBinder(getterSetter, context || this, update, computeState);
+ computed.hasDependencies = computedData.isListening
+ value = computedData.value;
+ }
+ off = function() {
+ computedData.teardown();
+ }
+ } else if (context) {
+
+ if (typeof context == "string") {
+ // `can.compute(obj, "propertyName", [eventName])`
+
+ var propertyName = context,
+ isObserve = getterSetter instanceof can.Observe;
+ if (isObserve) {
+ computed.hasDependencies = true;
+ }
+ get = function() {
+ if (isObserve) {
+ return getterSetter.attr(propertyName);
+ } else {
+ return getterSetter[propertyName];
+ }
+ }
+ set = function(newValue) {
+ if (isObserve) {
+ getterSetter.attr(propertyName, newValue)
+ } else {
+ getterSetter[propertyName] = newValue;
+ }
+ }
+ var handler;
+ on = function(update) {
+ handler = function() {
+ update(get(), value)
+ };
+ can.bind.call(getterSetter, eventName || propertyName, handler)
+
+ // use getValueAndObserved because
+ // we should not be indicating that some parent
+ // reads this property if it happens to be binding on it
+ value = getValueAndObserved(get).value
+ }
+ off = function() {
+ can.unbind.call(getterSetter, eventName || propertyName, handler)
+ }
+
+ } else {
+ // `can.compute(initialValue, setter)`
+ if (typeof context === "function") {
+ value = getterSetter;
+ set = context;
+ } else {
+ // `can.compute(initialValue,{get:, set:, on:, off:})`
+ value = getterSetter;
+ var options = context;
+ get = options.get || get;
+ set = options.set || set;
+ on = options.on || on;
+ off = options.off || off;
+ }
+
+ }
+
+
+
+ } else {
+ // `can.compute(5)`
+ value = getterSetter;
+ }
+
+ computed.isComputed = true;
+
+ can.cid(computed, "compute")
+
+ var updater = function(newValue, oldValue) {
+ value = newValue;
+ // might need a way to look up new and oldVal
+ can.Observe.triggerBatch(computed, "change", [newValue, oldValue])
+ }
+
+ return can.extend(computed, {
+ _bindsetup: function() {
+ computeState.bound = true;
+ // setup live-binding
+ on.call(this, updater)
+ },
+ _bindteardown: function() {
+ off.call(this, updater)
+ computeState.bound = false;
+ },
+
+ bind: can.bindAndSetup,
+
+ unbind: can.unbindAndTeardown
+ });
+ };
+ can.compute.binder = computeBinder;
+ return can.compute;
+ })(__m3, __m12);
+
+ // ## model/model.js
+ var __m14 = (function(can) {
+
+ // ## model.js
+ // `can.Model`
+ // _A `can.Observe` that connects to a RESTful interface._
+ // Generic deferred piping function
+
+ var pipe = function(def, model, func) {
+ var d = new can.Deferred();
+ def.then(function() {
+ var args = can.makeArray(arguments);
+ args[0] = model[func](args[0]);
+ d.resolveWith(d, args);
+ }, function() {
+ d.rejectWith(this, arguments);
+ });
+
+ if (typeof def.abort === 'function') {
+ d.abort = function() {
+ return def.abort();
+ }
+ }
+
+ return d;
+ },
+ modelNum = 0,
+ ignoreHookup = /change.observe\d+/,
+ getId = function(inst) {
+ // Instead of using attr, use __get for performance.
+ // Need to set reading
+ can.Observe.__reading && can.Observe.__reading(inst, inst.constructor.id)
+ return inst.__get(inst.constructor.id);
+ },
+ // Ajax `options` generator function
+ ajax = function(ajaxOb, data, type, dataType, success, error) {
+
+ var params = {};
+
+ // If we get a string, handle it.
+ if (typeof ajaxOb == "string") {
+ // If there's a space, it's probably the type.
+ var parts = ajaxOb.split(/\s+/);
+ params.url = parts.pop();
+ if (parts.length) {
+ params.type = parts.pop();
+ }
+ } else {
+ can.extend(params, ajaxOb);
+ }
+
+ // If we are a non-array object, copy to a new attrs.
+ params.data = typeof data == "object" && !can.isArray(data) ?
+ can.extend(params.data || {}, data) : data;
+
+ // Get the url with any templated values filled out.
+ params.url = can.sub(params.url, params.data, true);
+
+ return can.ajax(can.extend({
+ type: type || "post",
+ dataType: dataType || "json",
+ success: success,
+ error: error
+ }, params));
+ },
+ makeRequest = function(self, type, success, error, method) {
+ var args;
+ // if we pass an array as `self` it it means we are coming from
+ // the queued request, and we're passing already serialized data
+ // self's signature will be: [self, serializedData]
+ if (can.isArray(self)) {
+ args = self[1];
+ self = self[0];
+ } else {
+ args = self.serialize();
+ }
+ args = [args];
+ var deferred,
+ // The model.
+ model = self.constructor,
+ jqXHR;
+
+ // `destroy` does not need data.
+ if (type == 'destroy') {
+ args.shift();
+ }
+ // `update` and `destroy` need the `id`.
+ if (type !== 'create') {
+ args.unshift(getId(self));
+ }
+
+
+ jqXHR = model[type].apply(model, args);
+
+ deferred = jqXHR.pipe(function(data) {
+ self[method || type + "d"](data, jqXHR);
+ return self;
+ });
+
+ // Hook up `abort`
+ if (jqXHR.abort) {
+ deferred.abort = function() {
+ jqXHR.abort();
+ };
+ }
+
+ deferred.then(success, error);
+ return deferred;
+ },
+
+ // This object describes how to make an ajax request for each ajax method.
+ // The available properties are:
+ // `url` - The default url to use as indicated as a property on the model.
+ // `type` - The default http request type
+ // `data` - A method that takes the `arguments` and returns `data` used for ajax.
+
+ ajaxMethods = {
+
+ create: {
+ url: "_shortName",
+ type: "post"
+ },
+
+ update: {
+ data: function(id, attrs) {
+ attrs = attrs || {};
+ var identity = this.id;
+ if (attrs[identity] && attrs[identity] !== id) {
+ attrs["new" + can.capitalize(id)] = attrs[identity];
+ delete attrs[identity];
+ }
+ attrs[identity] = id;
+ return attrs;
+ },
+ type: "put"
+ },
+
+ destroy: {
+ type: "delete",
+ data: function(id) {
+ var args = {};
+ args.id = args[this.id] = id;
+ return args;
+ }
+ },
+
+ findAll: {
+ url: "_shortName"
+ },
+
+ findOne: {}
+ },
+ // Makes an ajax request `function` from a string.
+ // `ajaxMethod` - The `ajaxMethod` object defined above.
+ // `str` - The string the user provided. Ex: `findAll: "/recipes.json"`.
+ ajaxMaker = function(ajaxMethod, str) {
+ // Return a `function` that serves as the ajax method.
+ return function(data) {
+ // If the ajax method has it's own way of getting `data`, use that.
+ data = ajaxMethod.data ?
+ ajaxMethod.data.apply(this, arguments) :
+ // Otherwise use the data passed in.
+ data;
+ // Return the ajax method with `data` and the `type` provided.
+ return ajax(str || this[ajaxMethod.url || "_url"], data, ajaxMethod.type || "get")
+ }
+ }
+
+
+
+ can.Model = can.Observe({
+ fullName: "can.Model",
+ _reqs: 0,
+ setup: function(base) {
+ // create store here if someone wants to use model without inheriting from it
+ this.store = {};
+ can.Observe.setup.apply(this, arguments);
+ // Set default list as model list
+ if (!can.Model) {
+ return;
+ }
+ this.List = ML({
+ Observe: this
+ }, {});
+ var self = this,
+ clean = can.proxy(this._clean, self);
+
+
+ // go through ajax methods and set them up
+ can.each(ajaxMethods, function(method, name) {
+ // if an ajax method is not a function, it's either
+ // a string url like findAll: "/recipes" or an
+ // ajax options object like {url: "/recipes"}
+ if (!can.isFunction(self[name])) {
+ // use ajaxMaker to convert that into a function
+ // that returns a deferred with the data
+ self[name] = ajaxMaker(method, self[name]);
+ }
+ // check if there's a make function like makeFindAll
+ // these take deferred function and can do special
+ // behavior with it (like look up data in a store)
+ if (self["make" + can.capitalize(name)]) {
+ // pass the deferred method to the make method to get back
+ // the "findAll" method.
+ var newMethod = self["make" + can.capitalize(name)](self[name]);
+ can.Construct._overwrite(self, base, name, function() {
+ // increment the numer of requests
+ can.Model._reqs++;
+ var def = newMethod.apply(this, arguments);
+ var then = def.then(clean, clean);
+ then.abort = def.abort;
+
+ // attach abort to our then and return it
+ return then;
+ })
+ }
+ });
+
+ if (self.fullName == "can.Model" || !self.fullName) {
+ self.fullName = "Model" + (++modelNum);
+ }
+ // Add ajax converters.
+ can.Model._reqs = 0;
+ this._url = this._shortName + "/{" + this.id + "}"
+ },
+ _ajax: ajaxMaker,
+ _makeRequest: makeRequest,
+ _clean: function() {
+ can.Model._reqs--;
+ if (!can.Model._reqs) {
+ for (var id in this.store) {
+ if (!this.store[id]._bindings) {
+ delete this.store[id];
+ }
+ }
+ }
+ return arguments[0];
+ },
+
+ models: function(instancesRawData, oldList) {
+ // until "end of turn", increment reqs counter so instances will be added to the store
+ can.Model._reqs++;
+ if (!instancesRawData) {
+ return;
+ }
+
+ if (instancesRawData instanceof this.List) {
+ return instancesRawData;
+ }
+
+ // Get the list type.
+ var self = this,
+ tmp = [],
+ res = oldList instanceof can.Observe.List ? oldList : new(self.List || ML),
+ // Did we get an `array`?
+ arr = can.isArray(instancesRawData),
+
+ // Did we get a model list?
+ ml = (instancesRawData instanceof ML),
+
+ // Get the raw `array` of objects.
+ raw = arr ?
+
+ // If an `array`, return the `array`.
+ instancesRawData :
+
+ // Otherwise if a model list.
+ (ml ?
+
+ // Get the raw objects from the list.
+ instancesRawData.serialize() :
+
+ // Get the object's data.
+ instancesRawData.data),
+ i = 0;
+
+
+
+ if (res.length) {
+ res.splice(0);
+ }
+
+ can.each(raw, function(rawPart) {
+ tmp.push(self.model(rawPart));
+ });
+
+ // We only want one change event so push everything at once
+ res.push.apply(res, tmp);
+
+ if (!arr) { // Push other stuff onto `array`.
+ can.each(instancesRawData, function(val, prop) {
+ if (prop !== 'data') {
+ res.attr(prop, val);
+ }
+ })
+ }
+ // at "end of turn", clean up the store
+ setTimeout(can.proxy(this._clean, this), 1);
+ return res;
+ },
+
+ model: function(attributes) {
+ if (!attributes) {
+ return;
+ }
+ if (attributes instanceof this) {
+ attributes = attributes.serialize();
+ }
+ var id = attributes[this.id],
+ model = (id || id === 0) && this.store[id] ?
+ this.store[id].attr(attributes, this.removeAttr || false) : new this(attributes);
+ if (can.Model._reqs) {
+ this.store[attributes[this.id]] = model;
+ }
+ return model;
+ }
+ },
+
+
+ {
+
+ isNew: function() {
+ var id = getId(this);
+ return !(id || id === 0); // If `null` or `undefined`
+ },
+
+ save: function(success, error) {
+ return makeRequest(this, this.isNew() ? 'create' : 'update', success, error);
+ },
+
+ destroy: function(success, error) {
+ if (this.isNew()) {
+ var self = this;
+ var def = can.Deferred();
+ def.then(success, error);
+ return def.done(function(data) {
+ self.destroyed(data)
+ }).resolve(self);
+ }
+ return makeRequest(this, 'destroy', success, error, 'destroyed');
+ },
+
+ _bindsetup: function() {
+ this.constructor.store[this.__get(this.constructor.id)] = this;
+ return can.Observe.prototype._bindsetup.apply(this, arguments);
+ },
+
+ _bindteardown: function() {
+ delete this.constructor.store[getId(this)];
+ return can.Observe.prototype._bindteardown.apply(this, arguments)
+ },
+ // Change `id`.
+ ___set: function(prop, val) {
+ can.Observe.prototype.___set.call(this, prop, val)
+ // If we add an `id`, move it to the store.
+ if (prop === this.constructor.id && this._bindings) {
+ this.constructor.store[getId(this)] = this;
+ }
+ }
+ });
+
+ can.each({
+ makeFindAll: "models",
+ makeFindOne: "model",
+ makeCreate: "model",
+ makeUpdate: "model"
+ }, function(method, name) {
+ can.Model[name] = function(oldMethod) {
+ return function() {
+ var args = can.makeArray(arguments),
+ oldArgs = can.isFunction(args[1]) ? args.splice(0, 1) : args.splice(0, 2),
+ def = pipe(oldMethod.apply(this, oldArgs), this, method);
+ def.then(args[0], args[1]);
+ // return the original promise
+ return def;
+ };
+ };
+ });
+
+ can.each([
+
+ "created",
+
+ "updated",
+
+ "destroyed"
+ ], function(funcName) {
+ can.Model.prototype[funcName] = function(attrs) {
+ var stub,
+ constructor = this.constructor;
+
+ // Update attributes if attributes have been passed
+ stub = attrs && typeof attrs == 'object' && this.attr(attrs.attr ? attrs.attr() : attrs);
+
+ // triggers change event that bubble's like
+ // handler( 'change','1.destroyed' ). This is used
+ // to remove items on destroyed from Model Lists.
+ // but there should be a better way.
+ can.trigger(this, "change", funcName)
+
+
+ // Call event on the instance's Class
+ can.trigger(constructor, funcName, this);
+ };
+ });
+
+ // Model lists are just like `Observe.List` except that when their items are
+ // destroyed, it automatically gets removed from the list.
+
+ var ML = can.Model.List = can.Observe.List({
+ setup: function(params) {
+ if (can.isPlainObject(params) && !can.isArray(params)) {
+ can.Observe.List.prototype.setup.apply(this);
+ this.replace(this.constructor.Observe.findAll(params))
+ } else {
+ can.Observe.List.prototype.setup.apply(this, arguments);
+ }
+ },
+ _changes: function(ev, attr) {
+ can.Observe.List.prototype._changes.apply(this, arguments);
+ if (/\w+\.destroyed/.test(attr)) {
+ var index = this.indexOf(ev.target);
+ if (index != -1) {
+ this.splice(index, 1);
+ }
+ }
+ }
+ })
+
+ return can.Model;
+ })(__m3, __m11);
+
+ // ## view/view.js
+ var __m15 = (function(can) {
+ // ## view.js
+ // `can.view`
+ // _Templating abstraction._
+
+ var isFunction = can.isFunction,
+ makeArray = can.makeArray,
+ // Used for hookup `id`s.
+ hookupId = 1,
+
+ $view = can.view = can.template = function(view, data, helpers, callback) {
+ // If helpers is a `function`, it is actually a callback.
+ if (isFunction(helpers)) {
+ callback = helpers;
+ helpers = undefined;
+ }
+
+ var pipe = function(result) {
+ return $view.frag(result);
+ },
+ // In case we got a callback, we need to convert the can.view.render
+ // result to a document fragment
+ wrapCallback = isFunction(callback) ? function(frag) {
+ callback(pipe(frag));
+ } : null,
+ // Get the result.
+ result = $view.render(view, data, helpers, wrapCallback),
+ deferred = can.Deferred();
+
+ if (isFunction(result)) {
+ return result;
+ }
+
+ if (can.isDeferred(result)) {
+ result.then(function(result, data) {
+ deferred.resolve.call(deferred, pipe(result), data);
+ }, function() {
+ deferred.fail.apply(deferred, arguments);
+ });
+ return deferred;
+ }
+
+ // Convert it into a dom frag.
+ return pipe(result);
+ };
+
+ can.extend($view, {
+ // creates a frag and hooks it up all at once
+ frag: function(result, parentNode) {
+ return $view.hookup($view.fragment(result), parentNode);
+ },
+
+ // simply creates a frag
+ // this is used internally to create a frag
+ // insert it
+ // then hook it up
+ fragment: function(result) {
+ var frag = can.buildFragment(result, document.body);
+ // If we have an empty frag...
+ if (!frag.childNodes.length) {
+ frag.appendChild(document.createTextNode(''));
+ }
+ return frag;
+ },
+
+ // Convert a path like string into something that's ok for an `element` ID.
+ toId: function(src) {
+ return can.map(src.toString().split(/\/|\./g), function(part) {
+ // Dont include empty strings in toId functions
+ if (part) {
+ return part;
+ }
+ }).join("_");
+ },
+
+ hookup: function(fragment, parentNode) {
+ var hookupEls = [],
+ id,
+ func;
+
+ // Get all `childNodes`.
+ can.each(fragment.childNodes ? can.makeArray(fragment.childNodes) : fragment, function(node) {
+ if (node.nodeType === 1) {
+ hookupEls.push(node);
+ hookupEls.push.apply(hookupEls, can.makeArray(node.getElementsByTagName('*')));
+ }
+ });
+
+ // Filter by `data-view-id` attribute.
+ can.each(hookupEls, function(el) {
+ if (el.getAttribute && (id = el.getAttribute('data-view-id')) && (func = $view.hookups[id])) {
+ func(el, parentNode, id);
+ delete $view.hookups[id];
+ el.removeAttribute('data-view-id');
+ }
+ });
+
+ return fragment;
+ },
+
+
+ hookups: {},
+
+
+ hook: function(cb) {
+ $view.hookups[++hookupId] = cb;
+ return " data-view-id='" + hookupId + "'";
+ },
+
+
+ cached: {},
+
+ cachedRenderers: {},
+
+
+ cache: true,
+
+
+ register: function(info) {
+ this.types["." + info.suffix] = info;
+ },
+
+ types: {},
+
+
+ ext: ".ejs",
+
+
+ registerScript: function() {},
+
+
+ preload: function() {},
+
+
+ render: function(view, data, helpers, callback) {
+ // If helpers is a `function`, it is actually a callback.
+ if (isFunction(helpers)) {
+ callback = helpers;
+ helpers = undefined;
+ }
+
+ // See if we got passed any deferreds.
+ var deferreds = getDeferreds(data);
+
+ if (deferreds.length) { // Does data contain any deferreds?
+ // The deferred that resolves into the rendered content...
+ var deferred = new can.Deferred(),
+ dataCopy = can.extend({}, data);
+
+ // Add the view request to the list of deferreds.
+ deferreds.push(get(view, true))
+
+ // Wait for the view and all deferreds to finish...
+ can.when.apply(can, deferreds).then(function(resolved) {
+ // Get all the resolved deferreds.
+ var objs = makeArray(arguments),
+ // Renderer is the last index of the data.
+ renderer = objs.pop(),
+ // The result of the template rendering with data.
+ result;
+
+ // Make data look like the resolved deferreds.
+ if (can.isDeferred(data)) {
+ dataCopy = usefulPart(resolved);
+ } else {
+ // Go through each prop in data again and
+ // replace the defferreds with what they resolved to.
+ for (var prop in data) {
+ if (can.isDeferred(data[prop])) {
+ dataCopy[prop] = usefulPart(objs.shift());
+ }
+ }
+ }
+
+ // Get the rendered result.
+ result = renderer(dataCopy, helpers);
+
+ // Resolve with the rendered view.
+ deferred.resolve(result, dataCopy);
+
+ // If there's a `callback`, call it back with the result.
+ callback && callback(result, dataCopy);
+ }, function() {
+ deferred.reject.apply(deferred, arguments)
+ });
+ // Return the deferred...
+ return deferred;
+ } else {
+ // No deferreds! Render this bad boy.
+ var response,
+ // If there's a `callback` function
+ async = isFunction(callback),
+ // Get the `view` type
+ deferred = get(view, async);
+
+ // If we are `async`...
+ if (async) {
+ // Return the deferred
+ response = deferred;
+ // And fire callback with the rendered result.
+ deferred.then(function(renderer) {
+ callback(data ? renderer(data, helpers) : renderer);
+ })
+ } else {
+ // if the deferred is resolved, call the cached renderer instead
+ // this is because it's possible, with recursive deferreds to
+ // need to render a view while its deferred is _resolving_. A _resolving_ deferred
+ // is a deferred that was just resolved and is calling back it's success callbacks.
+ // If a new success handler is called while resoliving, it does not get fired by
+ // jQuery's deferred system. So instead of adding a new callback
+ // we use the cached renderer.
+ // We also add __view_id on the deferred so we can look up it's cached renderer.
+ // In the future, we might simply store either a deferred or the cached result.
+ if (deferred.state() === "resolved" && deferred.__view_id) {
+ var currentRenderer = $view.cachedRenderers[deferred.__view_id];
+ return data ? currentRenderer(data, helpers) : currentRenderer;
+ } else {
+ // Otherwise, the deferred is complete, so
+ // set response to the result of the rendering.
+ deferred.then(function(renderer) {
+ response = data ? renderer(data, helpers) : renderer;
+ });
+ }
+ }
+
+ return response;
+ }
+ },
+
+
+ registerView: function(id, text, type, def) {
+ // Get the renderer function.
+ var func = (type || $view.types[$view.ext]).renderer(id, text);
+ def = def || new can.Deferred();
+
+ // Cache if we are caching.
+ if ($view.cache) {
+ $view.cached[id] = def;
+ def.__view_id = id;
+ $view.cachedRenderers[id] = func;
+ }
+
+ // Return the objects for the response's `dataTypes`
+ // (in this case view).
+ return def.resolve(func);
+ }
+ });
+
+ // Makes sure there's a template, if not, have `steal` provide a warning.
+ var checkText = function(text, url) {
+ if (!text.length) {
+
+ throw "can.view: No template or empty template:" + url;
+ }
+ },
+ // `Returns a `view` renderer deferred.
+ // `url` - The url to the template.
+ // `async` - If the ajax request should be asynchronous.
+ // Returns a deferred.
+ get = function(url, async) {
+ var suffix = url.match(/\.[\w\d]+$/),
+ type,
+ // If we are reading a script element for the content of the template,
+ // `el` will be set to that script element.
+ el,
+ // A unique identifier for the view (used for caching).
+ // This is typically derived from the element id or
+ // the url for the template.
+ id,
+ // The ajax request used to retrieve the template content.
+ jqXHR;
+
+ //If the url has a #, we assume we want to use an inline template
+ //from a script element and not current page's HTML
+ if (url.match(/^#/)) {
+ url = url.substr(1);
+ }
+ // If we have an inline template, derive the suffix from the `text/???` part.
+ // This only supports `<script>` tags.
+ if (el = document.getElementById(url)) {
+ suffix = "." + el.type.match(/\/(x\-)?(.+)/)[2];
+ }
+
+ // If there is no suffix, add one.
+ if (!suffix && !$view.cached[url]) {
+ url += (suffix = $view.ext);
+ }
+
+ if (can.isArray(suffix)) {
+ suffix = suffix[0]
+ }
+
+ // Convert to a unique and valid id.
+ id = $view.toId(url);
+
+ // If an absolute path, use `steal` to get it.
+ // You should only be using `//` if you are using `steal`.
+ if (url.match(/^\/\//)) {
+ var sub = url.substr(2);
+ url = !window.steal ?
+ sub :
+ steal.config().root.mapJoin("" + steal.id(sub));
+ }
+
+ // Set the template engine type.
+ type = $view.types[suffix];
+
+ // If it is cached,
+ if ($view.cached[id]) {
+ // Return the cached deferred renderer.
+ return $view.cached[id];
+
+ // Otherwise if we are getting this from a `<script>` element.
+ } else if (el) {
+ // Resolve immediately with the element's `innerHTML`.
+ return $view.registerView(id, el.innerHTML, type);
+ } else {
+ // Make an ajax request for text.
+ var d = new can.Deferred();
+ can.ajax({
+ async: async,
+ url: url,
+ dataType: "text",
+ error: function(jqXHR) {
+ checkText("", url);
+ d.reject(jqXHR);
+ },
+ success: function(text) {
+ // Make sure we got some text back.
+ checkText(text, url);
+ $view.registerView(id, text, type, d)
+ }
+ });
+ return d;
+ }
+ },
+ // Gets an `array` of deferreds from an `object`.
+ // This only goes one level deep.
+ getDeferreds = function(data) {
+ var deferreds = [];
+
+ // pull out deferreds
+ if (can.isDeferred(data)) {
+ return [data]
+ } else {
+ for (var prop in data) {
+ if (can.isDeferred(data[prop])) {
+ deferreds.push(data[prop]);
+ }
+ }
+ }
+ return deferreds;
+ },
+ // Gets the useful part of a resolved deferred.
+ // This is for `model`s and `can.ajax` that resolve to an `array`.
+ usefulPart = function(resolved) {
+ return can.isArray(resolved) && resolved[1] === 'success' ? resolved[0] : resolved
+ };
+
+ //!steal-pluginify-remove-start
+ if (window.steal) {
+ steal.type("view js", function(options, success, error) {
+ var type = $view.types["." + options.type],
+ id = $view.toId(options.id);
+
+ options.text = "steal('" + (type.plugin || "can/view/" + options.type) + "',function(can){return " + "can.view.preload('" + id + "'," + options.text + ");\n})";
+ success();
+ })
+ }
+ //!steal-pluginify-remove-end
+
+ can.extend($view, {
+ register: function(info) {
+ this.types["." + info.suffix] = info;
+
+ //!steal-pluginify-remove-start
+ if (window.steal) {
+ steal.type(info.suffix + " view js", function(options, success, error) {
+ var type = $view.types["." + options.type],
+ id = $view.toId(options.id + '');
+
+ options.text = type.script(id, options.text)
+ success();
+ })
+ };
+ //!steal-pluginify-remove-end
+
+ $view[info.suffix] = function(id, text) {
+ if (!text) {
+ // Return a nameless renderer
+ var renderer = function() {
+ return $view.frag(renderer.render.apply(this, arguments));
+ }
+ renderer.render = function() {
+ var renderer = info.renderer(null, id);
+ return renderer.apply(renderer, arguments);
+ }
+ return renderer;
+ }
+
+ $view.preload(id, info.renderer(id, text));
+ return can.view(id);
+ }
+ },
+ registerScript: function(type, id, src) {
+ return "can.view.preload('" + id + "'," + $view.types["." + type].script(id, src) + ");";
+ },
+ preload: function(id, renderer) {
+ $view.cached[id] = new can.Deferred().resolve(function(data, helpers) {
+ return renderer.call(data, data, helpers);
+ });
+
+ function frag() {
+ return $view.frag(renderer.apply(this, arguments));
+ }
+ // expose the renderer for mustache
+ frag.render = renderer;
+ return frag;
+ }
+
+ });
+
+ return can;
+ })(__m3);
+
+ // ## view/elements.js
+ var __m18 = (function() {
+
+ var elements = {
+ tagToContentPropMap: {
+ option: "textContent" in document.createElement("option") ? "textContent" : "innerText",
+ textarea: "value"
+ },
+
+ attrMap: {
+ "class": "className",
+ "value": "value",
+ "innerText": "innerText",
+ "textContent": "textContent",
+ "checked": true,
+ "disabled": true,
+ "readonly": true,
+ "required": true
+ },
+ // elements whos default value we should set
+ defaultValue: ["input", "textarea"],
+ // a map of parent element to child elements
+ tagMap: {
+ "": "span",
+ table: "tbody",
+ tr: "td",
+ ol: "li",
+ ul: "li",
+ tbody: "tr",
+ thead: "tr",
+ tfoot: "tr",
+ select: "option",
+ optgroup: "option"
+ },
+ // a tag's parent element
+ reverseTagMap: {
+ tr: "tbody",
+ option: "select",
+ td: "tr",
+ th: "tr",
+ li: "ul"
+ },
+
+ getParentNode: function(el, defaultParentNode) {
+ return defaultParentNode && el.parentNode.nodeType === 11 ? defaultParentNode : el.parentNode;
+ },
+ // set an attribute on an element
+ setAttr: function(el, attrName, val) {
+ var tagName = el.nodeName.toString().toLowerCase(),
+ prop = elements.attrMap[attrName];
+ // if this is a special property
+ if (prop === true) {
+ el[attrName] = true;
+ } else if (prop) {
+ // set the value as true / false
+ el[prop] = val;
+ if (prop === "value" && can.inArray(tagName, elements.defaultValue) >= 0) {
+ el.defaultValue = val;
+ }
+ } else {
+ el.setAttribute(attrName, val);
+ }
+ },
+ // gets the value of an attribute
+ getAttr: function(el, attrName) {
+ // Default to a blank string for IE7/8
+ return (elements.attrMap[attrName] && el[elements.attrMap[attrName]] ?
+ el[elements.attrMap[attrName]] :
+ el.getAttribute(attrName)) || '';
+ },
+ // removes the attribute
+ removeAttr: function(el, attrName) {
+ if (elements.attrMap[attrName] === true) {
+ el[attrName] = false;
+ } else {
+ el.removeAttribute(attrName);
+ }
+ },
+ contentText: function(text) {
+ if (typeof text == 'string') {
+ return text;
+ }
+ // If has no value, return an empty string.
+ if (!text && text !== 0) {
+ return '';
+ }
+ return "" + text;
+ }
+ };
+
+ return elements;
+ })();
+
+ // ## view/scanner.js
+ var __m17 = (function(can, elements) {
+
+ var newLine = /(\r|\n)+/g,
+ // Escapes characters starting with `\`.
+ clean = function(content) {
+ return content
+ .split('\\').join("\\\\")
+ .split("\n").join("\\n")
+ .split('"').join('\\"')
+ .split("\t").join("\\t");
+ },
+ // Returns a tagName to use as a temporary placeholder for live content
+ // looks forward ... could be slow, but we only do it when necessary
+ getTag = function(tagName, tokens, i) {
+ // if a tagName is provided, use that
+ if (tagName) {
+ return tagName;
+ } else {
+ // otherwise go searching for the next two tokens like "<",TAG
+ while (i < tokens.length) {
+ if (tokens[i] == "<" && elements.reverseTagMap[tokens[i + 1]]) {
+ return elements.reverseTagMap[tokens[i + 1]];
+ }
+ i++;
+ }
+ }
+ return '';
+ },
+ bracketNum = function(content) {
+ return (--content.split("{").length) - (--content.split("}").length);
+ },
+ myEval = function(script) {
+ eval(script);
+ },
+ attrReg = /([^\s]+)[\s]*=[\s]*$/,
+ // Commands for caching.
+ startTxt = 'var ___v1ew = [];',
+ finishTxt = "return ___v1ew.join('')",
+ put_cmd = "___v1ew.push(",
+ insert_cmd = put_cmd,
+ // Global controls (used by other functions to know where we are).
+ // Are we inside a tag?
+ htmlTag = null,
+ // Are we within a quote within a tag?
+ quote = null,
+ // What was the text before the current quote? (used to get the `attr` name)
+ beforeQuote = null,
+ // Whether a rescan is in progress
+ rescan = null,
+ // Used to mark where the element is.
+ status = function() {
+ // `t` - `1`.
+ // `h` - `0`.
+ // `q` - String `beforeQuote`.
+ return quote ? "'" + beforeQuote.match(attrReg)[1] + "'" : (htmlTag ? 1 : 0);
+ };
+
+ can.view.Scanner = Scanner = function(options) {
+ // Set options on self
+ can.extend(this, {
+ text: {},
+ tokens: []
+ }, options);
+
+ // Cache a token lookup
+ this.tokenReg = [];
+ this.tokenSimple = {
+ "<": "<",
+ ">": ">",
+ '"': '"',
+ "'": "'"
+ };
+ this.tokenComplex = [];
+ this.tokenMap = {};
+ for (var i = 0, token; token = this.tokens[i]; i++) {
+
+
+ // Save complex mappings (custom regexp)
+ if (token[2]) {
+ this.tokenReg.push(token[2]);
+ this.tokenComplex.push({
+ abbr: token[1],
+ re: new RegExp(token[2]),
+ rescan: token[3]
+ });
+ }
+ // Save simple mappings (string only, no regexp)
+ else {
+ this.tokenReg.push(token[1]);
+ this.tokenSimple[token[1]] = token[0];
+ }
+ this.tokenMap[token[0]] = token[1];
+ }
+
+ // Cache the token registry.
+ this.tokenReg = new RegExp("(" + this.tokenReg.slice(0).concat(["<", ">", '"', "'"]).join("|") + ")", "g");
+ };
+
+ Scanner.prototype = {
+
+ helpers: [
+
+ {
+ name: /\s*\(([\$\w]+)\)\s*->([^\n]*)/,
+ fn: function(content) {
+ var quickFunc = /\s*\(([\$\w]+)\)\s*->([^\n]*)/,
+ parts = content.match(quickFunc);
+
+ return "can.proxy(function(__){var " + parts[1] + "=can.$(__);" + parts[2] + "}, this);";
+ }
+ }
+ ],
+
+ scan: function(source, name) {
+ var tokens = [],
+ last = 0,
+ simple = this.tokenSimple,
+ complex = this.tokenComplex;
+
+ source = source.replace(newLine, "\n");
+ if (this.transform) {
+ source = this.transform(source);
+ }
+ source.replace(this.tokenReg, function(whole, part) {
+ // offset is the second to last argument
+ var offset = arguments[arguments.length - 2];
+
+ // if the next token starts after the last token ends
+ // push what's in between
+ if (offset > last) {
+ tokens.push(source.substring(last, offset));
+ }
+
+ // push the simple token (if there is one)
+ if (simple[whole]) {
+ tokens.push(whole);
+ }
+ // otherwise lookup complex tokens
+ else {
+ for (var i = 0, token; token = complex[i]; i++) {
+ if (token.re.test(whole)) {
+ tokens.push(token.abbr);
+ // Push a rescan function if one exists
+ if (token.rescan) {
+ tokens.push(token.rescan(part));
+ }
+ break;
+ }
+ }
+ }
+
+ // update the position of the last part of the last token
+ last = offset + part.length;
+ });
+
+ // if there's something at the end, add it
+ if (last < source.length) {
+ tokens.push(source.substr(last));
+ }
+
+ var content = '',
+ buff = [startTxt + (this.text.start || '')],
+ // Helper `function` for putting stuff in the view concat.
+ put = function(content, bonus) {
+ buff.push(put_cmd, '"', clean(content), '"' + (bonus || '') + ');');
+ },
+ // A stack used to keep track of how we should end a bracket
+ // `}`.
+ // Once we have a `<%= %>` with a `leftBracket`,
+ // we store how the file should end here (either `))` or `;`).
+ endStack = [],
+ // The last token, used to remember which tag we are in.
+ lastToken,
+ // The corresponding magic tag.
+ startTag = null,
+ // Was there a magic tag inside an html tag?
+ magicInTag = false,
+ // The current tag name.
+ tagName = '',
+ // stack of tagNames
+ tagNames = [],
+ // Pop from tagNames?
+ popTagName = false,
+ // Declared here.
+ bracketCount,
+ i = 0,
+ token,
+ tmap = this.tokenMap;
+
+ // Reinitialize the tag state goodness.
+ htmlTag = quote = beforeQuote = null;
+
+ for (;
+ (token = tokens[i++]) !== undefined;) {
+ if (startTag === null) {
+ switch (token) {
+ case tmap.left:
+ case tmap.escapeLeft:
+ case tmap.returnLeft:
+ magicInTag = htmlTag && 1;
+ case tmap.commentLeft:
+ // A new line -- just add whatever content within a clean.
+ // Reset everything.
+ startTag = token;
+ if (content.length) {
+ put(content);
+ }
+ content = '';
+ break;
+ case tmap.escapeFull:
+ // This is a full line escape (a line that contains only whitespace and escaped logic)
+ // Break it up into escape left and right
+ magicInTag = htmlTag && 1;
+ rescan = 1;
+ startTag = tmap.escapeLeft;
+ if (content.length) {
+ put(content);
+ }
+ rescan = tokens[i++];
+ content = rescan.content || rescan;
+ if (rescan.before) {
+ put(rescan.before);
+ }
+ tokens.splice(i, 0, tmap.right);
+ break;
+ case tmap.commentFull:
+ // Ignore full line comments.
+ break;
+ case tmap.templateLeft:
+ content += tmap.left;
+ break;
+ case '<':
+ // Make sure we are not in a comment.
+ if (tokens[i].indexOf("!--") !== 0) {
+ htmlTag = 1;
+ magicInTag = 0;
+ }
+ content += token;
+ break;
+ case '>':
+ htmlTag = 0;
+ // content.substr(-1) doesn't work in IE7/8
+ var emptyElement = content.substr(content.length - 1) == "/" || content.substr(content.length - 2) == "--";
+ // if there was a magic tag
+ // or it's an element that has text content between its tags,
+ // but content is not other tags add a hookup
+ // TODO: we should only add `can.EJS.pending()` if there's a magic tag
+ // within the html tags.
+ if (magicInTag || !popTagName && elements.tagToContentPropMap[tagNames[tagNames.length - 1]]) {
+ // make sure / of /> is on the left of pending
+ if (emptyElement) {
+ put(content.substr(0, content.length - 1), ",can.view.pending(),\"/>\"");
+ } else {
+ put(content, ",can.view.pending(),\">\"");
+ }
+ content = '';
+ magicInTag = 0;
+ } else {
+ content += token;
+ }
+ // if it's a tag like <input/>
+ if (emptyElement || popTagName) {
+ // remove the current tag in the stack
+ tagNames.pop();
+ // set the current tag to the previous parent
+ tagName = tagNames[tagNames.length - 1];
+ // Don't pop next time
+ popTagName = false;
+ }
+ break;
+ case "'":
+ case '"':
+ // If we are in an html tag, finding matching quotes.
+ if (htmlTag) {
+ // We have a quote and it matches.
+ if (quote && quote === token) {
+ // We are exiting the quote.
+ quote = null;
+ // Otherwise we are creating a quote.
+ // TODO: does this handle `\`?
+ } else if (quote === null) {
+ quote = token;
+ beforeQuote = lastToken;
+ }
+ }
+ default:
+ // Track the current tag
+ if (lastToken === '<') {
+ tagName = token.split(/\s/)[0];
+ if (tagName.indexOf("/") === 0 && tagNames[tagNames.length - 1] === tagName.substr(1)) {
+ // set tagName to the last tagName
+ // if there are no more tagNames, we'll rely on getTag.
+ tagName = tagNames[tagNames.length - 1];
+ popTagName = true;
+ } else {
+ tagNames.push(tagName);
+ }
+ }
+ content += token;
+ break;
+ }
+ } else {
+ // We have a start tag.
+ switch (token) {
+ case tmap.right:
+ case tmap.returnRight:
+ switch (startTag) {
+ case tmap.left:
+ // Get the number of `{ minus }`
+ bracketCount = bracketNum(content);
+
+ // We are ending a block.
+ if (bracketCount == 1) {
+
+ // We are starting on.
+ buff.push(insert_cmd, "can.view.txt(0,'" + getTag(tagName, tokens, i) + "'," + status() + ",this,function(){", startTxt, content);
+
+ endStack.push({
+ before: "",
+ after: finishTxt + "}));\n"
+ });
+ } else {
+
+ // How are we ending this statement?
+ last = // If the stack has value and we are ending a block...
+ endStack.length && bracketCount == -1 ? // Use the last item in the block stack.
+ endStack.pop() : // Or use the default ending.
+ {
+ after: ";"
+ };
+
+ // If we are ending a returning block,
+ // add the finish text which returns the result of the
+ // block.
+ if (last.before) {
+ buff.push(last.before);
+ }
+ // Add the remaining content.
+ buff.push(content, ";", last.after);
+ }
+ break;
+ case tmap.escapeLeft:
+ case tmap.returnLeft:
+ // We have an extra `{` -> `block`.
+ // Get the number of `{ minus }`.
+ bracketCount = bracketNum(content);
+ // If we have more `{`, it means there is a block.
+ if (bracketCount) {
+ // When we return to the same # of `{` vs `}` end with a `doubleParent`.
+ endStack.push({
+ before: finishTxt,
+ after: "}));"
+ });
+ }
+
+ var escaped = startTag === tmap.escapeLeft ? 1 : 0,
+ commands = {
+ insert: insert_cmd,
+ tagName: getTag(tagName, tokens, i),
+ status: status()
+ };
+
+ for (var ii = 0; ii < this.helpers.length; ii++) {
+ // Match the helper based on helper
+ // regex name value
+ var helper = this.helpers[ii];
+ if (helper.name.test(content)) {
+ content = helper.fn(content, commands);
+
+ // dont escape partials
+ if (helper.name.source == /^>[\s]*\w*/.source) {
+ escaped = 0;
+ }
+ break;
+ }
+ }
+
+ // Handle special cases
+ if (typeof content == 'object') {
+ if (content.raw) {
+ buff.push(content.raw);
+ }
+ } else {
+ // If we have `<%== a(function(){ %>` then we want
+ // `can.EJS.text(0,this, function(){ return a(function(){ var _v1ew = [];`.
+ buff.push(insert_cmd, "can.view.txt(" + escaped + ",'" + tagName + "'," + status() + ",this,function(){ " + (this.text.escape || '') + "return ", content,
+ // If we have a block.
+ bracketCount ?
+ // Start with startTxt `"var _v1ew = [];"`.
+ startTxt :
+ // If not, add `doubleParent` to close push and text.
+ "}));");
+ }
+
+ if (rescan && rescan.after && rescan.after.length) {
+ put(rescan.after.length);
+ rescan = null;
+ }
+ break;
+ }
+ startTag = null;
+ content = '';
+ break;
+ case tmap.templateLeft:
+ content += tmap.left;
+ break;
+ default:
+ content += token;
+ break;
+ }
+ }
+ lastToken = token;
+ }
+
+ // Put it together...
+ if (content.length) {
+ // Should be `content.dump` in Ruby.
+ put(content);
+ }
+ buff.push(";");
+
+ var template = buff.join(''),
+ out = {
+ out: 'with(_VIEW) { with (_CONTEXT) {' + template + " " + finishTxt + "}}"
+ };
+ // Use `eval` instead of creating a function, because it is easier to debug.
+ myEval.call(out, 'this.fn = (function(_CONTEXT,_VIEW){' + out.out + '});\r\n//@ sourceURL=' + name + ".js");
+
+ return out;
+ }
+ };
+
+ return Scanner;
+ })(__m15, __m18);
+
+ // ## view/node_lists.js
+ var __m21 = (function(can) {
+
+ // text node expando test
+ var canExpando = true;
+ try {
+ document.createTextNode('')._ = 0;
+ } catch (ex) {
+ canExpando = false;
+ }
+
+ // a mapping of element ids to nodeList ids
+ var nodeMap = {},
+ // a mapping of ids to text nodes
+ textNodeMap = {},
+ // a mapping of nodeList ids to nodeList
+ nodeListMap = {},
+ expando = "ejs_" + Math.random(),
+ _id = 0,
+ id = function(node) {
+ if (canExpando || node.nodeType !== 3) {
+ if (node[expando]) {
+ return node[expando];
+ } else {
+ return node[expando] = (node.nodeName ? "element_" : "obj_") + (++_id);
+ }
+ } else {
+ for (var textNodeID in textNodeMap) {
+ if (textNodeMap[textNodeID] === node) {
+ return textNodeID;
+ }
+ }
+
+ textNodeMap["text_" + (++_id)] = node;
+ return "text_" + _id;
+ }
+ },
+ // removes a nodeListId from a node's nodeListIds
+ removeNodeListId = function(node, nodeListId) {
+ var nodeListIds = nodeMap[id(node)];
+ if (nodeListIds) {
+ var index = can.inArray(nodeListId, nodeListIds);
+
+ if (index >= 0) {
+ nodeListIds.splice(index, 1);
+ }
+ if (!nodeListIds.length) {
+ delete nodeMap[id(node)];
+ }
+ }
+ },
+ addNodeListId = function(node, nodeListId) {
+ var nodeListIds = nodeMap[id(node)];
+ if (!nodeListIds) {
+ nodeListIds = nodeMap[id(node)] = [];
+ }
+ nodeListIds.push(nodeListId);
+ };
+
+ var nodeLists = {
+ id: id,
+ // replaces the contents of one node list with the nodes in another list
+ replace: function(oldNodeList, newNodes) {
+ // for each node in the node list
+ oldNodeList = can.makeArray(oldNodeList);
+
+ // try every set
+ //can.each( oldNodeList, function(node){
+ var node = oldNodeList[0]
+ // for each nodeList the node is in
+ can.each(can.makeArray(nodeMap[id(node)]), function(nodeListId) {
+
+ // if startNode to endNode is
+ // within list, replace that list
+ // I think the problem is not the WHOLE part is being
+ // matched
+ var nodeList = nodeListMap[nodeListId],
+ startIndex = can.inArray(node, nodeList),
+ endIndex = can.inArray(oldNodeList[oldNodeList.length - 1], nodeList);
+
+
+ // remove this nodeListId from each node
+ if (startIndex >= 0 && endIndex >= 0) {
+ for (var i = startIndex; i <= endIndex; i++) {
+ var n = nodeList[i];
+ removeNodeListId(n, nodeListId);
+ }
+ // swap in new nodes into the nodeLIst
+ nodeList.splice.apply(nodeList, [startIndex, endIndex - startIndex + 1].concat(newNodes));
+
+ // tell these new nodes they belong to the nodeList
+ can.each(newNodes, function(node) {
+ addNodeListId(node, nodeListId);
+ });
+ } else {
+ nodeLists.unregister(nodeList);
+ }
+ });
+ //});
+ },
+ // registers a list of nodes
+ register: function(nodeList) {
+ var nLId = id(nodeList);
+ nodeListMap[nLId] = nodeList;
+
+ can.each(nodeList, function(node) {
+ addNodeListId(node, nLId);
+ });
+
+ },
+ // removes mappings
+ unregister: function(nodeList) {
+ var nLId = id(nodeList);
+ can.each(nodeList, function(node) {
+ removeNodeListId(node, nLId);
+ });
+ delete nodeListMap[nLId];
+ },
+ nodeMap: nodeMap,
+ nodeListMap: nodeListMap
+ }
+ var ids = function(nodeList) {
+ return nodeList.map(function(n) {
+ return id(n) + ":" + (n.innerHTML || n.nodeValue)
+ })
+ }
+ return nodeLists;
+
+ })(__m3);
+
+ // ## view/live.js
+ var __m20 = (function(can, elements, view, nodeLists) {
+ // ## live.js
+ // The live module provides live binding for computes
+ // and can.Observe.List.
+ // Currently, it's API is designed for `can/view/render`, but
+ // it could easily be used for other purposes.
+
+ // ### Helper methods
+ // #### setup
+ // `setup(HTMLElement, bind(data), unbind(data)) -> data`
+ // Calls bind right away, but will call unbind
+ // if the element is "destroyed" (removed from the DOM).
+ var setup = function(el, bind, unbind) {
+ var teardown = function() {
+ unbind(data)
+ can.unbind.call(el, 'destroyed', teardown);
+ },
+ data = {
+ teardownCheck: function(parent) {
+ if (!parent) {
+ teardown();
+ }
+ }
+ }
+
+ can.bind.call(el, 'destroyed', teardown);
+ bind(data)
+ return data;
+ },
+ // #### listen
+ // Calls setup, but presets bind and unbind to
+ // operate on a compute
+ listen = function(el, compute, change) {
+ return setup(el, function() {
+ compute.bind("change", change);
+ }, function(data) {
+ compute.unbind("change", change);
+ if (data.nodeList) {
+ nodeLists.unregister(data.nodeList);
+ }
+ });
+ },
+ // #### getAttributeParts
+ // Breaks up a string like foo='bar' into ["foo","'bar'""]
+ getAttributeParts = function(newVal) {
+ return (newVal || "").replace(/['"]/g, '').split('=')
+ }
+ // #### insertElementsAfter
+ // Appends elements after the last item in oldElements.
+ insertElementsAfter = function(oldElements, newFrag) {
+ var last = oldElements[oldElements.length - 1];
+
+ // Insert it in the `document` or `documentFragment`
+ if (last.nextSibling) {
+ last.parentNode.insertBefore(newFrag, last.nextSibling);
+ } else {
+ last.parentNode.appendChild(newFrag);
+ }
+ };
+
+ var live = {
+ nodeLists: nodeLists,
+ list: function(el, list, func, context, parentNode) {
+ // A mapping of the index to an array
+ // of elements that represent the item.
+ // Each array is registered so child or parent
+ // live structures can update the elements
+ var nodesMap = [],
+
+ add = function(ev, items, index) {
+
+ // Collect new html and mappings
+ var frag = document.createDocumentFragment(),
+ newMappings = [];
+ can.each(items, function(item) {
+ var itemHTML = func.call(context, item),
+ itemFrag = can.view.frag(itemHTML, parentNode);
+
+ newMappings.push(can.makeArray(itemFrag.childNodes));
+ frag.appendChild(itemFrag);
+ })
+
+ // Inserting at the end of the list
+ if (!nodesMap[index]) {
+ insertElementsAfter(
+ index == 0 ? [text] :
+ nodesMap[index - 1], frag)
+ } else {
+ var el = nodesMap[index][0];
+ el.parentNode.insertBefore(frag, el)
+ }
+ // register each item
+ can.each(newMappings, function(nodeList) {
+ nodeLists.register(nodeList)
+ });
+ [].splice.apply(nodesMap, [index, 0].concat(newMappings));
+ },
+ remove = function(ev, items, index) {
+ var removedMappings = nodesMap.splice(index, items.length),
+ itemsToRemove = [];
+
+ can.each(removedMappings, function(nodeList) {
+ // add items that we will remove all at once
+ [].push.apply(itemsToRemove, nodeList)
+ // Update any parent lists to remove these items
+ nodeLists.replace(nodeList, []);
+ // unregister the list
+ nodeLists.unregister(nodeList);
+
+ });
+ can.remove(can.$(itemsToRemove));
+ },
+ parentNode = elements.getParentNode(el, parentNode),
+ text = document.createTextNode("");
+
+ // Setup binding and teardown to add and remove events
+ setup(parentNode, function() {
+ list.bind("add", add).bind("remove", remove)
+ }, function() {
+ list.unbind("add", add).unbind("remove", remove);
+ can.each(nodesMap, function(nodeList) {
+ nodeLists.unregister(nodeList);
+ })
+ })
+
+ insertElementsAfter([el], text);
+ can.remove(can.$(el));
+ add({}, list, 0);
+
+ },
+ html: function(el, compute, parentNode) {
+ var parentNode = elements.getParentNode(el, parentNode),
+
+ data = listen(parentNode, compute, function(ev, newVal, oldVal) {
+ var attached = nodes[0].parentNode;
+ // update the nodes in the DOM with the new rendered value
+ if (attached) {
+ makeAndPut(newVal);
+ }
+ data.teardownCheck(nodes[0].parentNode);
+ });
+
+ var nodes,
+ makeAndPut = function(val) {
+ // create the fragment, but don't hook it up
+ // we need to insert it into the document first
+ var frag = can.view.frag(val, parentNode),
+ // keep a reference to each node
+ newNodes = can.makeArray(frag.childNodes);
+ // Insert it in the `document` or `documentFragment`
+ insertElementsAfter(nodes || [el], frag)
+ // nodes hasn't been set yet
+ if (!nodes) {
+ can.remove(can.$(el));
+ nodes = newNodes;
+ // set the teardown nodeList
+ data.nodeList = nodes;
+ nodeLists.register(nodes);
+ } else {
+ // Update node Array's to point to new nodes
+ // and then remove the old nodes.
+ // It has to be in this order for Mootools
+ // and IE because somehow, after an element
+ // is removed from the DOM, it loses its
+ // expando values.
+ var nodesToRemove = can.makeArray(nodes);
+ nodeLists.replace(nodes, newNodes);
+ can.remove(can.$(nodesToRemove));
+ }
+ };
+ makeAndPut(compute(), [el]);
+
+ },
+ text: function(el, compute, parentNode) {
+ var parent = elements.getParentNode(el, parentNode);
+
+ // setup listening right away so we don't have to re-calculate value
+ var data = listen(el.parentNode !== parent ? el.parentNode : parent, compute, function(ev, newVal, oldVal) {
+ // Sometimes this is 'unknown' in IE and will throw an exception if it is
+ if (typeof node.nodeValue != 'unknown') {
+ node.nodeValue = "" + newVal;
+ }
+ data.teardownCheck(node.parentNode);
+ });
+
+ var node = document.createTextNode(compute());
+
+ if (el.parentNode !== parent) {
+ parent = el.parentNode;
+ parent.insertBefore(node, el);
+ parent.removeChild(el);
+ } else {
+ parent.insertBefore(node, el);
+ parent.removeChild(el);
+ }
+ },
+ attributes: function(el, compute, currentValue) {
+ var setAttrs = function(newVal) {
+ var parts = getAttributeParts(newVal),
+ newAttrName = parts.shift();
+
+ // Remove if we have a change and used to have an `attrName`.
+ if ((newAttrName != attrName) && attrName) {
+ elements.removeAttr(el, attrName);
+ }
+ // Set if we have a new `attrName`.
+ if (newAttrName) {
+ elements.setAttr(el, newAttrName, parts.join('='));
+ attrName = newAttrName;
+ }
+ }
+
+ listen(el, compute, function(ev, newVal) {
+ setAttrs(newVal)
+ })
+ // current value has been set
+ if (arguments.length >= 3) {
+ var attrName = getAttributeParts(currentValue)[0]
+ } else {
+ setAttrs(compute())
+ }
+ },
+ attributePlaceholder: '__!!__',
+ attributeReplace: /__!!__/g,
+ attribute: function(el, attributeName, compute) {
+ listen(el, compute, function(ev, newVal) {
+ elements.setAttr(el, attributeName, hook.render());
+ })
+
+ var wrapped = can.$(el),
+ hooks;
+
+ // Get the list of hookups or create one for this element.
+ // Hooks is a map of attribute names to hookup `data`s.
+ // Each hookup data has:
+ // `render` - A `function` to render the value of the attribute.
+ // `funcs` - A list of hookup `function`s on that attribute.
+ // `batchNum` - The last event `batchNum`, used for performance.
+ hooks = can.data(wrapped, 'hooks');
+ if (!hooks) {
+ can.data(wrapped, 'hooks', hooks = {});
+ }
+
+ // Get the attribute value.
+ var attr = elements.getAttr(el, attributeName),
+ // Split the attribute value by the template.
+ // Only split out the first __!!__ so if we have multiple hookups in the same attribute,
+ // they will be put in the right spot on first render
+ parts = attr.split(live.attributePlaceholder),
+ goodParts = [],
+ hook;
+ goodParts.push(parts.shift(),
+ parts.join(live.attributePlaceholder));
+
+ // If we already had a hookup for this attribute...
+ if (hooks[attributeName]) {
+ // Just add to that attribute's list of `function`s.
+ hooks[attributeName].computes.push(compute);
+ } else {
+ // Create the hookup data.
+ hooks[attributeName] = {
+ render: function() {
+ var i = 0,
+ // attr doesn't have a value in IE
+ newAttr = attr ? attr.replace(live.attributeReplace, function() {
+ return elements.contentText(hook.computes[i++]());
+ }) : elements.contentText(hook.computes[i++]());
+ return newAttr;
+ },
+ computes: [compute],
+ batchNum: undefined
+ };
+ }
+
+ // Save the hook for slightly faster performance.
+ hook = hooks[attributeName];
+
+ // Insert the value in parts.
+ goodParts.splice(1, 0, compute());
+
+ // Set the attribute.
+ elements.setAttr(el, attributeName, goodParts.join(""));
+
+ }
+ }
+ return live;
+
+ })(__m3, __m18, __m15, __m21);
+
+ // ## view/render.js
+ var __m19 = (function(can, elements, live) {
+
+ var pendingHookups = [],
+ tagChildren = function(tagName) {
+ var newTag = elements.tagMap[tagName] || "span";
+ if (newTag === "span") {
+ //innerHTML in IE doesn't honor leading whitespace after empty elements
+ return "@@!!@@";
+ }
+ return "<" + newTag + ">" + tagChildren(newTag) + "</" + newTag + ">";
+ },
+ contentText = function(input, tag) {
+
+ // If it's a string, return.
+ if (typeof input == 'string') {
+ return input;
+ }
+ // If has no value, return an empty string.
+ if (!input && input !== 0) {
+ return '';
+ }
+
+ // If it's an object, and it has a hookup method.
+ var hook = (input.hookup &&
+
+ // Make a function call the hookup method.
+
+ function(el, id) {
+ input.hookup.call(input, el, id);
+ }) ||
+
+ // Or if it's a `function`, just use the input.
+ (typeof input == 'function' && input);
+
+ // Finally, if there is a `function` to hookup on some dom,
+ // add it to pending hookups.
+ if (hook) {
+ if (tag) {
+ return "<" + tag + " " + can.view.hook(hook) + "></" + tag + ">"
+ } else {
+ pendingHookups.push(hook);
+ }
+
+ return '';
+ }
+
+ // Finally, if all else is `false`, `toString()` it.
+ return "" + input;
+ },
+ // Returns escaped/sanatized content for anything other than a live-binding
+ contentEscape = function(txt) {
+ return (typeof txt == 'string' || typeof txt == 'number') ?
+ can.esc(txt) :
+ contentText(txt);
+ };
+
+ var current;
+
+ can.extend(can.view, {
+ live: live,
+ setupLists: function() {
+
+ var old = can.view.lists,
+ data;
+
+ can.view.lists = function(list, renderer) {
+ data = {
+ list: list,
+ renderer: renderer
+ }
+ }
+ return function() {
+ can.view.lists = old;
+ return data;
+ }
+ },
+ pending: function() {
+ // TODO, make this only run for the right tagName
+ var hooks = pendingHookups.slice(0);
+ lastHookups = hooks;
+ pendingHookups = [];
+ return can.view.hook(function(el) {
+ can.each(hooks, function(fn) {
+ fn(el);
+ });
+ });
+ },
+
+
+ txt: function(escape, tagName, status, self, func) {
+ var listTeardown = can.view.setupLists(),
+ emptyHandler = function() {},
+ unbind = function() {
+ compute.unbind("change", emptyHandler)
+ };
+
+ var compute = can.compute(func, self, false);
+ // bind to get and temporarily cache the value
+ compute.bind("change", emptyHandler);
+ // call the "wrapping" function and get the binding information
+ var tag = (elements.tagMap[tagName] || "span"),
+ listData = listTeardown(),
+ value = compute();
+
+
+ if (listData) {
+ return "<" + tag + can.view.hook(function(el, parentNode) {
+ live.list(el, listData.list, listData.renderer, self, parentNode);
+ }) + "></" + tag + ">";
+ }
+
+ // If we had no observes just return the value returned by func.
+ if (!compute.hasDependencies) {
+ unbind();
+ return (escape || status !== 0 ? contentEscape : contentText)(value, status === 0 && tag);
+ }
+
+ // the property (instead of innerHTML elements) to adjust. For
+ // example options should use textContent
+ var contentProp = elements.tagToContentPropMap[tagName];
+
+
+ // The magic tag is outside or between tags.
+ if (status === 0 && !contentProp) {
+ // Return an element tag with a hookup in place of the content
+ return "<" + tag + can.view.hook(
+ escape ?
+ // If we are escaping, replace the parentNode with
+ // a text node who's value is `func`'s return value.
+
+ function(el, parentNode) {
+ live.text(el, compute, parentNode);
+ unbind();
+ } :
+ // If we are not escaping, replace the parentNode with a
+ // documentFragment created as with `func`'s return value.
+
+ function(el, parentNode) {
+ live.html(el, compute, parentNode);
+ unbind();
+ //children have to be properly nested HTML for buildFragment to work properly
+ }) + ">" + tagChildren(tag) + "</" + tag + ">";
+ // In a tag, but not in an attribute
+ } else if (status === 1) {
+ // remember the old attr name
+ pendingHookups.push(function(el) {
+ live.attributes(el, compute, compute());
+ unbind();
+ });
+ return compute();
+ } else { // In an attribute...
+ var attributeName = status === 0 ? contentProp : status;
+ // if the magic tag is inside the element, like `<option><% TAG %></option>`,
+ // we add this hookup to the last element (ex: `option`'s) hookups.
+ // Otherwise, the magic tag is in an attribute, just add to the current element's
+ // hookups.
+ (status === 0 ? lastHookups : pendingHookups).push(function(el) {
+ live.attribute(el, attributeName, compute);
+ unbind();
+ });
+ return live.attributePlaceholder;
+ }
+ }
+ });
+
+ return can;
+ })(__m15, __m18, __m20, __m2);
+
+ // ## view/ejs/ejs.js
+ var __m16 = (function(can) {
+ // ## ejs.js
+ // `can.EJS`
+ // _Embedded JavaScript Templates._
+
+ // Helper methods.
+ var extend = can.extend,
+ EJS = function(options) {
+ // Supports calling EJS without the constructor
+ // This returns a function that renders the template.
+ if (this.constructor != EJS) {
+ var ejs = new EJS(options);
+ return function(data, helpers) {
+ return ejs.render(data, helpers);
+ };
+ }
+ // If we get a `function` directly, it probably is coming from
+ // a `steal`-packaged view.
+ if (typeof options == "function") {
+ this.template = {
+ fn: options
+ };
+ return;
+ }
+ // Set options on self.
+ extend(this, options);
+ this.template = this.scanner.scan(this.text, this.name);
+ };
+
+ can.EJS = EJS;
+
+
+ EJS.prototype.
+
+ render = function(object, extraHelpers) {
+ object = object || {};
+ return this.template.fn.call(object, object, new EJS.Helpers(object, extraHelpers || {}));
+ };
+
+ extend(EJS.prototype, {
+
+ scanner: new can.view.Scanner({
+
+ tokens: [
+ ["templateLeft", "<%%"], // Template
+ ["templateRight", "%>"], // Right Template
+ ["returnLeft", "<%=="], // Return Unescaped
+ ["escapeLeft", "<%="], // Return Escaped
+ ["commentLeft", "<%#"], // Comment
+ ["left", "<%"], // Run --- this is hack for now
+ ["right", "%>"], // Right -> All have same FOR Mustache ...
+ ["returnRight", "%>"]
+ ],
+
+
+ transform: function(source) {
+ return source.replace(/<%([\s\S]+?)%>/gm, function(whole, part) {
+ var brackets = [],
+ foundBracketPair,
+ i;
+
+ // Look for brackets (for removing self-contained blocks)
+ part.replace(/[{}]/gm, function(bracket, offset) {
+ brackets.push([bracket, offset]);
+ });
+
+ // Remove bracket pairs from the list of replacements
+ do {
+ foundBracketPair = false;
+ for (i = brackets.length - 2; i >= 0; i--) {
+ if (brackets[i][0] == '{' && brackets[i + 1][0] == '}') {
+ brackets.splice(i, 2);
+ foundBracketPair = true;
+ break;
+ }
+ }
+ } while (foundBracketPair);
+
+ // Unmatched brackets found, inject EJS tags
+ if (brackets.length >= 2) {
+ var result = ['<%'],
+ bracket,
+ last = 0;
+ for (i = 0; bracket = brackets[i]; i++) {
+ result.push(part.substring(last, last = bracket[1]));
+ if ((bracket[0] == '{' && i < brackets.length - 1) || (bracket[0] == '}' && i > 0)) {
+ result.push(bracket[0] == '{' ? '{ %><% ' : ' %><% }');
+ } else {
+ result.push(bracket[0]);
+ }
+ ++last;
+ }
+ result.push(part.substring(last), '%>');
+ return result.join('');
+ }
+ // Otherwise return the original
+ else {
+ return '<%' + part + '%>';
+ }
+ });
+ }
+ })
+ });
+
+ EJS.Helpers = function(data, extras) {
+ this._data = data;
+ this._extras = extras;
+ extend(this, extras);
+ };
+
+
+ EJS.Helpers.prototype = {
+ // TODO Deprecated!!
+ list: function(list, cb) {
+
+ can.each(list, function(item, i) {
+ cb(item, i, list)
+ })
+ },
+ each: function(list, cb) {
+ // Normal arrays don't get live updated
+ if (can.isArray(list)) {
+ this.list(list, cb);
+ } else {
+ can.view.lists(list, cb);
+ }
+ }
+ };
+
+ // Options for `steal`'s build.
+ can.view.register({
+ suffix: "ejs",
+ // returns a `function` that renders the view.
+ script: function(id, src) {
+ return "can.EJS(function(_CONTEXT,_VIEW) { " + new EJS({
+ text: src,
+ name: id
+ }).template.out + " })";
+ },
+ renderer: function(id, text) {
+ return EJS({
+ text: text,
+ name: id
+ });
+ }
+ });
+
+ return can;
+ })(__m3, __m15, __m2, __m13, __m17, __m19);
+
+ // ## control/control.js
+ var __m22 = (function(can) {
+ // ## control.js
+ // `can.Control`
+ // _Controller_
+
+ // Binds an element, returns a function that unbinds.
+ var bind = function(el, ev, callback) {
+
+ can.bind.call(el, ev, callback);
+
+ return function() {
+ can.unbind.call(el, ev, callback);
+ };
+ },
+ isFunction = can.isFunction,
+ extend = can.extend,
+ each = can.each,
+ slice = [].slice,
+ paramReplacer = /\{([^\}]+)\}/g,
+ special = can.getObject("$.event.special", [can]) || {},
+
+ // Binds an element, returns a function that unbinds.
+ delegate = function(el, selector, ev, callback) {
+ can.delegate.call(el, selector, ev, callback);
+ return function() {
+ can.undelegate.call(el, selector, ev, callback);
+ };
+ },
+
+ // Calls bind or unbind depending if there is a selector.
+ binder = function(el, ev, callback, selector) {
+ return selector ?
+ delegate(el, can.trim(selector), ev, callback) :
+ bind(el, ev, callback);
+ },
+
+ basicProcessor;
+
+ var Control = can.Control = can.Construct(
+
+ {
+ // Setup pre-processes which methods are event listeners.
+
+ setup: function() {
+
+ // Allow contollers to inherit "defaults" from super-classes as it
+ // done in `can.Construct`
+ can.Construct.setup.apply(this, arguments);
+
+ // If you didn't provide a name, or are `control`, don't do anything.
+ if (can.Control) {
+
+ // Cache the underscored names.
+ var control = this,
+ funcName;
+
+ // Calculate and cache actions.
+ control.actions = {};
+ for (funcName in control.prototype) {
+ if (control._isAction(funcName)) {
+ control.actions[funcName] = control._action(funcName);
+ }
+ }
+ }
+ },
+
+ // Moves `this` to the first argument, wraps it with `jQuery` if it's an element
+ _shifter: function(context, name) {
+
+ var method = typeof name == "string" ? context[name] : name;
+
+ if (!isFunction(method)) {
+ method = context[method];
+ }
+
+ return function() {
+ context.called = name;
+ return method.apply(context, [this.nodeName ? can.$(this) : this].concat(slice.call(arguments, 0)));
+ };
+ },
+
+ // Return `true` if is an action.
+
+ _isAction: function(methodName) {
+
+ var val = this.prototype[methodName],
+ type = typeof val;
+ // if not the constructor
+ return (methodName !== 'constructor') &&
+ // and is a function or links to a function
+ (type == "function" || (type == "string" && isFunction(this.prototype[val]))) &&
+ // and is in special, a processor, or has a funny character
+ !! (special[methodName] || processors[methodName] || /[^\w]/.test(methodName));
+ },
+ // Takes a method name and the options passed to a control
+ // and tries to return the data necessary to pass to a processor
+ // (something that binds things).
+
+ _action: function(methodName, options) {
+
+ // If we don't have options (a `control` instance), we'll run this
+ // later.
+ paramReplacer.lastIndex = 0;
+ if (options || !paramReplacer.test(methodName)) {
+ // If we have options, run sub to replace templates `{}` with a
+ // value from the options or the window
+ var convertedName = options ? can.sub(methodName, [options, window]) : methodName;
+ if (!convertedName) {
+ return null;
+ }
+ // If a `{}` template resolves to an object, `convertedName` will be
+ // an array
+ var arr = can.isArray(convertedName),
+
+ // Get the name
+ name = arr ? convertedName[1] : convertedName,
+
+ // Grab the event off the end
+ parts = name.split(/\s+/g),
+ event = parts.pop();
+
+ return {
+ processor: processors[event] || basicProcessor,
+ parts: [name, parts.join(" "), event],
+ delegate: arr ? convertedName[0] : undefined
+ };
+ }
+ },
+ // An object of `{eventName : function}` pairs that Control uses to
+ // hook up events auto-magically.
+
+ processors: {},
+ // A object of name-value pairs that act as default values for a
+ // control instance
+ defaults: {}
+
+ }, {
+
+ // Sets `this.element`, saves the control in `data, binds event
+ // handlers.
+
+ setup: function(element, options) {
+
+ var cls = this.constructor,
+ pluginname = cls.pluginName || cls._fullName,
+ arr;
+
+ // Want the raw element here.
+ this.element = can.$(element)
+
+ if (pluginname && pluginname !== 'can_control') {
+ // Set element and `className` on element.
+ this.element.addClass(pluginname);
+ }
+
+ (arr = can.data(this.element, "controls")) || can.data(this.element, "controls", arr = []);
+ arr.push(this);
+
+ // Option merging.
+
+ this.options = extend({}, cls.defaults, options);
+
+ // Bind all event handlers.
+ this.on();
+
+ // Gets passed into `init`.
+
+ return [this.element, this.options];
+ },
+
+ on: function(el, selector, eventName, func) {
+ if (!el) {
+
+ // Adds bindings.
+ this.off();
+
+ // Go through the cached list of actions and use the processor
+ // to bind
+ var cls = this.constructor,
+ bindings = this._bindings,
+ actions = cls.actions,
+ element = this.element,
+ destroyCB = can.Control._shifter(this, "destroy"),
+ funcName, ready;
+
+ for (funcName in actions) {
+ // Only push if we have the action and no option is `undefined`
+ if (actions.hasOwnProperty(funcName) &&
+ (ready = actions[funcName] || cls._action(funcName, this.options))) {
+ bindings.push(ready.processor(ready.delegate || element,
+ ready.parts[2], ready.parts[1], funcName, this));
+ }
+ }
+
+
+ // Setup to be destroyed...
+ // don't bind because we don't want to remove it.
+ can.bind.call(element, "destroyed", destroyCB);
+ bindings.push(function(el) {
+ can.unbind.call(el, "destroyed", destroyCB);
+ });
+ return bindings.length;
+ }
+
+ if (typeof el == 'string') {
+ func = eventName;
+ eventName = selector;
+ selector = el;
+ el = this.element;
+ }
+
+ if (func === undefined) {
+ func = eventName;
+ eventName = selector;
+ selector = null;
+ }
+
+ if (typeof func == 'string') {
+ func = can.Control._shifter(this, func);
+ }
+
+ this._bindings.push(binder(el, eventName, func, selector));
+
+ return this._bindings.length;
+ },
+ // Unbinds all event handlers on the controller.
+
+ off: function() {
+ var el = this.element[0]
+ each(this._bindings || [], function(value) {
+ value(el);
+ });
+ // Adds bindings.
+ this._bindings = [];
+ },
+ // Prepares a `control` for garbage collection
+
+ destroy: function() {
+ //Control already destroyed
+ if (this.element === null) {
+
+ return;
+ }
+ var Class = this.constructor,
+ pluginName = Class.pluginName || Class._fullName,
+ controls;
+
+ // Unbind bindings.
+ this.off();
+
+ if (pluginName && pluginName !== 'can_control') {
+ // Remove the `className`.
+ this.element.removeClass(pluginName);
+ }
+
+ // Remove from `data`.
+ controls = can.data(this.element, "controls");
+ controls.splice(can.inArray(this, controls), 1);
+
+ can.trigger(this, "destroyed"); // In case we want to know if the `control` is removed.
+
+ this.element = null;
+ }
+ });
+
+ var processors = can.Control.processors,
+ // Processors do the binding.
+ // They return a function that unbinds when called.
+ // The basic processor that binds events.
+ basicProcessor = function(el, event, selector, methodName, control) {
+ return binder(el, event, can.Control._shifter(control, methodName), selector);
+ };
+
+ // Set common events to be processed as a `basicProcessor`
+ each(["change", "click", "contextmenu", "dblclick", "keydown", "keyup",
+ "keypress", "mousedown", "mousemove", "mouseout", "mouseover",
+ "mouseup", "reset", "resize", "scroll", "select", "submit", "focusin",
+ "focusout", "mouseenter", "mouseleave",
+ // #104 - Add touch events as default processors
+ // TOOD feature detect?
+ "touchstart", "touchmove", "touchcancel", "touchend", "touchleave"
+ ], function(v) {
+ processors[v] = basicProcessor;
+ });
+
+ return Control;
+ })(__m3, __m1);
+
+ // ## util/string/deparam/deparam.js
+ var __m24 = (function(can) {
+
+ // ## deparam.js
+ // `can.deparam`
+ // _Takes a string of name value pairs and returns a Object literal that represents those params._
+ var digitTest = /^\d+$/,
+ keyBreaker = /([^\[\]]+)|(\[\])/g,
+ paramTest = /([^?#]*)(#.*)?$/,
+ prep = function(str) {
+ return decodeURIComponent(str.replace(/\+/g, " "));
+ };
+
+
+ can.extend(can, {
+
+ deparam: function(params) {
+
+ var data = {},
+ pairs, lastPart;
+
+ if (params && paramTest.test(params)) {
+
+ pairs = params.split('&'),
+
+ can.each(pairs, function(pair) {
+
+ var parts = pair.split('='),
+ key = prep(parts.shift()),
+ value = prep(parts.join("=")),
+ current = data;
+
+ if (key) {
+ parts = key.match(keyBreaker);
+
+ for (var j = 0, l = parts.length - 1; j < l; j++) {
+ if (!current[parts[j]]) {
+ // If what we are pointing to looks like an `array`
+ current[parts[j]] = digitTest.test(parts[j + 1]) || parts[j + 1] == "[]" ? [] : {};
+ }
+ current = current[parts[j]];
+ }
+ lastPart = parts.pop();
+ if (lastPart == "[]") {
+ current.push(value);
+ } else {
+ current[lastPart] = value;
+ }
+ }
+ });
+ }
+ return data;
+ }
+ });
+ return can;
+ })(__m3, __m2);
+
+ // ## route/route.js
+ var __m23 = (function(can) {
+
+ // ## route.js
+ // `can.route`
+ // _Helps manage browser history (and client state) by synchronizing the
+ // `window.location.hash` with a `can.Observe`._
+ // Helper methods used for matching routes.
+ var
+ // `RegExp` used to match route variables of the type ':name'.
+ // Any word character or a period is matched.
+ matcher = /\:([\w\.]+)/g,
+ // Regular expression for identifying &key=value lists.
+ paramsMatcher = /^(?:&[^=]+=[^&]*)+/,
+ // Converts a JS Object into a list of parameters that can be
+ // inserted into an html element tag.
+ makeProps = function(props) {
+ var tags = [];
+ can.each(props, function(val, name) {
+ tags.push((name === 'className' ? 'class' : name) + '="' +
+ (name === "href" ? val : can.esc(val)) + '"');
+ });
+ return tags.join(" ");
+ },
+ // Checks if a route matches the data provided. If any route variable
+ // is not present in the data, the route does not match. If all route
+ // variables are present in the data, the number of matches is returned
+ // to allow discerning between general and more specific routes.
+ matchesData = function(route, data) {
+ var count = 0,
+ i = 0,
+ defaults = {};
+ // look at default values, if they match ...
+ for (var name in route.defaults) {
+ if (route.defaults[name] === data[name]) {
+ // mark as matched
+ defaults[name] = 1;
+ count++;
+ }
+ }
+ for (; i < route.names.length; i++) {
+ if (!data.hasOwnProperty(route.names[i])) {
+ return -1;
+ }
+ if (!defaults[route.names[i]]) {
+ count++;
+ }
+
+ }
+
+ return count;
+ },
+ onready = !0,
+ location = window.location,
+ wrapQuote = function(str) {
+ return (str + '').replace(/([.?*+\^$\[\]\\(){}|\-])/g, "\\$1");
+ },
+ each = can.each,
+ extend = can.extend;
+
+ can.route = function(url, defaults) {
+ defaults = defaults || {};
+ // Extract the variable names and replace with `RegExp` that will match
+ // an atual URL with values.
+ var names = [],
+ test = url.replace(matcher, function(whole, name, i) {
+ names.push(name);
+ var next = "\\" + (url.substr(i + whole.length, 1) || can.route._querySeparator);
+ // a name without a default value HAS to have a value
+ // a name that has a default value can be empty
+ // The `\\` is for string-escaping giving single `\` for `RegExp` escaping.
+ return "([^" + next + "]" + (defaults[name] ? "*" : "+") + ")";
+ });
+
+ // Add route in a form that can be easily figured out.
+ can.route.routes[url] = {
+ // A regular expression that will match the route when variable values
+ // are present; i.e. for `:page/:type` the `RegExp` is `/([\w\.]*)/([\w\.]*)/` which
+ // will match for any value of `:page` and `:type` (word chars or period).
+ test: new RegExp("^" + test + "($|" + wrapQuote(can.route._querySeparator) + ")"),
+ // The original URL, same as the index for this entry in routes.
+ route: url,
+ // An `array` of all the variable names in this route.
+ names: names,
+ // Default values provided for the variables.
+ defaults: defaults,
+ // The number of parts in the URL separated by `/`.
+ length: url.split('/').length
+ };
+ return can.route;
+ };
+
+
+ extend(can.route, {
+
+ _querySeparator: '&',
+ _paramsMatcher: paramsMatcher,
+
+
+ param: function(data, _setRoute) {
+ // Check if the provided data keys match the names in any routes;
+ // Get the one with the most matches.
+ var route,
+ // Need to have at least 1 match.
+ matches = 0,
+ matchCount,
+ routeName = data.route,
+ propCount = 0;
+
+ delete data.route;
+
+ each(data, function() {
+ propCount++;
+ });
+ // Otherwise find route.
+ each(can.route.routes, function(temp, name) {
+ // best route is the first with all defaults matching
+
+
+ matchCount = matchesData(temp, data);
+ if (matchCount > matches) {
+ route = temp;
+ matches = matchCount;
+ }
+ if (matchCount >= propCount) {
+ return false;
+ }
+ });
+ // If we have a route name in our `can.route` data, and it's
+ // just as good as what currently matches, use that
+ if (can.route.routes[routeName] && matchesData(can.route.routes[routeName], data) === matches) {
+ route = can.route.routes[routeName];
+ }
+ // If this is match...
+ if (route) {
+ var cpy = extend({}, data),
+ // Create the url by replacing the var names with the provided data.
+ // If the default value is found an empty string is inserted.
+ res = route.route.replace(matcher, function(whole, name) {
+ delete cpy[name];
+ return data[name] === route.defaults[name] ? "" : encodeURIComponent(data[name]);
+ }),
+ after;
+ // Remove matching default values
+ each(route.defaults, function(val, name) {
+ if (cpy[name] === val) {
+ delete cpy[name];
+ }
+ });
+
+ // The remaining elements of data are added as
+ // `&` separated parameters to the url.
+ after = can.param(cpy);
+ // if we are paraming for setting the hash
+ // we also want to make sure the route value is updated
+ if (_setRoute) {
+ can.route.attr('route', route.route);
+ }
+ return res + (after ? can.route._querySeparator + after : "");
+ }
+ // If no route was found, there is no hash URL, only paramters.
+ return can.isEmptyObject(data) ? "" : can.route._querySeparator + can.param(data);
+ },
+
+ deparam: function(url) {
+ // See if the url matches any routes by testing it against the `route.test` `RegExp`.
+ // By comparing the URL length the most specialized route that matches is used.
+ var route = {
+ length: -1
+ };
+ each(can.route.routes, function(temp, name) {
+ if (temp.test.test(url) && temp.length > route.length) {
+ route = temp;
+ }
+ });
+ // If a route was matched.
+ if (route.length > -1) {
+
+ var // Since `RegExp` backreferences are used in `route.test` (parens)
+ // the parts will contain the full matched string and each variable (back-referenced) value.
+ parts = url.match(route.test),
+ // Start will contain the full matched string; parts contain the variable values.
+ start = parts.shift(),
+ // The remainder will be the `&key=value` list at the end of the URL.
+ remainder = url.substr(start.length - (parts[parts.length - 1] === can.route._querySeparator ? 1 : 0)),
+ // If there is a remainder and it contains a `&key=value` list deparam it.
+ obj = (remainder && can.route._paramsMatcher.test(remainder)) ? can.deparam(remainder.slice(1)) : {};
+
+ // Add the default values for this route.
+ obj = extend(true, {}, route.defaults, obj);
+ // Overwrite each of the default values in `obj` with those in
+ // parts if that part is not empty.
+ each(parts, function(part, i) {
+ if (part && part !== can.route._querySeparator) {
+ obj[route.names[i]] = decodeURIComponent(part);
+ }
+ });
+ obj.route = route.route;
+ return obj;
+ }
+ // If no route was matched, it is parsed as a `&key=value` list.
+ if (url.charAt(0) !== can.route._querySeparator) {
+ url = can.route._querySeparator + url;
+ }
+ return can.route._paramsMatcher.test(url) ? can.deparam(url.slice(1)) : {};
+ },
+
+ data: new can.Observe({}),
+
+ routes: {},
+
+ ready: function(val) {
+ if (val === false) {
+ onready = val;
+ }
+ if (val === true || onready === true) {
+ can.route._setup();
+ setState();
+ }
+ return can.route;
+ },
+
+ url: function(options, merge) {
+ if (merge) {
+ options = extend({}, curParams, options)
+ }
+ return "#!" + can.route.param(options);
+ },
+
+ link: function(name, options, props, merge) {
+ return "<a " + makeProps(
+ extend({
+ href: can.route.url(options, merge)
+ }, props)) + ">" + name + "</a>";
+ },
+
+ current: function(options) {
+ return location.hash == "#!" + can.route.param(options)
+ },
+ _setup: function() {
+ // If the hash changes, update the `can.route.data`.
+ can.bind.call(window, 'hashchange', setState);
+ },
+ _getHash: function() {
+ return location.href.split(/#!?/)[1] || "";
+ },
+ _setHash: function(serialized) {
+ var path = (can.route.param(serialized, true));
+ location.hash = "#!" + path;
+ return path;
+ }
+ });
+
+
+ // The functions in the following list applied to `can.route` (e.g. `can.route.attr('...')`) will
+ // instead act on the `can.route.data` observe.
+ each(['bind', 'unbind', 'delegate', 'undelegate', 'attr', 'removeAttr'], function(name) {
+ can.route[name] = function() {
+ // `delegate` and `undelegate` require
+ // the `can/observe/delegate` plugin
+ if (!can.route.data[name]) {
+ return;
+ }
+
+ return can.route.data[name].apply(can.route.data, arguments);
+ }
+ })
+
+ var // A ~~throttled~~ debounced function called multiple times will only fire once the
+ // timer runs down. Each call resets the timer.
+ timer,
+ // Intermediate storage for `can.route.data`.
+ curParams,
+ // Deparameterizes the portion of the hash of interest and assign the
+ // values to the `can.route.data` removing existing values no longer in the hash.
+ // setState is called typically by hashchange which fires asynchronously
+ // So it's possible that someone started changing the data before the
+ // hashchange event fired. For this reason, it will not set the route data
+ // if the data is changing or the hash already matches the hash that was set.
+ setState = can.route.setState = function() {
+ var hash = can.route._getHash();
+ curParams = can.route.deparam(hash);
+
+ // if the hash data is currently changing, or
+ // the hash is what we set it to anyway, do NOT change the hash
+ if (!changingData || hash !== lastHash) {
+ can.route.attr(curParams, true);
+ }
+ },
+ // The last hash caused by a data change
+ lastHash,
+ // Are data changes pending that haven't yet updated the hash
+ changingData;
+
+ // If the `can.route.data` changes, update the hash.
+ // Using `.serialize()` retrieves the raw data contained in the `observable`.
+ // This function is ~~throttled~~ debounced so it only updates once even if multiple values changed.
+ // This might be able to use batchNum and avoid this.
+ can.route.bind("change", function(ev, attr) {
+ // indicate that data is changing
+ changingData = 1;
+ clearTimeout(timer);
+ timer = setTimeout(function() {
+ // indicate that the hash is set to look like the data
+ changingData = 0;
+ var serialized = can.route.data.serialize();
+
+ lastHash = can.route._setHash(serialized);
+ }, 1);
+ });
+ // `onready` event...
+ can.bind.call(document, "ready", can.route.ready);
+
+ // Libraries other than jQuery don't execute the document `ready` listener
+ // if we are already DOM ready
+ if ((document.readyState === 'complete' || document.readyState === "interactive") && onready) {
+ can.route.ready();
+ }
+
+ // extend route to have a similar property
+ // that is often checked in mustache to determine
+ // an object's observability
+ can.route.constructor.canMakeObserve = can.Observe.canMakeObserve;
+
+ return can.route;
+ })(__m3, __m11, __m24);
+
+ // ## control/route/route.js
+ var __m25 = (function(can) {
+
+ // ## control/route.js
+ // _Controller route integration._
+
+ can.Control.processors.route = function(el, event, selector, funcName, controller) {
+ selector = selector || "";
+ can.route(selector);
+ var batchNum,
+ check = function(ev, attr, how) {
+ if (can.route.attr('route') === (selector) &&
+ (ev.batchNum === undefined || ev.batchNum !== batchNum)) {
+
+ batchNum = ev.batchNum;
+
+ var d = can.route.attr();
+ delete d.route;
+ if (can.isFunction(controller[funcName])) {
+ controller[funcName](d);
+ } else {
+ controller[controller[funcName]](d);
+ }
+
+ }
+ };
+ can.route.bind('change', check);
+ return function() {
+ can.route.unbind('change', check);
+ };
+ };
+
+ return can;
+ })(__m3, __m23, __m22);
+
+ window['can'] = __m4;
+ })();
\ No newline at end of file
Added: tags/nuiton-js-canjs-1.1.6-1/src/site/rst/index.rst
===================================================================
--- tags/nuiton-js-canjs-1.1.6-1/src/site/rst/index.rst (rev 0)
+++ tags/nuiton-js-canjs-1.1.6-1/src/site/rst/index.rst 2013-06-17 15:45:26 UTC (rev 92)
@@ -0,0 +1,35 @@
+
+Utilisation
+===========
+
+::
+
+ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+ <script language="javascript" type="text/javascript" src="<c:url value="/nuiton-js/can.jquery.js"/>"></script>
+
+JS disponible
+-------------
+
+- can.jquery.js
+- can.dojo.js
+- can.mootools.js
+- can.yui.js
+- can.zepto.js
+- can.construct.super.js
+- can.control.plugin.js
+- can.fixture.js
+- can.model.queue.js
+- can.object.js
+- can.observe.attributes.js
+- can.observe.backup.js
+- can.observe.delegate.js
+- can.observe.setter.js
+- can.observe.validations.js
+- can.util.string.js
+- can.view.modifiers.js
+- can.view.mustache.js
+
+CSS disponible
+--------------
+
+aucun
Added: tags/nuiton-js-canjs-1.1.6-1/src/site/site_fr.xml
===================================================================
--- tags/nuiton-js-canjs-1.1.6-1/src/site/site_fr.xml (rev 0)
+++ tags/nuiton-js-canjs-1.1.6-1/src/site/site_fr.xml 2013-06-17 15:45:26 UTC (rev 92)
@@ -0,0 +1,72 @@
+<!--
+ #%L
+ Nuiton JS :: JQuery
+ $Id:$
+ $HeadURL:$
+ %%
+ Copyright (C) 2012 - 2013 CodeLutin
+ %%
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Lesser Public License for more details.
+
+ You should have received a copy of the GNU General Lesser Public
+ License along with this program. If not, see
+ <http://www.gnu.org/licenses/lgpl-3.0.html>.
+ #L%
+ -->
+<project name="${project.name}">
+
+ <bannerLeft>
+ <src alt="NuitonJS">logo.jpg</src>
+ <name>${project.name}</name>
+ <href>index.html</href>
+ </bannerLeft>
+
+ <body>
+
+ <head>
+ <script type="text/javascript"
+ src="http://maven-site.nuiton.org/public/js/mavenpom-site.js">
+ </script>
+
+ <link rel="stylesheet" type="text/css"
+ href="http://maven-site.nuiton.org/public/css/mavenpom-site.css"/>
+ </head>
+
+ <breadcrumbs>
+ <item name="${project.name}" href="${project.url}"/>
+ </breadcrumbs>
+
+ <menu ref="parent"/>
+
+ <menu name="Utilisateurs">
+ <item name="Accueil" href="index.html"/>
+ </menu>
+
+ <menu ref="reports"/>
+
+ <footer>
+
+ <div id='projectMetas'
+ projectversion='${project.version}'
+ platform='${project.platform}'
+ projectid='${project.projectId}'
+ scm='${project.scm.connection}'
+ scmwebeditorenabled='${project.scmwebeditorEnabled}'
+ scmwebeditorurl='${project.scmwebeditorUrl}'
+ siteSourcesType='${project.siteSourcesType}'
+ piwikEnabled='${project.piwikEnabled}'
+ piwikId='${project.piwikId}'>
+ </div>
+ </footer>
+
+ </body>
+
+</project>
1
0
r91 - in tags/nuiton-js-bootstrap-2.3.2-1/src: . site site/rst
by kmorin@users.nuiton.org 17 Jun '13
by kmorin@users.nuiton.org 17 Jun '13
17 Jun '13
Author: kmorin
Date: 2013-06-17 17:44:23 +0200 (Mon, 17 Jun 2013)
New Revision: 91
Url: http://nuiton.org/projects/nuiton-js/repository/revisions/91
Log:
add site
Added:
tags/nuiton-js-bootstrap-2.3.2-1/src/site/
tags/nuiton-js-bootstrap-2.3.2-1/src/site/rst/
tags/nuiton-js-bootstrap-2.3.2-1/src/site/rst/index.rst
tags/nuiton-js-bootstrap-2.3.2-1/src/site/rst/index.rst~
tags/nuiton-js-bootstrap-2.3.2-1/src/site/site_fr.xml
Added: tags/nuiton-js-bootstrap-2.3.2-1/src/site/rst/index.rst
===================================================================
--- tags/nuiton-js-bootstrap-2.3.2-1/src/site/rst/index.rst (rev 0)
+++ tags/nuiton-js-bootstrap-2.3.2-1/src/site/rst/index.rst 2013-06-17 15:44:23 UTC (rev 91)
@@ -0,0 +1,44 @@
+.. -
+.. * #%L
+.. * Nuiton JS :: Bootstrap
+.. * $Id:$
+.. * $HeadURL:$
+.. * %%
+.. * Copyright (C) 2012 - 2013 CodeLutin
+.. * %%
+.. * This program is free software: you can redistribute it and/or modify
+.. * it under the terms of the GNU Lesser General Public License as
+.. * published by the Free Software Foundation, either version 3 of the
+.. * License, or (at your option) any later version.
+.. *
+.. * This program is distributed in the hope that it will be useful,
+.. * but WITHOUT ANY WARRANTY; without even the implied warranty of
+.. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.. * GNU General Lesser Public License for more details.
+.. *
+.. * You should have received a copy of the GNU General Lesser Public
+.. * License along with this program. If not, see
+.. * <http://www.gnu.org/licenses/lgpl-3.0.html>.
+.. * #L%
+.. -
+
+Utilisation
+===========
+
+::
+
+ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+
+ <link rel="stylesheet" type="text/css" href="<c:url value="/nuiton-js/bootstrap.css"/>" />
+ <script language="javascript" type="text/javascript" src="<c:url value="/nuiton-js/bootstrap.js"/>"></script>
+
+JS disponible
+-------------
+
+- bootstrap.js
+
+CSS disponible
+--------------
+
+- bootstrap.css
+- bootstrap-responsive.css
Added: tags/nuiton-js-bootstrap-2.3.2-1/src/site/rst/index.rst~
===================================================================
--- tags/nuiton-js-bootstrap-2.3.2-1/src/site/rst/index.rst~ (rev 0)
+++ tags/nuiton-js-bootstrap-2.3.2-1/src/site/rst/index.rst~ 2013-06-17 15:44:23 UTC (rev 91)
@@ -0,0 +1,43 @@
+.. -
+.. * #%L
+.. * Nuiton JS :: Bootstrap
+.. * $Id:$
+.. * $HeadURL:$
+.. * %%
+.. * Copyright (C) 2012 - 2013 CodeLutin
+.. * %%
+.. * This program is free software: you can redistribute it and/or modify
+.. * it under the terms of the GNU Lesser General Public License as
+.. * published by the Free Software Foundation, either version 3 of the
+.. * License, or (at your option) any later version.
+.. *
+.. * This program is distributed in the hope that it will be useful,
+.. * but WITHOUT ANY WARRANTY; without even the implied warranty of
+.. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.. * GNU General Lesser Public License for more details.
+.. *
+.. * You should have received a copy of the GNU General Lesser Public
+.. * License along with this program. If not, see
+.. * <http://www.gnu.org/licenses/lgpl-3.0.html>.
+.. * #L%
+.. -
+
+Utilisation
+===========
+
+::
+
+ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+
+ <link rel="stylesheet" type="text/css" href="<c:url value="/nuiton-js/bootstrap.css"/>" />
+ <script language="javascript" type="text/javascript" src="<c:url value="/nuiton-js/bootstrap.js"/>"></script>
+
+JS disponible
+-------------
+
+- bootstrap.js
+
+CSS disponible
+--------------
+
+- bootstrap.css
Added: tags/nuiton-js-bootstrap-2.3.2-1/src/site/site_fr.xml
===================================================================
--- tags/nuiton-js-bootstrap-2.3.2-1/src/site/site_fr.xml (rev 0)
+++ tags/nuiton-js-bootstrap-2.3.2-1/src/site/site_fr.xml 2013-06-17 15:44:23 UTC (rev 91)
@@ -0,0 +1,72 @@
+<!--
+ #%L
+ Nuiton JS :: Bootstrap
+ $Id:$
+ $HeadURL:$
+ %%
+ Copyright (C) 2012 - 2013 CodeLutin
+ %%
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Lesser Public License for more details.
+
+ You should have received a copy of the GNU General Lesser Public
+ License along with this program. If not, see
+ <http://www.gnu.org/licenses/lgpl-3.0.html>.
+ #L%
+ -->
+<project name="${project.name}">
+
+ <bannerLeft>
+ <src alt="NuitonJS">logo.jpg</src>
+ <name>${project.name}</name>
+ <href>index.html</href>
+ </bannerLeft>
+
+ <body>
+
+ <head>
+ <script type="text/javascript"
+ src="http://maven-site.nuiton.org/public/js/mavenpom-site.js">
+ </script>
+
+ <link rel="stylesheet" type="text/css"
+ href="http://maven-site.nuiton.org/public/css/mavenpom-site.css"/>
+ </head>
+
+ <breadcrumbs>
+ <item name="${project.name}" href="${project.url}"/>
+ </breadcrumbs>
+
+ <menu ref="parent"/>
+
+ <menu name="Utilisateurs">
+ <item name="Accueil" href="index.html"/>
+ </menu>
+
+ <menu ref="reports"/>
+
+ <footer>
+
+ <div id='projectMetas'
+ projectversion='${project.version}'
+ platform='${project.platform}'
+ projectid='${project.projectId}'
+ scm='${project.scm.connection}'
+ scmwebeditorenabled='${project.scmwebeditorEnabled}'
+ scmwebeditorurl='${project.scmwebeditorUrl}'
+ siteSourcesType='${project.siteSourcesType}'
+ piwikEnabled='${project.piwikEnabled}'
+ piwikId='${project.piwikId}'>
+ </div>
+ </footer>
+
+ </body>
+
+</project>
1
0
Author: kmorin
Date: 2013-06-17 17:04:40 +0200 (Mon, 17 Jun 2013)
New Revision: 90
Url: http://nuiton.org/projects/nuiton-js/repository/revisions/90
Log:
add bootstrap 2.3.2
Added:
tags/nuiton-js-bootstrap-2.3.2-1/
tags/nuiton-js-bootstrap-2.3.2-1/LICENSE.txt
tags/nuiton-js-bootstrap-2.3.2-1/README.txt
tags/nuiton-js-bootstrap-2.3.2-1/changelog.txt
tags/nuiton-js-bootstrap-2.3.2-1/pom.xml
tags/nuiton-js-bootstrap-2.3.2-1/src/
tags/nuiton-js-bootstrap-2.3.2-1/src/main/
tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/
tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/META-INF/
tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/META-INF/nuiton-js/
tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/META-INF/nuiton-js/wro-bootstrap.xml
tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/nuiton-js-bootstrap/
tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/nuiton-js-bootstrap/css/
tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/nuiton-js-bootstrap/css/bootstrap-responsive.css
tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/nuiton-js-bootstrap/css/bootstrap.css
tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/nuiton-js-bootstrap/img/
tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/nuiton-js-bootstrap/img/glyphicons-halflings-white.png
tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/nuiton-js-bootstrap/img/glyphicons-halflings.png
tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/nuiton-js-bootstrap/js/
tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/nuiton-js-bootstrap/js/bootstrap.js
Added: tags/nuiton-js-bootstrap-2.3.2-1/LICENSE.txt
===================================================================
--- tags/nuiton-js-bootstrap-2.3.2-1/LICENSE.txt (rev 0)
+++ tags/nuiton-js-bootstrap-2.3.2-1/LICENSE.txt 2013-06-17 15:04:40 UTC (rev 90)
@@ -0,0 +1,166 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
+
Added: tags/nuiton-js-bootstrap-2.3.2-1/README.txt
===================================================================
--- tags/nuiton-js-bootstrap-2.3.2-1/README.txt (rev 0)
+++ tags/nuiton-js-bootstrap-2.3.2-1/README.txt 2013-06-17 15:04:40 UTC (rev 90)
@@ -0,0 +1 @@
+
Added: tags/nuiton-js-bootstrap-2.3.2-1/changelog.txt
===================================================================
--- tags/nuiton-js-bootstrap-2.3.2-1/changelog.txt (rev 0)
+++ tags/nuiton-js-bootstrap-2.3.2-1/changelog.txt 2013-06-17 15:04:40 UTC (rev 90)
@@ -0,0 +1,2 @@
+Please refer to the release note :
+ http://maven-site.nuiton.org/nuiton-js/changes-report.html
Added: tags/nuiton-js-bootstrap-2.3.2-1/pom.xml
===================================================================
--- tags/nuiton-js-bootstrap-2.3.2-1/pom.xml (rev 0)
+++ tags/nuiton-js-bootstrap-2.3.2-1/pom.xml 2013-06-17 15:04:40 UTC (rev 90)
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <!-- ************************************************************* -->
+ <!-- *** POM Relationships *************************************** -->
+ <!-- ************************************************************* -->
+
+ <parent>
+ <groupId>org.nuiton.js</groupId>
+ <artifactId>nuiton-js-lib</artifactId>
+ <version>1.0</version>
+ </parent>
+
+ <artifactId>nuiton-js-bootstrap</artifactId>
+ <version>2.3.2-1</version>
+
+ <name>Nuiton JS :: Bootstrap</name>
+ <description>bootstrap packaging</description>
+
+ <url>http://twitter.github.com/bootstrap/</url>
+
+ <scm>
+ <connection>scm:svn:http://svn.nuiton.org/svn/nuiton-js/tags/nuiton-js-bootstrap-2.3.2-1</connection>
+ <developerConnection>scm:svn:http://svn.nuiton.org/svn/nuiton-js/tags/nuiton-js-bootstrap-2.3.2-1</developerConnection>
+ <url>http://www.nuiton.org/repositories/browse/nuiton-js/tags/nuiton-js-bootstra…</url>
+ </scm>
+
+ <licenses>
+ <license>
+ <name>Apache License, Version 2.0</name>
+ <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ </license>
+ </licenses>
+
+</project>
\ No newline at end of file
Added: tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/META-INF/nuiton-js/wro-bootstrap.xml
===================================================================
--- tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/META-INF/nuiton-js/wro-bootstrap.xml (rev 0)
+++ tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/META-INF/nuiton-js/wro-bootstrap.xml 2013-06-17 15:04:40 UTC (rev 90)
@@ -0,0 +1,13 @@
+<groups xmlns="http://www.isdc.ro/wro">
+
+ <group name='bootstrap'>
+ <css>classpath:nuiton-js-bootstrap/css/bootstrap.css</css>
+ <js>classpath:nuiton-js-bootstrap/js/bootstrap.js</js>
+ </group>
+
+ <group name='bootstrap-responsive'>
+ <group-ref>bootstrap</group-ref>
+ <css>classpath:nuiton-js-bootstrap/css/bootstrap-responsive.css</css>
+ </group>
+
+</groups>
\ No newline at end of file
Added: tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/nuiton-js-bootstrap/css/bootstrap-responsive.css
===================================================================
--- tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/nuiton-js-bootstrap/css/bootstrap-responsive.css (rev 0)
+++ tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/nuiton-js-bootstrap/css/bootstrap-responsive.css 2013-06-17 15:04:40 UTC (rev 90)
@@ -0,0 +1,1109 @@
+/*!
+ * Bootstrap Responsive v2.3.2
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */
+
+.clearfix {
+ *zoom: 1;
+}
+
+.clearfix:before,
+.clearfix:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.clearfix:after {
+ clear: both;
+}
+
+.hide-text {
+ font: 0/0 a;
+ color: transparent;
+ text-shadow: none;
+ background-color: transparent;
+ border: 0;
+}
+
+.input-block-level {
+ display: block;
+ width: 100%;
+ min-height: 30px;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+@-ms-viewport {
+ width: device-width;
+}
+
+.hidden {
+ display: none;
+ visibility: hidden;
+}
+
+.visible-phone {
+ display: none !important;
+}
+
+.visible-tablet {
+ display: none !important;
+}
+
+.hidden-desktop {
+ display: none !important;
+}
+
+.visible-desktop {
+ display: inherit !important;
+}
+
+@media (min-width: 768px) and (max-width: 979px) {
+ .hidden-desktop {
+ display: inherit !important;
+ }
+ .visible-desktop {
+ display: none !important ;
+ }
+ .visible-tablet {
+ display: inherit !important;
+ }
+ .hidden-tablet {
+ display: none !important;
+ }
+}
+
+@media (max-width: 767px) {
+ .hidden-desktop {
+ display: inherit !important;
+ }
+ .visible-desktop {
+ display: none !important;
+ }
+ .visible-phone {
+ display: inherit !important;
+ }
+ .hidden-phone {
+ display: none !important;
+ }
+}
+
+.visible-print {
+ display: none !important;
+}
+
+@media print {
+ .visible-print {
+ display: inherit !important;
+ }
+ .hidden-print {
+ display: none !important;
+ }
+}
+
+@media (min-width: 1200px) {
+ .row {
+ margin-left: -30px;
+ *zoom: 1;
+ }
+ .row:before,
+ .row:after {
+ display: table;
+ line-height: 0;
+ content: "";
+ }
+ .row:after {
+ clear: both;
+ }
+ [class*="span"] {
+ float: left;
+ min-height: 1px;
+ margin-left: 30px;
+ }
+ .container,
+ .navbar-static-top .container,
+ .navbar-fixed-top .container,
+ .navbar-fixed-bottom .container {
+ width: 1170px;
+ }
+ .span12 {
+ width: 1170px;
+ }
+ .span11 {
+ width: 1070px;
+ }
+ .span10 {
+ width: 970px;
+ }
+ .span9 {
+ width: 870px;
+ }
+ .span8 {
+ width: 770px;
+ }
+ .span7 {
+ width: 670px;
+ }
+ .span6 {
+ width: 570px;
+ }
+ .span5 {
+ width: 470px;
+ }
+ .span4 {
+ width: 370px;
+ }
+ .span3 {
+ width: 270px;
+ }
+ .span2 {
+ width: 170px;
+ }
+ .span1 {
+ width: 70px;
+ }
+ .offset12 {
+ margin-left: 1230px;
+ }
+ .offset11 {
+ margin-left: 1130px;
+ }
+ .offset10 {
+ margin-left: 1030px;
+ }
+ .offset9 {
+ margin-left: 930px;
+ }
+ .offset8 {
+ margin-left: 830px;
+ }
+ .offset7 {
+ margin-left: 730px;
+ }
+ .offset6 {
+ margin-left: 630px;
+ }
+ .offset5 {
+ margin-left: 530px;
+ }
+ .offset4 {
+ margin-left: 430px;
+ }
+ .offset3 {
+ margin-left: 330px;
+ }
+ .offset2 {
+ margin-left: 230px;
+ }
+ .offset1 {
+ margin-left: 130px;
+ }
+ .row-fluid {
+ width: 100%;
+ *zoom: 1;
+ }
+ .row-fluid:before,
+ .row-fluid:after {
+ display: table;
+ line-height: 0;
+ content: "";
+ }
+ .row-fluid:after {
+ clear: both;
+ }
+ .row-fluid [class*="span"] {
+ display: block;
+ float: left;
+ width: 100%;
+ min-height: 30px;
+ margin-left: 2.564102564102564%;
+ *margin-left: 2.5109110747408616%;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ }
+ .row-fluid [class*="span"]:first-child {
+ margin-left: 0;
+ }
+ .row-fluid .controls-row [class*="span"] + [class*="span"] {
+ margin-left: 2.564102564102564%;
+ }
+ .row-fluid .span12 {
+ width: 100%;
+ *width: 99.94680851063829%;
+ }
+ .row-fluid .span11 {
+ width: 91.45299145299145%;
+ *width: 91.39979996362975%;
+ }
+ .row-fluid .span10 {
+ width: 82.90598290598291%;
+ *width: 82.8527914166212%;
+ }
+ .row-fluid .span9 {
+ width: 74.35897435897436%;
+ *width: 74.30578286961266%;
+ }
+ .row-fluid .span8 {
+ width: 65.81196581196582%;
+ *width: 65.75877432260411%;
+ }
+ .row-fluid .span7 {
+ width: 57.26495726495726%;
+ *width: 57.21176577559556%;
+ }
+ .row-fluid .span6 {
+ width: 48.717948717948715%;
+ *width: 48.664757228587014%;
+ }
+ .row-fluid .span5 {
+ width: 40.17094017094017%;
+ *width: 40.11774868157847%;
+ }
+ .row-fluid .span4 {
+ width: 31.623931623931625%;
+ *width: 31.570740134569924%;
+ }
+ .row-fluid .span3 {
+ width: 23.076923076923077%;
+ *width: 23.023731587561375%;
+ }
+ .row-fluid .span2 {
+ width: 14.52991452991453%;
+ *width: 14.476723040552828%;
+ }
+ .row-fluid .span1 {
+ width: 5.982905982905983%;
+ *width: 5.929714493544281%;
+ }
+ .row-fluid .offset12 {
+ margin-left: 105.12820512820512%;
+ *margin-left: 105.02182214948171%;
+ }
+ .row-fluid .offset12:first-child {
+ margin-left: 102.56410256410257%;
+ *margin-left: 102.45771958537915%;
+ }
+ .row-fluid .offset11 {
+ margin-left: 96.58119658119658%;
+ *margin-left: 96.47481360247316%;
+ }
+ .row-fluid .offset11:first-child {
+ margin-left: 94.01709401709402%;
+ *margin-left: 93.91071103837061%;
+ }
+ .row-fluid .offset10 {
+ margin-left: 88.03418803418803%;
+ *margin-left: 87.92780505546462%;
+ }
+ .row-fluid .offset10:first-child {
+ margin-left: 85.47008547008548%;
+ *margin-left: 85.36370249136206%;
+ }
+ .row-fluid .offset9 {
+ margin-left: 79.48717948717949%;
+ *margin-left: 79.38079650845607%;
+ }
+ .row-fluid .offset9:first-child {
+ margin-left: 76.92307692307693%;
+ *margin-left: 76.81669394435352%;
+ }
+ .row-fluid .offset8 {
+ margin-left: 70.94017094017094%;
+ *margin-left: 70.83378796144753%;
+ }
+ .row-fluid .offset8:first-child {
+ margin-left: 68.37606837606839%;
+ *margin-left: 68.26968539734497%;
+ }
+ .row-fluid .offset7 {
+ margin-left: 62.393162393162385%;
+ *margin-left: 62.28677941443899%;
+ }
+ .row-fluid .offset7:first-child {
+ margin-left: 59.82905982905982%;
+ *margin-left: 59.72267685033642%;
+ }
+ .row-fluid .offset6 {
+ margin-left: 53.84615384615384%;
+ *margin-left: 53.739770867430444%;
+ }
+ .row-fluid .offset6:first-child {
+ margin-left: 51.28205128205128%;
+ *margin-left: 51.175668303327875%;
+ }
+ .row-fluid .offset5 {
+ margin-left: 45.299145299145295%;
+ *margin-left: 45.1927623204219%;
+ }
+ .row-fluid .offset5:first-child {
+ margin-left: 42.73504273504273%;
+ *margin-left: 42.62865975631933%;
+ }
+ .row-fluid .offset4 {
+ margin-left: 36.75213675213675%;
+ *margin-left: 36.645753773413354%;
+ }
+ .row-fluid .offset4:first-child {
+ margin-left: 34.18803418803419%;
+ *margin-left: 34.081651209310785%;
+ }
+ .row-fluid .offset3 {
+ margin-left: 28.205128205128204%;
+ *margin-left: 28.0987452264048%;
+ }
+ .row-fluid .offset3:first-child {
+ margin-left: 25.641025641025642%;
+ *margin-left: 25.53464266230224%;
+ }
+ .row-fluid .offset2 {
+ margin-left: 19.65811965811966%;
+ *margin-left: 19.551736679396257%;
+ }
+ .row-fluid .offset2:first-child {
+ margin-left: 17.094017094017094%;
+ *margin-left: 16.98763411529369%;
+ }
+ .row-fluid .offset1 {
+ margin-left: 11.11111111111111%;
+ *margin-left: 11.004728132387708%;
+ }
+ .row-fluid .offset1:first-child {
+ margin-left: 8.547008547008547%;
+ *margin-left: 8.440625568285142%;
+ }
+ input,
+ textarea,
+ .uneditable-input {
+ margin-left: 0;
+ }
+ .controls-row [class*="span"] + [class*="span"] {
+ margin-left: 30px;
+ }
+ input.span12,
+ textarea.span12,
+ .uneditable-input.span12 {
+ width: 1156px;
+ }
+ input.span11,
+ textarea.span11,
+ .uneditable-input.span11 {
+ width: 1056px;
+ }
+ input.span10,
+ textarea.span10,
+ .uneditable-input.span10 {
+ width: 956px;
+ }
+ input.span9,
+ textarea.span9,
+ .uneditable-input.span9 {
+ width: 856px;
+ }
+ input.span8,
+ textarea.span8,
+ .uneditable-input.span8 {
+ width: 756px;
+ }
+ input.span7,
+ textarea.span7,
+ .uneditable-input.span7 {
+ width: 656px;
+ }
+ input.span6,
+ textarea.span6,
+ .uneditable-input.span6 {
+ width: 556px;
+ }
+ input.span5,
+ textarea.span5,
+ .uneditable-input.span5 {
+ width: 456px;
+ }
+ input.span4,
+ textarea.span4,
+ .uneditable-input.span4 {
+ width: 356px;
+ }
+ input.span3,
+ textarea.span3,
+ .uneditable-input.span3 {
+ width: 256px;
+ }
+ input.span2,
+ textarea.span2,
+ .uneditable-input.span2 {
+ width: 156px;
+ }
+ input.span1,
+ textarea.span1,
+ .uneditable-input.span1 {
+ width: 56px;
+ }
+ .thumbnails {
+ margin-left: -30px;
+ }
+ .thumbnails > li {
+ margin-left: 30px;
+ }
+ .row-fluid .thumbnails {
+ margin-left: 0;
+ }
+}
+
+@media (min-width: 768px) and (max-width: 979px) {
+ .row {
+ margin-left: -20px;
+ *zoom: 1;
+ }
+ .row:before,
+ .row:after {
+ display: table;
+ line-height: 0;
+ content: "";
+ }
+ .row:after {
+ clear: both;
+ }
+ [class*="span"] {
+ float: left;
+ min-height: 1px;
+ margin-left: 20px;
+ }
+ .container,
+ .navbar-static-top .container,
+ .navbar-fixed-top .container,
+ .navbar-fixed-bottom .container {
+ width: 724px;
+ }
+ .span12 {
+ width: 724px;
+ }
+ .span11 {
+ width: 662px;
+ }
+ .span10 {
+ width: 600px;
+ }
+ .span9 {
+ width: 538px;
+ }
+ .span8 {
+ width: 476px;
+ }
+ .span7 {
+ width: 414px;
+ }
+ .span6 {
+ width: 352px;
+ }
+ .span5 {
+ width: 290px;
+ }
+ .span4 {
+ width: 228px;
+ }
+ .span3 {
+ width: 166px;
+ }
+ .span2 {
+ width: 104px;
+ }
+ .span1 {
+ width: 42px;
+ }
+ .offset12 {
+ margin-left: 764px;
+ }
+ .offset11 {
+ margin-left: 702px;
+ }
+ .offset10 {
+ margin-left: 640px;
+ }
+ .offset9 {
+ margin-left: 578px;
+ }
+ .offset8 {
+ margin-left: 516px;
+ }
+ .offset7 {
+ margin-left: 454px;
+ }
+ .offset6 {
+ margin-left: 392px;
+ }
+ .offset5 {
+ margin-left: 330px;
+ }
+ .offset4 {
+ margin-left: 268px;
+ }
+ .offset3 {
+ margin-left: 206px;
+ }
+ .offset2 {
+ margin-left: 144px;
+ }
+ .offset1 {
+ margin-left: 82px;
+ }
+ .row-fluid {
+ width: 100%;
+ *zoom: 1;
+ }
+ .row-fluid:before,
+ .row-fluid:after {
+ display: table;
+ line-height: 0;
+ content: "";
+ }
+ .row-fluid:after {
+ clear: both;
+ }
+ .row-fluid [class*="span"] {
+ display: block;
+ float: left;
+ width: 100%;
+ min-height: 30px;
+ margin-left: 2.7624309392265194%;
+ *margin-left: 2.709239449864817%;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ }
+ .row-fluid [class*="span"]:first-child {
+ margin-left: 0;
+ }
+ .row-fluid .controls-row [class*="span"] + [class*="span"] {
+ margin-left: 2.7624309392265194%;
+ }
+ .row-fluid .span12 {
+ width: 100%;
+ *width: 99.94680851063829%;
+ }
+ .row-fluid .span11 {
+ width: 91.43646408839778%;
+ *width: 91.38327259903608%;
+ }
+ .row-fluid .span10 {
+ width: 82.87292817679558%;
+ *width: 82.81973668743387%;
+ }
+ .row-fluid .span9 {
+ width: 74.30939226519337%;
+ *width: 74.25620077583166%;
+ }
+ .row-fluid .span8 {
+ width: 65.74585635359117%;
+ *width: 65.69266486422946%;
+ }
+ .row-fluid .span7 {
+ width: 57.18232044198895%;
+ *width: 57.12912895262725%;
+ }
+ .row-fluid .span6 {
+ width: 48.61878453038674%;
+ *width: 48.56559304102504%;
+ }
+ .row-fluid .span5 {
+ width: 40.05524861878453%;
+ *width: 40.00205712942283%;
+ }
+ .row-fluid .span4 {
+ width: 31.491712707182323%;
+ *width: 31.43852121782062%;
+ }
+ .row-fluid .span3 {
+ width: 22.92817679558011%;
+ *width: 22.87498530621841%;
+ }
+ .row-fluid .span2 {
+ width: 14.3646408839779%;
+ *width: 14.311449394616199%;
+ }
+ .row-fluid .span1 {
+ width: 5.801104972375691%;
+ *width: 5.747913483013988%;
+ }
+ .row-fluid .offset12 {
+ margin-left: 105.52486187845304%;
+ *margin-left: 105.41847889972962%;
+ }
+ .row-fluid .offset12:first-child {
+ margin-left: 102.76243093922652%;
+ *margin-left: 102.6560479605031%;
+ }
+ .row-fluid .offset11 {
+ margin-left: 96.96132596685082%;
+ *margin-left: 96.8549429881274%;
+ }
+ .row-fluid .offset11:first-child {
+ margin-left: 94.1988950276243%;
+ *margin-left: 94.09251204890089%;
+ }
+ .row-fluid .offset10 {
+ margin-left: 88.39779005524862%;
+ *margin-left: 88.2914070765252%;
+ }
+ .row-fluid .offset10:first-child {
+ margin-left: 85.6353591160221%;
+ *margin-left: 85.52897613729868%;
+ }
+ .row-fluid .offset9 {
+ margin-left: 79.8342541436464%;
+ *margin-left: 79.72787116492299%;
+ }
+ .row-fluid .offset9:first-child {
+ margin-left: 77.07182320441989%;
+ *margin-left: 76.96544022569647%;
+ }
+ .row-fluid .offset8 {
+ margin-left: 71.2707182320442%;
+ *margin-left: 71.16433525332079%;
+ }
+ .row-fluid .offset8:first-child {
+ margin-left: 68.50828729281768%;
+ *margin-left: 68.40190431409427%;
+ }
+ .row-fluid .offset7 {
+ margin-left: 62.70718232044199%;
+ *margin-left: 62.600799341718584%;
+ }
+ .row-fluid .offset7:first-child {
+ margin-left: 59.94475138121547%;
+ *margin-left: 59.838368402492065%;
+ }
+ .row-fluid .offset6 {
+ margin-left: 54.14364640883978%;
+ *margin-left: 54.037263430116376%;
+ }
+ .row-fluid .offset6:first-child {
+ margin-left: 51.38121546961326%;
+ *margin-left: 51.27483249088986%;
+ }
+ .row-fluid .offset5 {
+ margin-left: 45.58011049723757%;
+ *margin-left: 45.47372751851417%;
+ }
+ .row-fluid .offset5:first-child {
+ margin-left: 42.81767955801105%;
+ *margin-left: 42.71129657928765%;
+ }
+ .row-fluid .offset4 {
+ margin-left: 37.01657458563536%;
+ *margin-left: 36.91019160691196%;
+ }
+ .row-fluid .offset4:first-child {
+ margin-left: 34.25414364640884%;
+ *margin-left: 34.14776066768544%;
+ }
+ .row-fluid .offset3 {
+ margin-left: 28.45303867403315%;
+ *margin-left: 28.346655695309746%;
+ }
+ .row-fluid .offset3:first-child {
+ margin-left: 25.69060773480663%;
+ *margin-left: 25.584224756083227%;
+ }
+ .row-fluid .offset2 {
+ margin-left: 19.88950276243094%;
+ *margin-left: 19.783119783707537%;
+ }
+ .row-fluid .offset2:first-child {
+ margin-left: 17.12707182320442%;
+ *margin-left: 17.02068884448102%;
+ }
+ .row-fluid .offset1 {
+ margin-left: 11.32596685082873%;
+ *margin-left: 11.219583872105325%;
+ }
+ .row-fluid .offset1:first-child {
+ margin-left: 8.56353591160221%;
+ *margin-left: 8.457152932878806%;
+ }
+ input,
+ textarea,
+ .uneditable-input {
+ margin-left: 0;
+ }
+ .controls-row [class*="span"] + [class*="span"] {
+ margin-left: 20px;
+ }
+ input.span12,
+ textarea.span12,
+ .uneditable-input.span12 {
+ width: 710px;
+ }
+ input.span11,
+ textarea.span11,
+ .uneditable-input.span11 {
+ width: 648px;
+ }
+ input.span10,
+ textarea.span10,
+ .uneditable-input.span10 {
+ width: 586px;
+ }
+ input.span9,
+ textarea.span9,
+ .uneditable-input.span9 {
+ width: 524px;
+ }
+ input.span8,
+ textarea.span8,
+ .uneditable-input.span8 {
+ width: 462px;
+ }
+ input.span7,
+ textarea.span7,
+ .uneditable-input.span7 {
+ width: 400px;
+ }
+ input.span6,
+ textarea.span6,
+ .uneditable-input.span6 {
+ width: 338px;
+ }
+ input.span5,
+ textarea.span5,
+ .uneditable-input.span5 {
+ width: 276px;
+ }
+ input.span4,
+ textarea.span4,
+ .uneditable-input.span4 {
+ width: 214px;
+ }
+ input.span3,
+ textarea.span3,
+ .uneditable-input.span3 {
+ width: 152px;
+ }
+ input.span2,
+ textarea.span2,
+ .uneditable-input.span2 {
+ width: 90px;
+ }
+ input.span1,
+ textarea.span1,
+ .uneditable-input.span1 {
+ width: 28px;
+ }
+}
+
+@media (max-width: 767px) {
+ body {
+ padding-right: 20px;
+ padding-left: 20px;
+ }
+ .navbar-fixed-top,
+ .navbar-fixed-bottom,
+ .navbar-static-top {
+ margin-right: -20px;
+ margin-left: -20px;
+ }
+ .container-fluid {
+ padding: 0;
+ }
+ .dl-horizontal dt {
+ float: none;
+ width: auto;
+ clear: none;
+ text-align: left;
+ }
+ .dl-horizontal dd {
+ margin-left: 0;
+ }
+ .container {
+ width: auto;
+ }
+ .row-fluid {
+ width: 100%;
+ }
+ .row,
+ .thumbnails {
+ margin-left: 0;
+ }
+ .thumbnails > li {
+ float: none;
+ margin-left: 0;
+ }
+ [class*="span"],
+ .uneditable-input[class*="span"],
+ .row-fluid [class*="span"] {
+ display: block;
+ float: none;
+ width: 100%;
+ margin-left: 0;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ }
+ .span12,
+ .row-fluid .span12 {
+ width: 100%;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ }
+ .row-fluid [class*="offset"]:first-child {
+ margin-left: 0;
+ }
+ .input-large,
+ .input-xlarge,
+ .input-xxlarge,
+ input[class*="span"],
+ select[class*="span"],
+ textarea[class*="span"],
+ .uneditable-input {
+ display: block;
+ width: 100%;
+ min-height: 30px;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ }
+ .input-prepend input,
+ .input-append input,
+ .input-prepend input[class*="span"],
+ .input-append input[class*="span"] {
+ display: inline-block;
+ width: auto;
+ }
+ .controls-row [class*="span"] + [class*="span"] {
+ margin-left: 0;
+ }
+ .modal {
+ position: fixed;
+ top: 20px;
+ right: 20px;
+ left: 20px;
+ width: auto;
+ margin: 0;
+ }
+ .modal.fade {
+ top: -100px;
+ }
+ .modal.fade.in {
+ top: 20px;
+ }
+}
+
+@media (max-width: 480px) {
+ .nav-collapse {
+ -webkit-transform: translate3d(0, 0, 0);
+ }
+ .page-header h1 small {
+ display: block;
+ line-height: 20px;
+ }
+ input[type="checkbox"],
+ input[type="radio"] {
+ border: 1px solid #ccc;
+ }
+ .form-horizontal .control-label {
+ float: none;
+ width: auto;
+ padding-top: 0;
+ text-align: left;
+ }
+ .form-horizontal .controls {
+ margin-left: 0;
+ }
+ .form-horizontal .control-list {
+ padding-top: 0;
+ }
+ .form-horizontal .form-actions {
+ padding-right: 10px;
+ padding-left: 10px;
+ }
+ .media .pull-left,
+ .media .pull-right {
+ display: block;
+ float: none;
+ margin-bottom: 10px;
+ }
+ .media-object {
+ margin-right: 0;
+ margin-left: 0;
+ }
+ .modal {
+ top: 10px;
+ right: 10px;
+ left: 10px;
+ }
+ .modal-header .close {
+ padding: 10px;
+ margin: -10px;
+ }
+ .carousel-caption {
+ position: static;
+ }
+}
+
+@media (max-width: 979px) {
+ body {
+ padding-top: 0;
+ }
+ .navbar-fixed-top,
+ .navbar-fixed-bottom {
+ position: static;
+ }
+ .navbar-fixed-top {
+ margin-bottom: 20px;
+ }
+ .navbar-fixed-bottom {
+ margin-top: 20px;
+ }
+ .navbar-fixed-top .navbar-inner,
+ .navbar-fixed-bottom .navbar-inner {
+ padding: 5px;
+ }
+ .navbar .container {
+ width: auto;
+ padding: 0;
+ }
+ .navbar .brand {
+ padding-right: 10px;
+ padding-left: 10px;
+ margin: 0 0 0 -5px;
+ }
+ .nav-collapse {
+ clear: both;
+ }
+ .nav-collapse .nav {
+ float: none;
+ margin: 0 0 10px;
+ }
+ .nav-collapse .nav > li {
+ float: none;
+ }
+ .nav-collapse .nav > li > a {
+ margin-bottom: 2px;
+ }
+ .nav-collapse .nav > .divider-vertical {
+ display: none;
+ }
+ .nav-collapse .nav .nav-header {
+ color: #777777;
+ text-shadow: none;
+ }
+ .nav-collapse .nav > li > a,
+ .nav-collapse .dropdown-menu a {
+ padding: 9px 15px;
+ font-weight: bold;
+ color: #777777;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+ }
+ .nav-collapse .btn {
+ padding: 4px 10px 4px;
+ font-weight: normal;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ }
+ .nav-collapse .dropdown-menu li + li a {
+ margin-bottom: 2px;
+ }
+ .nav-collapse .nav > li > a:hover,
+ .nav-collapse .nav > li > a:focus,
+ .nav-collapse .dropdown-menu a:hover,
+ .nav-collapse .dropdown-menu a:focus {
+ background-color: #f2f2f2;
+ }
+ .navbar-inverse .nav-collapse .nav > li > a,
+ .navbar-inverse .nav-collapse .dropdown-menu a {
+ color: #999999;
+ }
+ .navbar-inverse .nav-collapse .nav > li > a:hover,
+ .navbar-inverse .nav-collapse .nav > li > a:focus,
+ .navbar-inverse .nav-collapse .dropdown-menu a:hover,
+ .navbar-inverse .nav-collapse .dropdown-menu a:focus {
+ background-color: #111111;
+ }
+ .nav-collapse.in .btn-group {
+ padding: 0;
+ margin-top: 5px;
+ }
+ .nav-collapse .dropdown-menu {
+ position: static;
+ top: auto;
+ left: auto;
+ display: none;
+ float: none;
+ max-width: none;
+ padding: 0;
+ margin: 0 15px;
+ background-color: transparent;
+ border: none;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+ }
+ .nav-collapse .open > .dropdown-menu {
+ display: block;
+ }
+ .nav-collapse .dropdown-menu:before,
+ .nav-collapse .dropdown-menu:after {
+ display: none;
+ }
+ .nav-collapse .dropdown-menu .divider {
+ display: none;
+ }
+ .nav-collapse .nav > li > .dropdown-menu:before,
+ .nav-collapse .nav > li > .dropdown-menu:after {
+ display: none;
+ }
+ .nav-collapse .navbar-form,
+ .nav-collapse .navbar-search {
+ float: none;
+ padding: 10px 15px;
+ margin: 10px 0;
+ border-top: 1px solid #f2f2f2;
+ border-bottom: 1px solid #f2f2f2;
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
+ -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
+ }
+ .navbar-inverse .nav-collapse .navbar-form,
+ .navbar-inverse .nav-collapse .navbar-search {
+ border-top-color: #111111;
+ border-bottom-color: #111111;
+ }
+ .navbar .nav-collapse .nav.pull-right {
+ float: none;
+ margin-left: 0;
+ }
+ .nav-collapse,
+ .nav-collapse.collapse {
+ height: 0;
+ overflow: hidden;
+ }
+ .navbar .btn-navbar {
+ display: block;
+ }
+ .navbar-static .navbar-inner {
+ padding-right: 10px;
+ padding-left: 10px;
+ }
+}
+
+@media (min-width: 980px) {
+ .nav-collapse.collapse {
+ height: auto !important;
+ overflow: visible !important;
+ }
+}
Added: tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/nuiton-js-bootstrap/css/bootstrap.css
===================================================================
--- tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/nuiton-js-bootstrap/css/bootstrap.css (rev 0)
+++ tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/nuiton-js-bootstrap/css/bootstrap.css 2013-06-17 15:04:40 UTC (rev 90)
@@ -0,0 +1,6315 @@
+/*!
+ * Bootstrap v2.3.2
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */
+.clearfix {
+ *zoom: 1;
+}
+.clearfix:before,
+.clearfix:after {
+ display: table;
+ content: "";
+ line-height: 0;
+}
+.clearfix:after {
+ clear: both;
+}
+.hide-text {
+ font: 0/0 a;
+ color: transparent;
+ text-shadow: none;
+ background-color: transparent;
+ border: 0;
+}
+.input-block-level {
+ display: block;
+ width: 100%;
+ min-height: 30px;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+nav,
+section {
+ display: block;
+}
+audio,
+canvas,
+video {
+ display: inline-block;
+ *display: inline;
+ *zoom: 1;
+}
+audio:not([controls]) {
+ display: none;
+}
+html {
+ font-size: 100%;
+ -webkit-text-size-adjust: 100%;
+ -ms-text-size-adjust: 100%;
+}
+a:focus {
+ outline: thin dotted #333;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+a:hover,
+a:active {
+ outline: 0;
+}
+sub,
+sup {
+ position: relative;
+ font-size: 75%;
+ line-height: 0;
+ vertical-align: baseline;
+}
+sup {
+ top: -0.5em;
+}
+sub {
+ bottom: -0.25em;
+}
+img {
+ /* Responsive images (ensure images don't scale beyond their parents) */
+
+ max-width: 100%;
+ /* Part 1: Set a maxium relative to the parent */
+
+ width: auto\9;
+ /* IE7-8 need help adjusting responsive images */
+
+ height: auto;
+ /* Part 2: Scale the height according to the width, otherwise you get stretching */
+
+ vertical-align: middle;
+ border: 0;
+ -ms-interpolation-mode: bicubic;
+}
+#map_canvas img,
+.google-maps img {
+ max-width: none;
+}
+button,
+input,
+select,
+textarea {
+ margin: 0;
+ font-size: 100%;
+ vertical-align: middle;
+}
+button,
+input {
+ *overflow: visible;
+ line-height: normal;
+}
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+ padding: 0;
+ border: 0;
+}
+button,
+html input[type="button"],
+input[type="reset"],
+input[type="submit"] {
+ -webkit-appearance: button;
+ cursor: pointer;
+}
+label,
+select,
+button,
+input[type="button"],
+input[type="reset"],
+input[type="submit"],
+input[type="radio"],
+input[type="checkbox"] {
+ cursor: pointer;
+}
+input[type="search"] {
+ -webkit-box-sizing: content-box;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+ -webkit-appearance: textfield;
+}
+input[type="search"]::-webkit-search-decoration,
+input[type="search"]::-webkit-search-cancel-button {
+ -webkit-appearance: none;
+}
+textarea {
+ overflow: auto;
+ vertical-align: top;
+}
+@media print {
+ * {
+ text-shadow: none !important;
+ color: #000 !important;
+ background: transparent !important;
+ box-shadow: none !important;
+ }
+ a,
+ a:visited {
+ text-decoration: underline;
+ }
+ a[href]:after {
+ content: " (" attr(href) ")";
+ }
+ abbr[title]:after {
+ content: " (" attr(title) ")";
+ }
+ .ir a:after,
+ a[href^="javascript:"]:after,
+ a[href^="#"]:after {
+ content: "";
+ }
+ pre,
+ blockquote {
+ border: 1px solid #999;
+ page-break-inside: avoid;
+ }
+ thead {
+ display: table-header-group;
+ }
+ tr,
+ img {
+ page-break-inside: avoid;
+ }
+ img {
+ max-width: 100% !important;
+ }
+ @page {
+ margin: 0.5cm;
+ }
+ p,
+ h2,
+ h3 {
+ orphans: 3;
+ widows: 3;
+ }
+ h2,
+ h3 {
+ page-break-after: avoid;
+ }
+}
+body {
+ margin: 0;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-size: 14px;
+ line-height: 20px;
+ color: #333333;
+ background-color: #ffffff;
+}
+a {
+ color: #0088cc;
+ text-decoration: none;
+}
+a:hover,
+a:focus {
+ color: #005580;
+ text-decoration: underline;
+}
+.img-rounded {
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+}
+.img-polaroid {
+ padding: 4px;
+ background-color: #fff;
+ border: 1px solid #ccc;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+ -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+}
+.img-circle {
+ -webkit-border-radius: 500px;
+ -moz-border-radius: 500px;
+ border-radius: 500px;
+}
+.row {
+ margin-left: -20px;
+ *zoom: 1;
+}
+.row:before,
+.row:after {
+ display: table;
+ content: "";
+ line-height: 0;
+}
+.row:after {
+ clear: both;
+}
+[class*="span"] {
+ float: left;
+ min-height: 1px;
+ margin-left: 20px;
+}
+.container,
+.navbar-static-top .container,
+.navbar-fixed-top .container,
+.navbar-fixed-bottom .container {
+ width: 940px;
+}
+.span12 {
+ width: 940px;
+}
+.span11 {
+ width: 860px;
+}
+.span10 {
+ width: 780px;
+}
+.span9 {
+ width: 700px;
+}
+.span8 {
+ width: 620px;
+}
+.span7 {
+ width: 540px;
+}
+.span6 {
+ width: 460px;
+}
+.span5 {
+ width: 380px;
+}
+.span4 {
+ width: 300px;
+}
+.span3 {
+ width: 220px;
+}
+.span2 {
+ width: 140px;
+}
+.span1 {
+ width: 60px;
+}
+.offset12 {
+ margin-left: 980px;
+}
+.offset11 {
+ margin-left: 900px;
+}
+.offset10 {
+ margin-left: 820px;
+}
+.offset9 {
+ margin-left: 740px;
+}
+.offset8 {
+ margin-left: 660px;
+}
+.offset7 {
+ margin-left: 580px;
+}
+.offset6 {
+ margin-left: 500px;
+}
+.offset5 {
+ margin-left: 420px;
+}
+.offset4 {
+ margin-left: 340px;
+}
+.offset3 {
+ margin-left: 260px;
+}
+.offset2 {
+ margin-left: 180px;
+}
+.offset1 {
+ margin-left: 100px;
+}
+.row-fluid {
+ width: 100%;
+ *zoom: 1;
+}
+.row-fluid:before,
+.row-fluid:after {
+ display: table;
+ content: "";
+ line-height: 0;
+}
+.row-fluid:after {
+ clear: both;
+}
+.row-fluid [class*="span"] {
+ display: block;
+ width: 100%;
+ min-height: 30px;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ float: left;
+ margin-left: 2.127659574468085%;
+ *margin-left: 2.074468085106383%;
+}
+.row-fluid [class*="span"]:first-child {
+ margin-left: 0;
+}
+.row-fluid .controls-row [class*="span"] + [class*="span"] {
+ margin-left: 2.127659574468085%;
+}
+.row-fluid .span12 {
+ width: 100%;
+ *width: 99.94680851063829%;
+}
+.row-fluid .span11 {
+ width: 91.48936170212765%;
+ *width: 91.43617021276594%;
+}
+.row-fluid .span10 {
+ width: 82.97872340425532%;
+ *width: 82.92553191489361%;
+}
+.row-fluid .span9 {
+ width: 74.46808510638297%;
+ *width: 74.41489361702126%;
+}
+.row-fluid .span8 {
+ width: 65.95744680851064%;
+ *width: 65.90425531914893%;
+}
+.row-fluid .span7 {
+ width: 57.44680851063829%;
+ *width: 57.39361702127659%;
+}
+.row-fluid .span6 {
+ width: 48.93617021276595%;
+ *width: 48.88297872340425%;
+}
+.row-fluid .span5 {
+ width: 40.42553191489362%;
+ *width: 40.37234042553192%;
+}
+.row-fluid .span4 {
+ width: 31.914893617021278%;
+ *width: 31.861702127659576%;
+}
+.row-fluid .span3 {
+ width: 23.404255319148934%;
+ *width: 23.351063829787233%;
+}
+.row-fluid .span2 {
+ width: 14.893617021276595%;
+ *width: 14.840425531914894%;
+}
+.row-fluid .span1 {
+ width: 6.382978723404255%;
+ *width: 6.329787234042553%;
+}
+.row-fluid .offset12 {
+ margin-left: 104.25531914893617%;
+ *margin-left: 104.14893617021275%;
+}
+.row-fluid .offset12:first-child {
+ margin-left: 102.12765957446808%;
+ *margin-left: 102.02127659574467%;
+}
+.row-fluid .offset11 {
+ margin-left: 95.74468085106382%;
+ *margin-left: 95.6382978723404%;
+}
+.row-fluid .offset11:first-child {
+ margin-left: 93.61702127659574%;
+ *margin-left: 93.51063829787232%;
+}
+.row-fluid .offset10 {
+ margin-left: 87.23404255319149%;
+ *margin-left: 87.12765957446807%;
+}
+.row-fluid .offset10:first-child {
+ margin-left: 85.1063829787234%;
+ *margin-left: 84.99999999999999%;
+}
+.row-fluid .offset9 {
+ margin-left: 78.72340425531914%;
+ *margin-left: 78.61702127659572%;
+}
+.row-fluid .offset9:first-child {
+ margin-left: 76.59574468085106%;
+ *margin-left: 76.48936170212764%;
+}
+.row-fluid .offset8 {
+ margin-left: 70.2127659574468%;
+ *margin-left: 70.10638297872339%;
+}
+.row-fluid .offset8:first-child {
+ margin-left: 68.08510638297872%;
+ *margin-left: 67.9787234042553%;
+}
+.row-fluid .offset7 {
+ margin-left: 61.70212765957446%;
+ *margin-left: 61.59574468085106%;
+}
+.row-fluid .offset7:first-child {
+ margin-left: 59.574468085106375%;
+ *margin-left: 59.46808510638297%;
+}
+.row-fluid .offset6 {
+ margin-left: 53.191489361702125%;
+ *margin-left: 53.085106382978715%;
+}
+.row-fluid .offset6:first-child {
+ margin-left: 51.063829787234035%;
+ *margin-left: 50.95744680851063%;
+}
+.row-fluid .offset5 {
+ margin-left: 44.68085106382979%;
+ *margin-left: 44.57446808510638%;
+}
+.row-fluid .offset5:first-child {
+ margin-left: 42.5531914893617%;
+ *margin-left: 42.4468085106383%;
+}
+.row-fluid .offset4 {
+ margin-left: 36.170212765957444%;
+ *margin-left: 36.06382978723405%;
+}
+.row-fluid .offset4:first-child {
+ margin-left: 34.04255319148936%;
+ *margin-left: 33.93617021276596%;
+}
+.row-fluid .offset3 {
+ margin-left: 27.659574468085104%;
+ *margin-left: 27.5531914893617%;
+}
+.row-fluid .offset3:first-child {
+ margin-left: 25.53191489361702%;
+ *margin-left: 25.425531914893618%;
+}
+.row-fluid .offset2 {
+ margin-left: 19.148936170212764%;
+ *margin-left: 19.04255319148936%;
+}
+.row-fluid .offset2:first-child {
+ margin-left: 17.02127659574468%;
+ *margin-left: 16.914893617021278%;
+}
+.row-fluid .offset1 {
+ margin-left: 10.638297872340425%;
+ *margin-left: 10.53191489361702%;
+}
+.row-fluid .offset1:first-child {
+ margin-left: 8.51063829787234%;
+ *margin-left: 8.404255319148938%;
+}
+[class*="span"].hide,
+.row-fluid [class*="span"].hide {
+ display: none;
+}
+[class*="span"].pull-right,
+.row-fluid [class*="span"].pull-right {
+ float: right;
+}
+.container {
+ margin-right: auto;
+ margin-left: auto;
+ *zoom: 1;
+}
+.container:before,
+.container:after {
+ display: table;
+ content: "";
+ line-height: 0;
+}
+.container:after {
+ clear: both;
+}
+.container-fluid {
+ padding-right: 20px;
+ padding-left: 20px;
+ *zoom: 1;
+}
+.container-fluid:before,
+.container-fluid:after {
+ display: table;
+ content: "";
+ line-height: 0;
+}
+.container-fluid:after {
+ clear: both;
+}
+p {
+ margin: 0 0 10px;
+}
+.lead {
+ margin-bottom: 20px;
+ font-size: 21px;
+ font-weight: 200;
+ line-height: 30px;
+}
+small {
+ font-size: 85%;
+}
+strong {
+ font-weight: bold;
+}
+em {
+ font-style: italic;
+}
+cite {
+ font-style: normal;
+}
+.muted {
+ color: #999999;
+}
+a.muted:hover,
+a.muted:focus {
+ color: #808080;
+}
+.text-warning {
+ color: #c09853;
+}
+a.text-warning:hover,
+a.text-warning:focus {
+ color: #a47e3c;
+}
+.text-error {
+ color: #b94a48;
+}
+a.text-error:hover,
+a.text-error:focus {
+ color: #953b39;
+}
+.text-info {
+ color: #3a87ad;
+}
+a.text-info:hover,
+a.text-info:focus {
+ color: #2d6987;
+}
+.text-success {
+ color: #468847;
+}
+a.text-success:hover,
+a.text-success:focus {
+ color: #356635;
+}
+.text-left {
+ text-align: left;
+}
+.text-right {
+ text-align: right;
+}
+.text-center {
+ text-align: center;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ margin: 10px 0;
+ font-family: inherit;
+ font-weight: bold;
+ line-height: 20px;
+ color: inherit;
+ text-rendering: optimizelegibility;
+}
+h1 small,
+h2 small,
+h3 small,
+h4 small,
+h5 small,
+h6 small {
+ font-weight: normal;
+ line-height: 1;
+ color: #999999;
+}
+h1,
+h2,
+h3 {
+ line-height: 40px;
+}
+h1 {
+ font-size: 38.5px;
+}
+h2 {
+ font-size: 31.5px;
+}
+h3 {
+ font-size: 24.5px;
+}
+h4 {
+ font-size: 17.5px;
+}
+h5 {
+ font-size: 14px;
+}
+h6 {
+ font-size: 11.9px;
+}
+h1 small {
+ font-size: 24.5px;
+}
+h2 small {
+ font-size: 17.5px;
+}
+h3 small {
+ font-size: 14px;
+}
+h4 small {
+ font-size: 14px;
+}
+.page-header {
+ padding-bottom: 9px;
+ margin: 20px 0 30px;
+ border-bottom: 1px solid #eeeeee;
+}
+ul,
+ol {
+ padding: 0;
+ margin: 0 0 10px 25px;
+}
+ul ul,
+ul ol,
+ol ol,
+ol ul {
+ margin-bottom: 0;
+}
+li {
+ line-height: 20px;
+}
+ul.unstyled,
+ol.unstyled {
+ margin-left: 0;
+ list-style: none;
+}
+ul.inline,
+ol.inline {
+ margin-left: 0;
+ list-style: none;
+}
+ul.inline > li,
+ol.inline > li {
+ display: inline-block;
+ *display: inline;
+ /* IE7 inline-block hack */
+
+ *zoom: 1;
+ padding-left: 5px;
+ padding-right: 5px;
+}
+dl {
+ margin-bottom: 20px;
+}
+dt,
+dd {
+ line-height: 20px;
+}
+dt {
+ font-weight: bold;
+}
+dd {
+ margin-left: 10px;
+}
+.dl-horizontal {
+ *zoom: 1;
+}
+.dl-horizontal:before,
+.dl-horizontal:after {
+ display: table;
+ content: "";
+ line-height: 0;
+}
+.dl-horizontal:after {
+ clear: both;
+}
+.dl-horizontal dt {
+ float: left;
+ width: 160px;
+ clear: left;
+ text-align: right;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+.dl-horizontal dd {
+ margin-left: 180px;
+}
+hr {
+ margin: 20px 0;
+ border: 0;
+ border-top: 1px solid #eeeeee;
+ border-bottom: 1px solid #ffffff;
+}
+abbr[title],
+abbr[data-original-title] {
+ cursor: help;
+ border-bottom: 1px dotted #999999;
+}
+abbr.initialism {
+ font-size: 90%;
+ text-transform: uppercase;
+}
+blockquote {
+ padding: 0 0 0 15px;
+ margin: 0 0 20px;
+ border-left: 5px solid #eeeeee;
+}
+blockquote p {
+ margin-bottom: 0;
+ font-size: 17.5px;
+ font-weight: 300;
+ line-height: 1.25;
+}
+blockquote small {
+ display: block;
+ line-height: 20px;
+ color: #999999;
+}
+blockquote small:before {
+ content: '\2014 \00A0';
+}
+blockquote.pull-right {
+ float: right;
+ padding-right: 15px;
+ padding-left: 0;
+ border-right: 5px solid #eeeeee;
+ border-left: 0;
+}
+blockquote.pull-right p,
+blockquote.pull-right small {
+ text-align: right;
+}
+blockquote.pull-right small:before {
+ content: '';
+}
+blockquote.pull-right small:after {
+ content: '\00A0 \2014';
+}
+q:before,
+q:after,
+blockquote:before,
+blockquote:after {
+ content: "";
+}
+address {
+ display: block;
+ margin-bottom: 20px;
+ font-style: normal;
+ line-height: 20px;
+}
+code,
+pre {
+ padding: 0 3px 2px;
+ font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
+ font-size: 12px;
+ color: #333333;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+}
+code {
+ padding: 2px 4px;
+ color: #d14;
+ background-color: #f7f7f9;
+ border: 1px solid #e1e1e8;
+ white-space: nowrap;
+}
+pre {
+ display: block;
+ padding: 9.5px;
+ margin: 0 0 10px;
+ font-size: 13px;
+ line-height: 20px;
+ word-break: break-all;
+ word-wrap: break-word;
+ white-space: pre;
+ white-space: pre-wrap;
+ background-color: #f5f5f5;
+ border: 1px solid #ccc;
+ border: 1px solid rgba(0, 0, 0, 0.15);
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+pre.prettyprint {
+ margin-bottom: 20px;
+}
+pre code {
+ padding: 0;
+ color: inherit;
+ white-space: pre;
+ white-space: pre-wrap;
+ background-color: transparent;
+ border: 0;
+}
+.pre-scrollable {
+ max-height: 340px;
+ overflow-y: scroll;
+}
+.label,
+.badge {
+ display: inline-block;
+ padding: 2px 4px;
+ font-size: 11.844px;
+ font-weight: bold;
+ line-height: 14px;
+ color: #ffffff;
+ vertical-align: baseline;
+ white-space: nowrap;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #999999;
+}
+.label {
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+}
+.badge {
+ padding-left: 9px;
+ padding-right: 9px;
+ -webkit-border-radius: 9px;
+ -moz-border-radius: 9px;
+ border-radius: 9px;
+}
+.label:empty,
+.badge:empty {
+ display: none;
+}
+a.label:hover,
+a.label:focus,
+a.badge:hover,
+a.badge:focus {
+ color: #ffffff;
+ text-decoration: none;
+ cursor: pointer;
+}
+.label-important,
+.badge-important {
+ background-color: #b94a48;
+}
+.label-important[href],
+.badge-important[href] {
+ background-color: #953b39;
+}
+.label-warning,
+.badge-warning {
+ background-color: #f89406;
+}
+.label-warning[href],
+.badge-warning[href] {
+ background-color: #c67605;
+}
+.label-success,
+.badge-success {
+ background-color: #468847;
+}
+.label-success[href],
+.badge-success[href] {
+ background-color: #356635;
+}
+.label-info,
+.badge-info {
+ background-color: #3a87ad;
+}
+.label-info[href],
+.badge-info[href] {
+ background-color: #2d6987;
+}
+.label-inverse,
+.badge-inverse {
+ background-color: #333333;
+}
+.label-inverse[href],
+.badge-inverse[href] {
+ background-color: #1a1a1a;
+}
+.btn .label,
+.btn .badge {
+ position: relative;
+ top: -1px;
+}
+.btn-mini .label,
+.btn-mini .badge {
+ top: 0;
+}
+table {
+ max-width: 100%;
+ background-color: transparent;
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+.table {
+ width: 100%;
+ margin-bottom: 20px;
+}
+.table th,
+.table td {
+ padding: 8px;
+ line-height: 20px;
+ text-align: left;
+ vertical-align: top;
+ border-top: 1px solid #dddddd;
+}
+.table th {
+ font-weight: bold;
+}
+.table thead th {
+ vertical-align: bottom;
+}
+.table caption + thead tr:first-child th,
+.table caption + thead tr:first-child td,
+.table colgroup + thead tr:first-child th,
+.table colgroup + thead tr:first-child td,
+.table thead:first-child tr:first-child th,
+.table thead:first-child tr:first-child td {
+ border-top: 0;
+}
+.table tbody + tbody {
+ border-top: 2px solid #dddddd;
+}
+.table .table {
+ background-color: #ffffff;
+}
+.table-condensed th,
+.table-condensed td {
+ padding: 4px 5px;
+}
+.table-bordered {
+ border: 1px solid #dddddd;
+ border-collapse: separate;
+ *border-collapse: collapse;
+ border-left: 0;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+.table-bordered th,
+.table-bordered td {
+ border-left: 1px solid #dddddd;
+}
+.table-bordered caption + thead tr:first-child th,
+.table-bordered caption + tbody tr:first-child th,
+.table-bordered caption + tbody tr:first-child td,
+.table-bordered colgroup + thead tr:first-child th,
+.table-bordered colgroup + tbody tr:first-child th,
+.table-bordered colgroup + tbody tr:first-child td,
+.table-bordered thead:first-child tr:first-child th,
+.table-bordered tbody:first-child tr:first-child th,
+.table-bordered tbody:first-child tr:first-child td {
+ border-top: 0;
+}
+.table-bordered thead:first-child tr:first-child > th:first-child,
+.table-bordered tbody:first-child tr:first-child > td:first-child,
+.table-bordered tbody:first-child tr:first-child > th:first-child {
+ -webkit-border-top-left-radius: 4px;
+ -moz-border-radius-topleft: 4px;
+ border-top-left-radius: 4px;
+}
+.table-bordered thead:first-child tr:first-child > th:last-child,
+.table-bordered tbody:first-child tr:first-child > td:last-child,
+.table-bordered tbody:first-child tr:first-child > th:last-child {
+ -webkit-border-top-right-radius: 4px;
+ -moz-border-radius-topright: 4px;
+ border-top-right-radius: 4px;
+}
+.table-bordered thead:last-child tr:last-child > th:first-child,
+.table-bordered tbody:last-child tr:last-child > td:first-child,
+.table-bordered tbody:last-child tr:last-child > th:first-child,
+.table-bordered tfoot:last-child tr:last-child > td:first-child,
+.table-bordered tfoot:last-child tr:last-child > th:first-child {
+ -webkit-border-bottom-left-radius: 4px;
+ -moz-border-radius-bottomleft: 4px;
+ border-bottom-left-radius: 4px;
+}
+.table-bordered thead:last-child tr:last-child > th:last-child,
+.table-bordered tbody:last-child tr:last-child > td:last-child,
+.table-bordered tbody:last-child tr:last-child > th:last-child,
+.table-bordered tfoot:last-child tr:last-child > td:last-child,
+.table-bordered tfoot:last-child tr:last-child > th:last-child {
+ -webkit-border-bottom-right-radius: 4px;
+ -moz-border-radius-bottomright: 4px;
+ border-bottom-right-radius: 4px;
+}
+.table-bordered tfoot + tbody:last-child tr:last-child td:first-child {
+ -webkit-border-bottom-left-radius: 0;
+ -moz-border-radius-bottomleft: 0;
+ border-bottom-left-radius: 0;
+}
+.table-bordered tfoot + tbody:last-child tr:last-child td:last-child {
+ -webkit-border-bottom-right-radius: 0;
+ -moz-border-radius-bottomright: 0;
+ border-bottom-right-radius: 0;
+}
+.table-bordered caption + thead tr:first-child th:first-child,
+.table-bordered caption + tbody tr:first-child td:first-child,
+.table-bordered colgroup + thead tr:first-child th:first-child,
+.table-bordered colgroup + tbody tr:first-child td:first-child {
+ -webkit-border-top-left-radius: 4px;
+ -moz-border-radius-topleft: 4px;
+ border-top-left-radius: 4px;
+}
+.table-bordered caption + thead tr:first-child th:last-child,
+.table-bordered caption + tbody tr:first-child td:last-child,
+.table-bordered colgroup + thead tr:first-child th:last-child,
+.table-bordered colgroup + tbody tr:first-child td:last-child {
+ -webkit-border-top-right-radius: 4px;
+ -moz-border-radius-topright: 4px;
+ border-top-right-radius: 4px;
+}
+.table-striped tbody > tr:nth-child(odd) > td,
+.table-striped tbody > tr:nth-child(odd) > th {
+ background-color: #f9f9f9;
+}
+.table-hover tbody tr:hover > td,
+.table-hover tbody tr:hover > th {
+ background-color: #f5f5f5;
+}
+table td[class*="span"],
+table th[class*="span"],
+.row-fluid table td[class*="span"],
+.row-fluid table th[class*="span"] {
+ display: table-cell;
+ float: none;
+ margin-left: 0;
+}
+.table td.span1,
+.table th.span1 {
+ float: none;
+ width: 44px;
+ margin-left: 0;
+}
+.table td.span2,
+.table th.span2 {
+ float: none;
+ width: 124px;
+ margin-left: 0;
+}
+.table td.span3,
+.table th.span3 {
+ float: none;
+ width: 204px;
+ margin-left: 0;
+}
+.table td.span4,
+.table th.span4 {
+ float: none;
+ width: 284px;
+ margin-left: 0;
+}
+.table td.span5,
+.table th.span5 {
+ float: none;
+ width: 364px;
+ margin-left: 0;
+}
+.table td.span6,
+.table th.span6 {
+ float: none;
+ width: 444px;
+ margin-left: 0;
+}
+.table td.span7,
+.table th.span7 {
+ float: none;
+ width: 524px;
+ margin-left: 0;
+}
+.table td.span8,
+.table th.span8 {
+ float: none;
+ width: 604px;
+ margin-left: 0;
+}
+.table td.span9,
+.table th.span9 {
+ float: none;
+ width: 684px;
+ margin-left: 0;
+}
+.table td.span10,
+.table th.span10 {
+ float: none;
+ width: 764px;
+ margin-left: 0;
+}
+.table td.span11,
+.table th.span11 {
+ float: none;
+ width: 844px;
+ margin-left: 0;
+}
+.table td.span12,
+.table th.span12 {
+ float: none;
+ width: 924px;
+ margin-left: 0;
+}
+.table tbody tr.success > td {
+ background-color: #dff0d8;
+}
+.table tbody tr.error > td {
+ background-color: #f2dede;
+}
+.table tbody tr.warning > td {
+ background-color: #fcf8e3;
+}
+.table tbody tr.info > td {
+ background-color: #d9edf7;
+}
+.table-hover tbody tr.success:hover > td {
+ background-color: #d0e9c6;
+}
+.table-hover tbody tr.error:hover > td {
+ background-color: #ebcccc;
+}
+.table-hover tbody tr.warning:hover > td {
+ background-color: #faf2cc;
+}
+.table-hover tbody tr.info:hover > td {
+ background-color: #c4e3f3;
+}
+form {
+ margin: 0 0 20px;
+}
+fieldset {
+ padding: 0;
+ margin: 0;
+ border: 0;
+}
+legend {
+ display: block;
+ width: 100%;
+ padding: 0;
+ margin-bottom: 20px;
+ font-size: 21px;
+ line-height: 40px;
+ color: #333333;
+ border: 0;
+ border-bottom: 1px solid #e5e5e5;
+}
+legend small {
+ font-size: 15px;
+ color: #999999;
+}
+label,
+input,
+button,
+select,
+textarea {
+ font-size: 14px;
+ font-weight: normal;
+ line-height: 20px;
+}
+input,
+button,
+select,
+textarea {
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+}
+label {
+ display: block;
+ margin-bottom: 5px;
+}
+select,
+textarea,
+input[type="text"],
+input[type="password"],
+input[type="datetime"],
+input[type="datetime-local"],
+input[type="date"],
+input[type="month"],
+input[type="time"],
+input[type="week"],
+input[type="number"],
+input[type="email"],
+input[type="url"],
+input[type="search"],
+input[type="tel"],
+input[type="color"],
+.uneditable-input {
+ display: inline-block;
+ height: 20px;
+ padding: 4px 6px;
+ margin-bottom: 10px;
+ font-size: 14px;
+ line-height: 20px;
+ color: #555555;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ vertical-align: middle;
+}
+input,
+textarea,
+.uneditable-input {
+ width: 206px;
+}
+textarea {
+ height: auto;
+}
+textarea,
+input[type="text"],
+input[type="password"],
+input[type="datetime"],
+input[type="datetime-local"],
+input[type="date"],
+input[type="month"],
+input[type="time"],
+input[type="week"],
+input[type="number"],
+input[type="email"],
+input[type="url"],
+input[type="search"],
+input[type="tel"],
+input[type="color"],
+.uneditable-input {
+ background-color: #ffffff;
+ border: 1px solid #cccccc;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -webkit-transition: border linear .2s, box-shadow linear .2s;
+ -moz-transition: border linear .2s, box-shadow linear .2s;
+ -o-transition: border linear .2s, box-shadow linear .2s;
+ transition: border linear .2s, box-shadow linear .2s;
+}
+textarea:focus,
+input[type="text"]:focus,
+input[type="password"]:focus,
+input[type="datetime"]:focus,
+input[type="datetime-local"]:focus,
+input[type="date"]:focus,
+input[type="month"]:focus,
+input[type="time"]:focus,
+input[type="week"]:focus,
+input[type="number"]:focus,
+input[type="email"]:focus,
+input[type="url"]:focus,
+input[type="search"]:focus,
+input[type="tel"]:focus,
+input[type="color"]:focus,
+.uneditable-input:focus {
+ border-color: rgba(82, 168, 236, 0.8);
+ outline: 0;
+ outline: thin dotted \9;
+ /* IE6-9 */
+
+ -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6);
+ -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6);
+ box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6);
+}
+input[type="radio"],
+input[type="checkbox"] {
+ margin: 4px 0 0;
+ *margin-top: 0;
+ /* IE7 */
+
+ margin-top: 1px \9;
+ /* IE8-9 */
+
+ line-height: normal;
+}
+input[type="file"],
+input[type="image"],
+input[type="submit"],
+input[type="reset"],
+input[type="button"],
+input[type="radio"],
+input[type="checkbox"] {
+ width: auto;
+}
+select,
+input[type="file"] {
+ height: 30px;
+ /* In IE7, the height of the select element cannot be changed by height, only font-size */
+
+ *margin-top: 4px;
+ /* For IE7, add top margin to align select with labels */
+
+ line-height: 30px;
+}
+select {
+ width: 220px;
+ border: 1px solid #cccccc;
+ background-color: #ffffff;
+}
+select[multiple],
+select[size] {
+ height: auto;
+}
+select:focus,
+input[type="file"]:focus,
+input[type="radio"]:focus,
+input[type="checkbox"]:focus {
+ outline: thin dotted #333;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+.uneditable-input,
+.uneditable-textarea {
+ color: #999999;
+ background-color: #fcfcfc;
+ border-color: #cccccc;
+ -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
+ -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
+ cursor: not-allowed;
+}
+.uneditable-input {
+ overflow: hidden;
+ white-space: nowrap;
+}
+.uneditable-textarea {
+ width: auto;
+ height: auto;
+}
+input:-moz-placeholder,
+textarea:-moz-placeholder {
+ color: #999999;
+}
+input:-ms-input-placeholder,
+textarea:-ms-input-placeholder {
+ color: #999999;
+}
+input::-webkit-input-placeholder,
+textarea::-webkit-input-placeholder {
+ color: #999999;
+}
+.radio,
+.checkbox {
+ min-height: 20px;
+ padding-left: 20px;
+}
+.radio input[type="radio"],
+.checkbox input[type="checkbox"] {
+ float: left;
+ margin-left: -20px;
+}
+.controls > .radio:first-child,
+.controls > .checkbox:first-child {
+ padding-top: 5px;
+}
+.radio.inline,
+.checkbox.inline {
+ display: inline-block;
+ padding-top: 5px;
+ margin-bottom: 0;
+ vertical-align: middle;
+}
+.radio.inline + .radio.inline,
+.checkbox.inline + .checkbox.inline {
+ margin-left: 10px;
+}
+.input-mini {
+ width: 60px;
+}
+.input-small {
+ width: 90px;
+}
+.input-medium {
+ width: 150px;
+}
+.input-large {
+ width: 210px;
+}
+.input-xlarge {
+ width: 270px;
+}
+.input-xxlarge {
+ width: 530px;
+}
+input[class*="span"],
+select[class*="span"],
+textarea[class*="span"],
+.uneditable-input[class*="span"],
+.row-fluid input[class*="span"],
+.row-fluid select[class*="span"],
+.row-fluid textarea[class*="span"],
+.row-fluid .uneditable-input[class*="span"] {
+ float: none;
+ margin-left: 0;
+}
+.input-append input[class*="span"],
+.input-append .uneditable-input[class*="span"],
+.input-prepend input[class*="span"],
+.input-prepend .uneditable-input[class*="span"],
+.row-fluid input[class*="span"],
+.row-fluid select[class*="span"],
+.row-fluid textarea[class*="span"],
+.row-fluid .uneditable-input[class*="span"],
+.row-fluid .input-prepend [class*="span"],
+.row-fluid .input-append [class*="span"] {
+ display: inline-block;
+}
+input,
+textarea,
+.uneditable-input {
+ margin-left: 0;
+}
+.controls-row [class*="span"] + [class*="span"] {
+ margin-left: 20px;
+}
+input.span12,
+textarea.span12,
+.uneditable-input.span12 {
+ width: 926px;
+}
+input.span11,
+textarea.span11,
+.uneditable-input.span11 {
+ width: 846px;
+}
+input.span10,
+textarea.span10,
+.uneditable-input.span10 {
+ width: 766px;
+}
+input.span9,
+textarea.span9,
+.uneditable-input.span9 {
+ width: 686px;
+}
+input.span8,
+textarea.span8,
+.uneditable-input.span8 {
+ width: 606px;
+}
+input.span7,
+textarea.span7,
+.uneditable-input.span7 {
+ width: 526px;
+}
+input.span6,
+textarea.span6,
+.uneditable-input.span6 {
+ width: 446px;
+}
+input.span5,
+textarea.span5,
+.uneditable-input.span5 {
+ width: 366px;
+}
+input.span4,
+textarea.span4,
+.uneditable-input.span4 {
+ width: 286px;
+}
+input.span3,
+textarea.span3,
+.uneditable-input.span3 {
+ width: 206px;
+}
+input.span2,
+textarea.span2,
+.uneditable-input.span2 {
+ width: 126px;
+}
+input.span1,
+textarea.span1,
+.uneditable-input.span1 {
+ width: 46px;
+}
+.controls-row {
+ *zoom: 1;
+}
+.controls-row:before,
+.controls-row:after {
+ display: table;
+ content: "";
+ line-height: 0;
+}
+.controls-row:after {
+ clear: both;
+}
+.controls-row [class*="span"],
+.row-fluid .controls-row [class*="span"] {
+ float: left;
+}
+.controls-row .checkbox[class*="span"],
+.controls-row .radio[class*="span"] {
+ padding-top: 5px;
+}
+input[disabled],
+select[disabled],
+textarea[disabled],
+input[readonly],
+select[readonly],
+textarea[readonly] {
+ cursor: not-allowed;
+ background-color: #eeeeee;
+}
+input[type="radio"][disabled],
+input[type="checkbox"][disabled],
+input[type="radio"][readonly],
+input[type="checkbox"][readonly] {
+ background-color: transparent;
+}
+.control-group.warning .control-label,
+.control-group.warning .help-block,
+.control-group.warning .help-inline {
+ color: #c09853;
+}
+.control-group.warning .checkbox,
+.control-group.warning .radio,
+.control-group.warning input,
+.control-group.warning select,
+.control-group.warning textarea {
+ color: #c09853;
+}
+.control-group.warning input,
+.control-group.warning select,
+.control-group.warning textarea {
+ border-color: #c09853;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+.control-group.warning input:focus,
+.control-group.warning select:focus,
+.control-group.warning textarea:focus {
+ border-color: #a47e3c;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;
+}
+.control-group.warning .input-prepend .add-on,
+.control-group.warning .input-append .add-on {
+ color: #c09853;
+ background-color: #fcf8e3;
+ border-color: #c09853;
+}
+.control-group.error .control-label,
+.control-group.error .help-block,
+.control-group.error .help-inline {
+ color: #b94a48;
+}
+.control-group.error .checkbox,
+.control-group.error .radio,
+.control-group.error input,
+.control-group.error select,
+.control-group.error textarea {
+ color: #b94a48;
+}
+.control-group.error input,
+.control-group.error select,
+.control-group.error textarea {
+ border-color: #b94a48;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+.control-group.error input:focus,
+.control-group.error select:focus,
+.control-group.error textarea:focus {
+ border-color: #953b39;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;
+}
+.control-group.error .input-prepend .add-on,
+.control-group.error .input-append .add-on {
+ color: #b94a48;
+ background-color: #f2dede;
+ border-color: #b94a48;
+}
+.control-group.success .control-label,
+.control-group.success .help-block,
+.control-group.success .help-inline {
+ color: #468847;
+}
+.control-group.success .checkbox,
+.control-group.success .radio,
+.control-group.success input,
+.control-group.success select,
+.control-group.success textarea {
+ color: #468847;
+}
+.control-group.success input,
+.control-group.success select,
+.control-group.success textarea {
+ border-color: #468847;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+.control-group.success input:focus,
+.control-group.success select:focus,
+.control-group.success textarea:focus {
+ border-color: #356635;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;
+}
+.control-group.success .input-prepend .add-on,
+.control-group.success .input-append .add-on {
+ color: #468847;
+ background-color: #dff0d8;
+ border-color: #468847;
+}
+.control-group.info .control-label,
+.control-group.info .help-block,
+.control-group.info .help-inline {
+ color: #3a87ad;
+}
+.control-group.info .checkbox,
+.control-group.info .radio,
+.control-group.info input,
+.control-group.info select,
+.control-group.info textarea {
+ color: #3a87ad;
+}
+.control-group.info input,
+.control-group.info select,
+.control-group.info textarea {
+ border-color: #3a87ad;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+.control-group.info input:focus,
+.control-group.info select:focus,
+.control-group.info textarea:focus {
+ border-color: #2d6987;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;
+}
+.control-group.info .input-prepend .add-on,
+.control-group.info .input-append .add-on {
+ color: #3a87ad;
+ background-color: #d9edf7;
+ border-color: #3a87ad;
+}
+input:focus:invalid,
+textarea:focus:invalid,
+select:focus:invalid {
+ color: #b94a48;
+ border-color: #ee5f5b;
+}
+input:focus:invalid:focus,
+textarea:focus:invalid:focus,
+select:focus:invalid:focus {
+ border-color: #e9322d;
+ -webkit-box-shadow: 0 0 6px #f8b9b7;
+ -moz-box-shadow: 0 0 6px #f8b9b7;
+ box-shadow: 0 0 6px #f8b9b7;
+}
+.form-actions {
+ padding: 19px 20px 20px;
+ margin-top: 20px;
+ margin-bottom: 20px;
+ background-color: #f5f5f5;
+ border-top: 1px solid #e5e5e5;
+ *zoom: 1;
+}
+.form-actions:before,
+.form-actions:after {
+ display: table;
+ content: "";
+ line-height: 0;
+}
+.form-actions:after {
+ clear: both;
+}
+.help-block,
+.help-inline {
+ color: #595959;
+}
+.help-block {
+ display: block;
+ margin-bottom: 10px;
+}
+.help-inline {
+ display: inline-block;
+ *display: inline;
+ /* IE7 inline-block hack */
+
+ *zoom: 1;
+ vertical-align: middle;
+ padding-left: 5px;
+}
+.input-append,
+.input-prepend {
+ display: inline-block;
+ margin-bottom: 10px;
+ vertical-align: middle;
+ font-size: 0;
+ white-space: nowrap;
+}
+.input-append input,
+.input-prepend input,
+.input-append select,
+.input-prepend select,
+.input-append .uneditable-input,
+.input-prepend .uneditable-input,
+.input-append .dropdown-menu,
+.input-prepend .dropdown-menu,
+.input-append .popover,
+.input-prepend .popover {
+ font-size: 14px;
+}
+.input-append input,
+.input-prepend input,
+.input-append select,
+.input-prepend select,
+.input-append .uneditable-input,
+.input-prepend .uneditable-input {
+ position: relative;
+ margin-bottom: 0;
+ *margin-left: 0;
+ vertical-align: top;
+ -webkit-border-radius: 0 4px 4px 0;
+ -moz-border-radius: 0 4px 4px 0;
+ border-radius: 0 4px 4px 0;
+}
+.input-append input:focus,
+.input-prepend input:focus,
+.input-append select:focus,
+.input-prepend select:focus,
+.input-append .uneditable-input:focus,
+.input-prepend .uneditable-input:focus {
+ z-index: 2;
+}
+.input-append .add-on,
+.input-prepend .add-on {
+ display: inline-block;
+ width: auto;
+ height: 20px;
+ min-width: 16px;
+ padding: 4px 5px;
+ font-size: 14px;
+ font-weight: normal;
+ line-height: 20px;
+ text-align: center;
+ text-shadow: 0 1px 0 #ffffff;
+ background-color: #eeeeee;
+ border: 1px solid #ccc;
+}
+.input-append .add-on,
+.input-prepend .add-on,
+.input-append .btn,
+.input-prepend .btn,
+.input-append .btn-group > .dropdown-toggle,
+.input-prepend .btn-group > .dropdown-toggle {
+ vertical-align: top;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+.input-append .active,
+.input-prepend .active {
+ background-color: #a9dba9;
+ border-color: #46a546;
+}
+.input-prepend .add-on,
+.input-prepend .btn {
+ margin-right: -1px;
+}
+.input-prepend .add-on:first-child,
+.input-prepend .btn:first-child {
+ -webkit-border-radius: 4px 0 0 4px;
+ -moz-border-radius: 4px 0 0 4px;
+ border-radius: 4px 0 0 4px;
+}
+.input-append input,
+.input-append select,
+.input-append .uneditable-input {
+ -webkit-border-radius: 4px 0 0 4px;
+ -moz-border-radius: 4px 0 0 4px;
+ border-radius: 4px 0 0 4px;
+}
+.input-append input + .btn-group .btn:last-child,
+.input-append select + .btn-group .btn:last-child,
+.input-append .uneditable-input + .btn-group .btn:last-child {
+ -webkit-border-radius: 0 4px 4px 0;
+ -moz-border-radius: 0 4px 4px 0;
+ border-radius: 0 4px 4px 0;
+}
+.input-append .add-on,
+.input-append .btn,
+.input-append .btn-group {
+ margin-left: -1px;
+}
+.input-append .add-on:last-child,
+.input-append .btn:last-child,
+.input-append .btn-group:last-child > .dropdown-toggle {
+ -webkit-border-radius: 0 4px 4px 0;
+ -moz-border-radius: 0 4px 4px 0;
+ border-radius: 0 4px 4px 0;
+}
+.input-prepend.input-append input,
+.input-prepend.input-append select,
+.input-prepend.input-append .uneditable-input {
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+.input-prepend.input-append input + .btn-group .btn,
+.input-prepend.input-append select + .btn-group .btn,
+.input-prepend.input-append .uneditable-input + .btn-group .btn {
+ -webkit-border-radius: 0 4px 4px 0;
+ -moz-border-radius: 0 4px 4px 0;
+ border-radius: 0 4px 4px 0;
+}
+.input-prepend.input-append .add-on:first-child,
+.input-prepend.input-append .btn:first-child {
+ margin-right: -1px;
+ -webkit-border-radius: 4px 0 0 4px;
+ -moz-border-radius: 4px 0 0 4px;
+ border-radius: 4px 0 0 4px;
+}
+.input-prepend.input-append .add-on:last-child,
+.input-prepend.input-append .btn:last-child {
+ margin-left: -1px;
+ -webkit-border-radius: 0 4px 4px 0;
+ -moz-border-radius: 0 4px 4px 0;
+ border-radius: 0 4px 4px 0;
+}
+.input-prepend.input-append .btn-group:first-child {
+ margin-left: 0;
+}
+input.search-query {
+ padding-right: 14px;
+ padding-right: 4px \9;
+ padding-left: 14px;
+ padding-left: 4px \9;
+ /* IE7-8 doesn't have border-radius, so don't indent the padding */
+
+ margin-bottom: 0;
+ -webkit-border-radius: 15px;
+ -moz-border-radius: 15px;
+ border-radius: 15px;
+}
+/* Allow for input prepend/append in search forms */
+.form-search .input-append .search-query,
+.form-search .input-prepend .search-query {
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+.form-search .input-append .search-query {
+ -webkit-border-radius: 14px 0 0 14px;
+ -moz-border-radius: 14px 0 0 14px;
+ border-radius: 14px 0 0 14px;
+}
+.form-search .input-append .btn {
+ -webkit-border-radius: 0 14px 14px 0;
+ -moz-border-radius: 0 14px 14px 0;
+ border-radius: 0 14px 14px 0;
+}
+.form-search .input-prepend .search-query {
+ -webkit-border-radius: 0 14px 14px 0;
+ -moz-border-radius: 0 14px 14px 0;
+ border-radius: 0 14px 14px 0;
+}
+.form-search .input-prepend .btn {
+ -webkit-border-radius: 14px 0 0 14px;
+ -moz-border-radius: 14px 0 0 14px;
+ border-radius: 14px 0 0 14px;
+}
+.form-search input,
+.form-inline input,
+.form-horizontal input,
+.form-search textarea,
+.form-inline textarea,
+.form-horizontal textarea,
+.form-search select,
+.form-inline select,
+.form-horizontal select,
+.form-search .help-inline,
+.form-inline .help-inline,
+.form-horizontal .help-inline,
+.form-search .uneditable-input,
+.form-inline .uneditable-input,
+.form-horizontal .uneditable-input,
+.form-search .input-prepend,
+.form-inline .input-prepend,
+.form-horizontal .input-prepend,
+.form-search .input-append,
+.form-inline .input-append,
+.form-horizontal .input-append {
+ display: inline-block;
+ *display: inline;
+ /* IE7 inline-block hack */
+
+ *zoom: 1;
+ margin-bottom: 0;
+ vertical-align: middle;
+}
+.form-search .hide,
+.form-inline .hide,
+.form-horizontal .hide {
+ display: none;
+}
+.form-search label,
+.form-inline label,
+.form-search .btn-group,
+.form-inline .btn-group {
+ display: inline-block;
+}
+.form-search .input-append,
+.form-inline .input-append,
+.form-search .input-prepend,
+.form-inline .input-prepend {
+ margin-bottom: 0;
+}
+.form-search .radio,
+.form-search .checkbox,
+.form-inline .radio,
+.form-inline .checkbox {
+ padding-left: 0;
+ margin-bottom: 0;
+ vertical-align: middle;
+}
+.form-search .radio input[type="radio"],
+.form-search .checkbox input[type="checkbox"],
+.form-inline .radio input[type="radio"],
+.form-inline .checkbox input[type="checkbox"] {
+ float: left;
+ margin-right: 3px;
+ margin-left: 0;
+}
+.control-group {
+ margin-bottom: 10px;
+}
+legend + .control-group {
+ margin-top: 20px;
+ -webkit-margin-top-collapse: separate;
+}
+.form-horizontal .control-group {
+ margin-bottom: 20px;
+ *zoom: 1;
+}
+.form-horizontal .control-group:before,
+.form-horizontal .control-group:after {
+ display: table;
+ content: "";
+ line-height: 0;
+}
+.form-horizontal .control-group:after {
+ clear: both;
+}
+.form-horizontal .control-label {
+ float: left;
+ width: 160px;
+ padding-top: 5px;
+ text-align: right;
+}
+.form-horizontal .controls {
+ *display: inline-block;
+ *padding-left: 20px;
+ margin-left: 180px;
+ *margin-left: 0;
+}
+.form-horizontal .controls:first-child {
+ *padding-left: 180px;
+}
+.form-horizontal .help-block {
+ margin-bottom: 0;
+}
+.form-horizontal input + .help-block,
+.form-horizontal select + .help-block,
+.form-horizontal textarea + .help-block,
+.form-horizontal .uneditable-input + .help-block,
+.form-horizontal .input-prepend + .help-block,
+.form-horizontal .input-append + .help-block {
+ margin-top: 10px;
+}
+.form-horizontal .form-actions {
+ padding-left: 180px;
+}
+.btn {
+ display: inline-block;
+ *display: inline;
+ /* IE7 inline-block hack */
+
+ *zoom: 1;
+ padding: 4px 12px;
+ margin-bottom: 0;
+ font-size: 14px;
+ line-height: 20px;
+ text-align: center;
+ vertical-align: middle;
+ cursor: pointer;
+ color: #333333;
+ text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
+ background-color: #f5f5f5;
+ background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));
+ background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6);
+ background-image: -o-linear-gradient(top, #ffffff, #e6e6e6);
+ background-image: linear-gradient(to bottom, #ffffff, #e6e6e6);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0);
+ border-color: #e6e6e6 #e6e6e6 #bfbfbf;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ *background-color: #e6e6e6;
+ /* Darken IE7 buttons by default so they stand out more given they won't have borders */
+
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+ border: 1px solid #cccccc;
+ *border: 0;
+ border-bottom-color: #b3b3b3;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ *margin-left: .3em;
+ -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
+ -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
+ box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
+}
+.btn:hover,
+.btn:focus,
+.btn:active,
+.btn.active,
+.btn.disabled,
+.btn[disabled] {
+ color: #333333;
+ background-color: #e6e6e6;
+ *background-color: #d9d9d9;
+}
+.btn:active,
+.btn.active {
+ background-color: #cccccc \9;
+}
+.btn:first-child {
+ *margin-left: 0;
+}
+.btn:hover,
+.btn:focus {
+ color: #333333;
+ text-decoration: none;
+ background-position: 0 -15px;
+ -webkit-transition: background-position 0.1s linear;
+ -moz-transition: background-position 0.1s linear;
+ -o-transition: background-position 0.1s linear;
+ transition: background-position 0.1s linear;
+}
+.btn:focus {
+ outline: thin dotted #333;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+.btn.active,
+.btn:active {
+ background-image: none;
+ outline: 0;
+ -webkit-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);
+ -moz-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);
+ box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);
+}
+.btn.disabled,
+.btn[disabled] {
+ cursor: default;
+ background-image: none;
+ opacity: 0.65;
+ filter: alpha(opacity=65);
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+}
+.btn-large {
+ padding: 11px 19px;
+ font-size: 17.5px;
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+}
+.btn-large [class^="icon-"],
+.btn-large [class*=" icon-"] {
+ margin-top: 4px;
+}
+.btn-small {
+ padding: 2px 10px;
+ font-size: 11.9px;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+}
+.btn-small [class^="icon-"],
+.btn-small [class*=" icon-"] {
+ margin-top: 0;
+}
+.btn-mini [class^="icon-"],
+.btn-mini [class*=" icon-"] {
+ margin-top: -1px;
+}
+.btn-mini {
+ padding: 0 6px;
+ font-size: 10.5px;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+}
+.btn-block {
+ display: block;
+ width: 100%;
+ padding-left: 0;
+ padding-right: 0;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+.btn-block + .btn-block {
+ margin-top: 5px;
+}
+input[type="submit"].btn-block,
+input[type="reset"].btn-block,
+input[type="button"].btn-block {
+ width: 100%;
+}
+.btn-primary.active,
+.btn-warning.active,
+.btn-danger.active,
+.btn-success.active,
+.btn-info.active,
+.btn-inverse.active {
+ color: rgba(255, 255, 255, 0.75);
+}
+.btn-primary {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #006dcc;
+ background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
+ background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
+ background-image: -o-linear-gradient(top, #0088cc, #0044cc);
+ background-image: linear-gradient(to bottom, #0088cc, #0044cc);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0);
+ border-color: #0044cc #0044cc #002a80;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ *background-color: #0044cc;
+ /* Darken IE7 buttons by default so they stand out more given they won't have borders */
+
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+}
+.btn-primary:hover,
+.btn-primary:focus,
+.btn-primary:active,
+.btn-primary.active,
+.btn-primary.disabled,
+.btn-primary[disabled] {
+ color: #ffffff;
+ background-color: #0044cc;
+ *background-color: #003bb3;
+}
+.btn-primary:active,
+.btn-primary.active {
+ background-color: #003399 \9;
+}
+.btn-warning {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #faa732;
+ background-image: -moz-linear-gradient(top, #fbb450, #f89406);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));
+ background-image: -webkit-linear-gradient(top, #fbb450, #f89406);
+ background-image: -o-linear-gradient(top, #fbb450, #f89406);
+ background-image: linear-gradient(to bottom, #fbb450, #f89406);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0);
+ border-color: #f89406 #f89406 #ad6704;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ *background-color: #f89406;
+ /* Darken IE7 buttons by default so they stand out more given they won't have borders */
+
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+}
+.btn-warning:hover,
+.btn-warning:focus,
+.btn-warning:active,
+.btn-warning.active,
+.btn-warning.disabled,
+.btn-warning[disabled] {
+ color: #ffffff;
+ background-color: #f89406;
+ *background-color: #df8505;
+}
+.btn-warning:active,
+.btn-warning.active {
+ background-color: #c67605 \9;
+}
+.btn-danger {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #da4f49;
+ background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));
+ background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f);
+ background-image: -o-linear-gradient(top, #ee5f5b, #bd362f);
+ background-image: linear-gradient(to bottom, #ee5f5b, #bd362f);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffbd362f', GradientType=0);
+ border-color: #bd362f #bd362f #802420;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ *background-color: #bd362f;
+ /* Darken IE7 buttons by default so they stand out more given they won't have borders */
+
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+}
+.btn-danger:hover,
+.btn-danger:focus,
+.btn-danger:active,
+.btn-danger.active,
+.btn-danger.disabled,
+.btn-danger[disabled] {
+ color: #ffffff;
+ background-color: #bd362f;
+ *background-color: #a9302a;
+}
+.btn-danger:active,
+.btn-danger.active {
+ background-color: #942a25 \9;
+}
+.btn-success {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #5bb75b;
+ background-image: -moz-linear-gradient(top, #62c462, #51a351);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));
+ background-image: -webkit-linear-gradient(top, #62c462, #51a351);
+ background-image: -o-linear-gradient(top, #62c462, #51a351);
+ background-image: linear-gradient(to bottom, #62c462, #51a351);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff51a351', GradientType=0);
+ border-color: #51a351 #51a351 #387038;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ *background-color: #51a351;
+ /* Darken IE7 buttons by default so they stand out more given they won't have borders */
+
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+}
+.btn-success:hover,
+.btn-success:focus,
+.btn-success:active,
+.btn-success.active,
+.btn-success.disabled,
+.btn-success[disabled] {
+ color: #ffffff;
+ background-color: #51a351;
+ *background-color: #499249;
+}
+.btn-success:active,
+.btn-success.active {
+ background-color: #408140 \9;
+}
+.btn-info {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #49afcd;
+ background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));
+ background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4);
+ background-image: -o-linear-gradient(top, #5bc0de, #2f96b4);
+ background-image: linear-gradient(to bottom, #5bc0de, #2f96b4);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2f96b4', GradientType=0);
+ border-color: #2f96b4 #2f96b4 #1f6377;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ *background-color: #2f96b4;
+ /* Darken IE7 buttons by default so they stand out more given they won't have borders */
+
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+}
+.btn-info:hover,
+.btn-info:focus,
+.btn-info:active,
+.btn-info.active,
+.btn-info.disabled,
+.btn-info[disabled] {
+ color: #ffffff;
+ background-color: #2f96b4;
+ *background-color: #2a85a0;
+}
+.btn-info:active,
+.btn-info.active {
+ background-color: #24748c \9;
+}
+.btn-inverse {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #363636;
+ background-image: -moz-linear-gradient(top, #444444, #222222);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#222222));
+ background-image: -webkit-linear-gradient(top, #444444, #222222);
+ background-image: -o-linear-gradient(top, #444444, #222222);
+ background-image: linear-gradient(to bottom, #444444, #222222);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff222222', GradientType=0);
+ border-color: #222222 #222222 #000000;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ *background-color: #222222;
+ /* Darken IE7 buttons by default so they stand out more given they won't have borders */
+
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+}
+.btn-inverse:hover,
+.btn-inverse:focus,
+.btn-inverse:active,
+.btn-inverse.active,
+.btn-inverse.disabled,
+.btn-inverse[disabled] {
+ color: #ffffff;
+ background-color: #222222;
+ *background-color: #151515;
+}
+.btn-inverse:active,
+.btn-inverse.active {
+ background-color: #080808 \9;
+}
+button.btn,
+input[type="submit"].btn {
+ *padding-top: 3px;
+ *padding-bottom: 3px;
+}
+button.btn::-moz-focus-inner,
+input[type="submit"].btn::-moz-focus-inner {
+ padding: 0;
+ border: 0;
+}
+button.btn.btn-large,
+input[type="submit"].btn.btn-large {
+ *padding-top: 7px;
+ *padding-bottom: 7px;
+}
+button.btn.btn-small,
+input[type="submit"].btn.btn-small {
+ *padding-top: 3px;
+ *padding-bottom: 3px;
+}
+button.btn.btn-mini,
+input[type="submit"].btn.btn-mini {
+ *padding-top: 1px;
+ *padding-bottom: 1px;
+}
+.btn-link,
+.btn-link:active,
+.btn-link[disabled] {
+ background-color: transparent;
+ background-image: none;
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+}
+.btn-link {
+ border-color: transparent;
+ cursor: pointer;
+ color: #0088cc;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+.btn-link:hover,
+.btn-link:focus {
+ color: #005580;
+ text-decoration: underline;
+ background-color: transparent;
+}
+.btn-link[disabled]:hover,
+.btn-link[disabled]:focus {
+ color: #333333;
+ text-decoration: none;
+}
+[class^="icon-"],
+[class*=" icon-"] {
+ display: inline-block;
+ width: 14px;
+ height: 14px;
+ *margin-right: .3em;
+ line-height: 14px;
+ vertical-align: text-top;
+ background-image: url("../img/glyphicons-halflings.png");
+ background-position: 14px 14px;
+ background-repeat: no-repeat;
+ margin-top: 1px;
+}
+/* White icons with optional class, or on hover/focus/active states of certain elements */
+.icon-white,
+.nav-pills > .active > a > [class^="icon-"],
+.nav-pills > .active > a > [class*=" icon-"],
+.nav-list > .active > a > [class^="icon-"],
+.nav-list > .active > a > [class*=" icon-"],
+.navbar-inverse .nav > .active > a > [class^="icon-"],
+.navbar-inverse .nav > .active > a > [class*=" icon-"],
+.dropdown-menu > li > a:hover > [class^="icon-"],
+.dropdown-menu > li > a:focus > [class^="icon-"],
+.dropdown-menu > li > a:hover > [class*=" icon-"],
+.dropdown-menu > li > a:focus > [class*=" icon-"],
+.dropdown-menu > .active > a > [class^="icon-"],
+.dropdown-menu > .active > a > [class*=" icon-"],
+.dropdown-submenu:hover > a > [class^="icon-"],
+.dropdown-submenu:focus > a > [class^="icon-"],
+.dropdown-submenu:hover > a > [class*=" icon-"],
+.dropdown-submenu:focus > a > [class*=" icon-"] {
+ background-image: url("../img/glyphicons-halflings-white.png");
+}
+.icon-glass {
+ background-position: 0 0;
+}
+.icon-music {
+ background-position: -24px 0;
+}
+.icon-search {
+ background-position: -48px 0;
+}
+.icon-envelope {
+ background-position: -72px 0;
+}
+.icon-heart {
+ background-position: -96px 0;
+}
+.icon-star {
+ background-position: -120px 0;
+}
+.icon-star-empty {
+ background-position: -144px 0;
+}
+.icon-user {
+ background-position: -168px 0;
+}
+.icon-film {
+ background-position: -192px 0;
+}
+.icon-th-large {
+ background-position: -216px 0;
+}
+.icon-th {
+ background-position: -240px 0;
+}
+.icon-th-list {
+ background-position: -264px 0;
+}
+.icon-ok {
+ background-position: -288px 0;
+}
+.icon-remove {
+ background-position: -312px 0;
+}
+.icon-zoom-in {
+ background-position: -336px 0;
+}
+.icon-zoom-out {
+ background-position: -360px 0;
+}
+.icon-off {
+ background-position: -384px 0;
+}
+.icon-signal {
+ background-position: -408px 0;
+}
+.icon-cog {
+ background-position: -432px 0;
+}
+.icon-trash {
+ background-position: -456px 0;
+}
+.icon-home {
+ background-position: 0 -24px;
+}
+.icon-file {
+ background-position: -24px -24px;
+}
+.icon-time {
+ background-position: -48px -24px;
+}
+.icon-road {
+ background-position: -72px -24px;
+}
+.icon-download-alt {
+ background-position: -96px -24px;
+}
+.icon-download {
+ background-position: -120px -24px;
+}
+.icon-upload {
+ background-position: -144px -24px;
+}
+.icon-inbox {
+ background-position: -168px -24px;
+}
+.icon-play-circle {
+ background-position: -192px -24px;
+}
+.icon-repeat {
+ background-position: -216px -24px;
+}
+.icon-refresh {
+ background-position: -240px -24px;
+}
+.icon-list-alt {
+ background-position: -264px -24px;
+}
+.icon-lock {
+ background-position: -287px -24px;
+}
+.icon-flag {
+ background-position: -312px -24px;
+}
+.icon-headphones {
+ background-position: -336px -24px;
+}
+.icon-volume-off {
+ background-position: -360px -24px;
+}
+.icon-volume-down {
+ background-position: -384px -24px;
+}
+.icon-volume-up {
+ background-position: -408px -24px;
+}
+.icon-qrcode {
+ background-position: -432px -24px;
+}
+.icon-barcode {
+ background-position: -456px -24px;
+}
+.icon-tag {
+ background-position: 0 -48px;
+}
+.icon-tags {
+ background-position: -25px -48px;
+}
+.icon-book {
+ background-position: -48px -48px;
+}
+.icon-bookmark {
+ background-position: -72px -48px;
+}
+.icon-print {
+ background-position: -96px -48px;
+}
+.icon-camera {
+ background-position: -120px -48px;
+}
+.icon-font {
+ background-position: -144px -48px;
+}
+.icon-bold {
+ background-position: -167px -48px;
+}
+.icon-italic {
+ background-position: -192px -48px;
+}
+.icon-text-height {
+ background-position: -216px -48px;
+}
+.icon-text-width {
+ background-position: -240px -48px;
+}
+.icon-align-left {
+ background-position: -264px -48px;
+}
+.icon-align-center {
+ background-position: -288px -48px;
+}
+.icon-align-right {
+ background-position: -312px -48px;
+}
+.icon-align-justify {
+ background-position: -336px -48px;
+}
+.icon-list {
+ background-position: -360px -48px;
+}
+.icon-indent-left {
+ background-position: -384px -48px;
+}
+.icon-indent-right {
+ background-position: -408px -48px;
+}
+.icon-facetime-video {
+ background-position: -432px -48px;
+}
+.icon-picture {
+ background-position: -456px -48px;
+}
+.icon-pencil {
+ background-position: 0 -72px;
+}
+.icon-map-marker {
+ background-position: -24px -72px;
+}
+.icon-adjust {
+ background-position: -48px -72px;
+}
+.icon-tint {
+ background-position: -72px -72px;
+}
+.icon-edit {
+ background-position: -96px -72px;
+}
+.icon-share {
+ background-position: -120px -72px;
+}
+.icon-check {
+ background-position: -144px -72px;
+}
+.icon-move {
+ background-position: -168px -72px;
+}
+.icon-step-backward {
+ background-position: -192px -72px;
+}
+.icon-fast-backward {
+ background-position: -216px -72px;
+}
+.icon-backward {
+ background-position: -240px -72px;
+}
+.icon-play {
+ background-position: -264px -72px;
+}
+.icon-pause {
+ background-position: -288px -72px;
+}
+.icon-stop {
+ background-position: -312px -72px;
+}
+.icon-forward {
+ background-position: -336px -72px;
+}
+.icon-fast-forward {
+ background-position: -360px -72px;
+}
+.icon-step-forward {
+ background-position: -384px -72px;
+}
+.icon-eject {
+ background-position: -408px -72px;
+}
+.icon-chevron-left {
+ background-position: -432px -72px;
+}
+.icon-chevron-right {
+ background-position: -456px -72px;
+}
+.icon-plus-sign {
+ background-position: 0 -96px;
+}
+.icon-minus-sign {
+ background-position: -24px -96px;
+}
+.icon-remove-sign {
+ background-position: -48px -96px;
+}
+.icon-ok-sign {
+ background-position: -72px -96px;
+}
+.icon-question-sign {
+ background-position: -96px -96px;
+}
+.icon-info-sign {
+ background-position: -120px -96px;
+}
+.icon-screenshot {
+ background-position: -144px -96px;
+}
+.icon-remove-circle {
+ background-position: -168px -96px;
+}
+.icon-ok-circle {
+ background-position: -192px -96px;
+}
+.icon-ban-circle {
+ background-position: -216px -96px;
+}
+.icon-arrow-left {
+ background-position: -240px -96px;
+}
+.icon-arrow-right {
+ background-position: -264px -96px;
+}
+.icon-arrow-up {
+ background-position: -289px -96px;
+}
+.icon-arrow-down {
+ background-position: -312px -96px;
+}
+.icon-share-alt {
+ background-position: -336px -96px;
+}
+.icon-resize-full {
+ background-position: -360px -96px;
+}
+.icon-resize-small {
+ background-position: -384px -96px;
+}
+.icon-plus {
+ background-position: -408px -96px;
+}
+.icon-minus {
+ background-position: -433px -96px;
+}
+.icon-asterisk {
+ background-position: -456px -96px;
+}
+.icon-exclamation-sign {
+ background-position: 0 -120px;
+}
+.icon-gift {
+ background-position: -24px -120px;
+}
+.icon-leaf {
+ background-position: -48px -120px;
+}
+.icon-fire {
+ background-position: -72px -120px;
+}
+.icon-eye-open {
+ background-position: -96px -120px;
+}
+.icon-eye-close {
+ background-position: -120px -120px;
+}
+.icon-warning-sign {
+ background-position: -144px -120px;
+}
+.icon-plane {
+ background-position: -168px -120px;
+}
+.icon-calendar {
+ background-position: -192px -120px;
+}
+.icon-random {
+ background-position: -216px -120px;
+ width: 16px;
+}
+.icon-comment {
+ background-position: -240px -120px;
+}
+.icon-magnet {
+ background-position: -264px -120px;
+}
+.icon-chevron-up {
+ background-position: -288px -120px;
+}
+.icon-chevron-down {
+ background-position: -313px -119px;
+}
+.icon-retweet {
+ background-position: -336px -120px;
+}
+.icon-shopping-cart {
+ background-position: -360px -120px;
+}
+.icon-folder-close {
+ background-position: -384px -120px;
+ width: 16px;
+}
+.icon-folder-open {
+ background-position: -408px -120px;
+ width: 16px;
+}
+.icon-resize-vertical {
+ background-position: -432px -119px;
+}
+.icon-resize-horizontal {
+ background-position: -456px -118px;
+}
+.icon-hdd {
+ background-position: 0 -144px;
+}
+.icon-bullhorn {
+ background-position: -24px -144px;
+}
+.icon-bell {
+ background-position: -48px -144px;
+}
+.icon-certificate {
+ background-position: -72px -144px;
+}
+.icon-thumbs-up {
+ background-position: -96px -144px;
+}
+.icon-thumbs-down {
+ background-position: -120px -144px;
+}
+.icon-hand-right {
+ background-position: -144px -144px;
+}
+.icon-hand-left {
+ background-position: -168px -144px;
+}
+.icon-hand-up {
+ background-position: -192px -144px;
+}
+.icon-hand-down {
+ background-position: -216px -144px;
+}
+.icon-circle-arrow-right {
+ background-position: -240px -144px;
+}
+.icon-circle-arrow-left {
+ background-position: -264px -144px;
+}
+.icon-circle-arrow-up {
+ background-position: -288px -144px;
+}
+.icon-circle-arrow-down {
+ background-position: -312px -144px;
+}
+.icon-globe {
+ background-position: -336px -144px;
+}
+.icon-wrench {
+ background-position: -360px -144px;
+}
+.icon-tasks {
+ background-position: -384px -144px;
+}
+.icon-filter {
+ background-position: -408px -144px;
+}
+.icon-briefcase {
+ background-position: -432px -144px;
+}
+.icon-fullscreen {
+ background-position: -456px -144px;
+}
+.btn-group {
+ position: relative;
+ display: inline-block;
+ *display: inline;
+ /* IE7 inline-block hack */
+
+ *zoom: 1;
+ font-size: 0;
+ vertical-align: middle;
+ white-space: nowrap;
+ *margin-left: .3em;
+}
+.btn-group:first-child {
+ *margin-left: 0;
+}
+.btn-group + .btn-group {
+ margin-left: 5px;
+}
+.btn-toolbar {
+ font-size: 0;
+ margin-top: 10px;
+ margin-bottom: 10px;
+}
+.btn-toolbar > .btn + .btn,
+.btn-toolbar > .btn-group + .btn,
+.btn-toolbar > .btn + .btn-group {
+ margin-left: 5px;
+}
+.btn-group > .btn {
+ position: relative;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+.btn-group > .btn + .btn {
+ margin-left: -1px;
+}
+.btn-group > .btn,
+.btn-group > .dropdown-menu,
+.btn-group > .popover {
+ font-size: 14px;
+}
+.btn-group > .btn-mini {
+ font-size: 10.5px;
+}
+.btn-group > .btn-small {
+ font-size: 11.9px;
+}
+.btn-group > .btn-large {
+ font-size: 17.5px;
+}
+.btn-group > .btn:first-child {
+ margin-left: 0;
+ -webkit-border-top-left-radius: 4px;
+ -moz-border-radius-topleft: 4px;
+ border-top-left-radius: 4px;
+ -webkit-border-bottom-left-radius: 4px;
+ -moz-border-radius-bottomleft: 4px;
+ border-bottom-left-radius: 4px;
+}
+.btn-group > .btn:last-child,
+.btn-group > .dropdown-toggle {
+ -webkit-border-top-right-radius: 4px;
+ -moz-border-radius-topright: 4px;
+ border-top-right-radius: 4px;
+ -webkit-border-bottom-right-radius: 4px;
+ -moz-border-radius-bottomright: 4px;
+ border-bottom-right-radius: 4px;
+}
+.btn-group > .btn.large:first-child {
+ margin-left: 0;
+ -webkit-border-top-left-radius: 6px;
+ -moz-border-radius-topleft: 6px;
+ border-top-left-radius: 6px;
+ -webkit-border-bottom-left-radius: 6px;
+ -moz-border-radius-bottomleft: 6px;
+ border-bottom-left-radius: 6px;
+}
+.btn-group > .btn.large:last-child,
+.btn-group > .large.dropdown-toggle {
+ -webkit-border-top-right-radius: 6px;
+ -moz-border-radius-topright: 6px;
+ border-top-right-radius: 6px;
+ -webkit-border-bottom-right-radius: 6px;
+ -moz-border-radius-bottomright: 6px;
+ border-bottom-right-radius: 6px;
+}
+.btn-group > .btn:hover,
+.btn-group > .btn:focus,
+.btn-group > .btn:active,
+.btn-group > .btn.active {
+ z-index: 2;
+}
+.btn-group .dropdown-toggle:active,
+.btn-group.open .dropdown-toggle {
+ outline: 0;
+}
+.btn-group > .btn + .dropdown-toggle {
+ padding-left: 8px;
+ padding-right: 8px;
+ -webkit-box-shadow: inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
+ -moz-box-shadow: inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
+ box-shadow: inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
+ *padding-top: 5px;
+ *padding-bottom: 5px;
+}
+.btn-group > .btn-mini + .dropdown-toggle {
+ padding-left: 5px;
+ padding-right: 5px;
+ *padding-top: 2px;
+ *padding-bottom: 2px;
+}
+.btn-group > .btn-small + .dropdown-toggle {
+ *padding-top: 5px;
+ *padding-bottom: 4px;
+}
+.btn-group > .btn-large + .dropdown-toggle {
+ padding-left: 12px;
+ padding-right: 12px;
+ *padding-top: 7px;
+ *padding-bottom: 7px;
+}
+.btn-group.open .dropdown-toggle {
+ background-image: none;
+ -webkit-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);
+ -moz-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);
+ box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);
+}
+.btn-group.open .btn.dropdown-toggle {
+ background-color: #e6e6e6;
+}
+.btn-group.open .btn-primary.dropdown-toggle {
+ background-color: #0044cc;
+}
+.btn-group.open .btn-warning.dropdown-toggle {
+ background-color: #f89406;
+}
+.btn-group.open .btn-danger.dropdown-toggle {
+ background-color: #bd362f;
+}
+.btn-group.open .btn-success.dropdown-toggle {
+ background-color: #51a351;
+}
+.btn-group.open .btn-info.dropdown-toggle {
+ background-color: #2f96b4;
+}
+.btn-group.open .btn-inverse.dropdown-toggle {
+ background-color: #222222;
+}
+.btn .caret {
+ margin-top: 8px;
+ margin-left: 0;
+}
+.btn-large .caret {
+ margin-top: 6px;
+}
+.btn-large .caret {
+ border-left-width: 5px;
+ border-right-width: 5px;
+ border-top-width: 5px;
+}
+.btn-mini .caret,
+.btn-small .caret {
+ margin-top: 8px;
+}
+.dropup .btn-large .caret {
+ border-bottom-width: 5px;
+}
+.btn-primary .caret,
+.btn-warning .caret,
+.btn-danger .caret,
+.btn-info .caret,
+.btn-success .caret,
+.btn-inverse .caret {
+ border-top-color: #ffffff;
+ border-bottom-color: #ffffff;
+}
+.btn-group-vertical {
+ display: inline-block;
+ *display: inline;
+ /* IE7 inline-block hack */
+
+ *zoom: 1;
+}
+.btn-group-vertical > .btn {
+ display: block;
+ float: none;
+ max-width: 100%;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+.btn-group-vertical > .btn + .btn {
+ margin-left: 0;
+ margin-top: -1px;
+}
+.btn-group-vertical > .btn:first-child {
+ -webkit-border-radius: 4px 4px 0 0;
+ -moz-border-radius: 4px 4px 0 0;
+ border-radius: 4px 4px 0 0;
+}
+.btn-group-vertical > .btn:last-child {
+ -webkit-border-radius: 0 0 4px 4px;
+ -moz-border-radius: 0 0 4px 4px;
+ border-radius: 0 0 4px 4px;
+}
+.btn-group-vertical > .btn-large:first-child {
+ -webkit-border-radius: 6px 6px 0 0;
+ -moz-border-radius: 6px 6px 0 0;
+ border-radius: 6px 6px 0 0;
+}
+.btn-group-vertical > .btn-large:last-child {
+ -webkit-border-radius: 0 0 6px 6px;
+ -moz-border-radius: 0 0 6px 6px;
+ border-radius: 0 0 6px 6px;
+}
+.nav {
+ margin-left: 0;
+ margin-bottom: 20px;
+ list-style: none;
+}
+.nav > li > a {
+ display: block;
+}
+.nav > li > a:hover,
+.nav > li > a:focus {
+ text-decoration: none;
+ background-color: #eeeeee;
+}
+.nav > li > a > img {
+ max-width: none;
+}
+.nav > .pull-right {
+ float: right;
+}
+.nav-header {
+ display: block;
+ padding: 3px 15px;
+ font-size: 11px;
+ font-weight: bold;
+ line-height: 20px;
+ color: #999999;
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+ text-transform: uppercase;
+}
+.nav li + .nav-header {
+ margin-top: 9px;
+}
+.nav-list {
+ padding-left: 15px;
+ padding-right: 15px;
+ margin-bottom: 0;
+}
+.nav-list > li > a,
+.nav-list .nav-header {
+ margin-left: -15px;
+ margin-right: -15px;
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+}
+.nav-list > li > a {
+ padding: 3px 15px;
+}
+.nav-list > .active > a,
+.nav-list > .active > a:hover,
+.nav-list > .active > a:focus {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
+ background-color: #0088cc;
+}
+.nav-list [class^="icon-"],
+.nav-list [class*=" icon-"] {
+ margin-right: 2px;
+}
+.nav-list .divider {
+ *width: 100%;
+ height: 1px;
+ margin: 9px 1px;
+ *margin: -5px 0 5px;
+ overflow: hidden;
+ background-color: #e5e5e5;
+ border-bottom: 1px solid #ffffff;
+}
+.nav-tabs,
+.nav-pills {
+ *zoom: 1;
+}
+.nav-tabs:before,
+.nav-pills:before,
+.nav-tabs:after,
+.nav-pills:after {
+ display: table;
+ content: "";
+ line-height: 0;
+}
+.nav-tabs:after,
+.nav-pills:after {
+ clear: both;
+}
+.nav-tabs > li,
+.nav-pills > li {
+ float: left;
+}
+.nav-tabs > li > a,
+.nav-pills > li > a {
+ padding-right: 12px;
+ padding-left: 12px;
+ margin-right: 2px;
+ line-height: 14px;
+}
+.nav-tabs {
+ border-bottom: 1px solid #ddd;
+}
+.nav-tabs > li {
+ margin-bottom: -1px;
+}
+.nav-tabs > li > a {
+ padding-top: 8px;
+ padding-bottom: 8px;
+ line-height: 20px;
+ border: 1px solid transparent;
+ -webkit-border-radius: 4px 4px 0 0;
+ -moz-border-radius: 4px 4px 0 0;
+ border-radius: 4px 4px 0 0;
+}
+.nav-tabs > li > a:hover,
+.nav-tabs > li > a:focus {
+ border-color: #eeeeee #eeeeee #dddddd;
+}
+.nav-tabs > .active > a,
+.nav-tabs > .active > a:hover,
+.nav-tabs > .active > a:focus {
+ color: #555555;
+ background-color: #ffffff;
+ border: 1px solid #ddd;
+ border-bottom-color: transparent;
+ cursor: default;
+}
+.nav-pills > li > a {
+ padding-top: 8px;
+ padding-bottom: 8px;
+ margin-top: 2px;
+ margin-bottom: 2px;
+ -webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ border-radius: 5px;
+}
+.nav-pills > .active > a,
+.nav-pills > .active > a:hover,
+.nav-pills > .active > a:focus {
+ color: #ffffff;
+ background-color: #0088cc;
+}
+.nav-stacked > li {
+ float: none;
+}
+.nav-stacked > li > a {
+ margin-right: 0;
+}
+.nav-tabs.nav-stacked {
+ border-bottom: 0;
+}
+.nav-tabs.nav-stacked > li > a {
+ border: 1px solid #ddd;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+.nav-tabs.nav-stacked > li:first-child > a {
+ -webkit-border-top-right-radius: 4px;
+ -moz-border-radius-topright: 4px;
+ border-top-right-radius: 4px;
+ -webkit-border-top-left-radius: 4px;
+ -moz-border-radius-topleft: 4px;
+ border-top-left-radius: 4px;
+}
+.nav-tabs.nav-stacked > li:last-child > a {
+ -webkit-border-bottom-right-radius: 4px;
+ -moz-border-radius-bottomright: 4px;
+ border-bottom-right-radius: 4px;
+ -webkit-border-bottom-left-radius: 4px;
+ -moz-border-radius-bottomleft: 4px;
+ border-bottom-left-radius: 4px;
+}
+.nav-tabs.nav-stacked > li > a:hover,
+.nav-tabs.nav-stacked > li > a:focus {
+ border-color: #ddd;
+ z-index: 2;
+}
+.nav-pills.nav-stacked > li > a {
+ margin-bottom: 3px;
+}
+.nav-pills.nav-stacked > li:last-child > a {
+ margin-bottom: 1px;
+}
+.nav-tabs .dropdown-menu {
+ -webkit-border-radius: 0 0 6px 6px;
+ -moz-border-radius: 0 0 6px 6px;
+ border-radius: 0 0 6px 6px;
+}
+.nav-pills .dropdown-menu {
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+}
+.nav .dropdown-toggle .caret {
+ border-top-color: #0088cc;
+ border-bottom-color: #0088cc;
+ margin-top: 6px;
+}
+.nav .dropdown-toggle:hover .caret,
+.nav .dropdown-toggle:focus .caret {
+ border-top-color: #005580;
+ border-bottom-color: #005580;
+}
+/* move down carets for tabs */
+.nav-tabs .dropdown-toggle .caret {
+ margin-top: 8px;
+}
+.nav .active .dropdown-toggle .caret {
+ border-top-color: #fff;
+ border-bottom-color: #fff;
+}
+.nav-tabs .active .dropdown-toggle .caret {
+ border-top-color: #555555;
+ border-bottom-color: #555555;
+}
+.nav > .dropdown.active > a:hover,
+.nav > .dropdown.active > a:focus {
+ cursor: pointer;
+}
+.nav-tabs .open .dropdown-toggle,
+.nav-pills .open .dropdown-toggle,
+.nav > li.dropdown.open.active > a:hover,
+.nav > li.dropdown.open.active > a:focus {
+ color: #ffffff;
+ background-color: #999999;
+ border-color: #999999;
+}
+.nav li.dropdown.open .caret,
+.nav li.dropdown.open.active .caret,
+.nav li.dropdown.open a:hover .caret,
+.nav li.dropdown.open a:focus .caret {
+ border-top-color: #ffffff;
+ border-bottom-color: #ffffff;
+ opacity: 1;
+ filter: alpha(opacity=100);
+}
+.tabs-stacked .open > a:hover,
+.tabs-stacked .open > a:focus {
+ border-color: #999999;
+}
+.tabbable {
+ *zoom: 1;
+}
+.tabbable:before,
+.tabbable:after {
+ display: table;
+ content: "";
+ line-height: 0;
+}
+.tabbable:after {
+ clear: both;
+}
+.tab-content {
+ overflow: auto;
+}
+.tabs-below > .nav-tabs,
+.tabs-right > .nav-tabs,
+.tabs-left > .nav-tabs {
+ border-bottom: 0;
+}
+.tab-content > .tab-pane,
+.pill-content > .pill-pane {
+ display: none;
+}
+.tab-content > .active,
+.pill-content > .active {
+ display: block;
+}
+.tabs-below > .nav-tabs {
+ border-top: 1px solid #ddd;
+}
+.tabs-below > .nav-tabs > li {
+ margin-top: -1px;
+ margin-bottom: 0;
+}
+.tabs-below > .nav-tabs > li > a {
+ -webkit-border-radius: 0 0 4px 4px;
+ -moz-border-radius: 0 0 4px 4px;
+ border-radius: 0 0 4px 4px;
+}
+.tabs-below > .nav-tabs > li > a:hover,
+.tabs-below > .nav-tabs > li > a:focus {
+ border-bottom-color: transparent;
+ border-top-color: #ddd;
+}
+.tabs-below > .nav-tabs > .active > a,
+.tabs-below > .nav-tabs > .active > a:hover,
+.tabs-below > .nav-tabs > .active > a:focus {
+ border-color: transparent #ddd #ddd #ddd;
+}
+.tabs-left > .nav-tabs > li,
+.tabs-right > .nav-tabs > li {
+ float: none;
+}
+.tabs-left > .nav-tabs > li > a,
+.tabs-right > .nav-tabs > li > a {
+ min-width: 74px;
+ margin-right: 0;
+ margin-bottom: 3px;
+}
+.tabs-left > .nav-tabs {
+ float: left;
+ margin-right: 19px;
+ border-right: 1px solid #ddd;
+}
+.tabs-left > .nav-tabs > li > a {
+ margin-right: -1px;
+ -webkit-border-radius: 4px 0 0 4px;
+ -moz-border-radius: 4px 0 0 4px;
+ border-radius: 4px 0 0 4px;
+}
+.tabs-left > .nav-tabs > li > a:hover,
+.tabs-left > .nav-tabs > li > a:focus {
+ border-color: #eeeeee #dddddd #eeeeee #eeeeee;
+}
+.tabs-left > .nav-tabs .active > a,
+.tabs-left > .nav-tabs .active > a:hover,
+.tabs-left > .nav-tabs .active > a:focus {
+ border-color: #ddd transparent #ddd #ddd;
+ *border-right-color: #ffffff;
+}
+.tabs-right > .nav-tabs {
+ float: right;
+ margin-left: 19px;
+ border-left: 1px solid #ddd;
+}
+.tabs-right > .nav-tabs > li > a {
+ margin-left: -1px;
+ -webkit-border-radius: 0 4px 4px 0;
+ -moz-border-radius: 0 4px 4px 0;
+ border-radius: 0 4px 4px 0;
+}
+.tabs-right > .nav-tabs > li > a:hover,
+.tabs-right > .nav-tabs > li > a:focus {
+ border-color: #eeeeee #eeeeee #eeeeee #dddddd;
+}
+.tabs-right > .nav-tabs .active > a,
+.tabs-right > .nav-tabs .active > a:hover,
+.tabs-right > .nav-tabs .active > a:focus {
+ border-color: #ddd #ddd #ddd transparent;
+ *border-left-color: #ffffff;
+}
+.nav > .disabled > a {
+ color: #999999;
+}
+.nav > .disabled > a:hover,
+.nav > .disabled > a:focus {
+ text-decoration: none;
+ background-color: transparent;
+ cursor: default;
+}
+.navbar {
+ overflow: visible;
+ margin-bottom: 20px;
+ *position: relative;
+ *z-index: 2;
+}
+.navbar-inner {
+ min-height: 40px;
+ padding-left: 20px;
+ padding-right: 20px;
+ background-color: #fafafa;
+ background-image: -moz-linear-gradient(top, #ffffff, #f2f2f2);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f2f2f2));
+ background-image: -webkit-linear-gradient(top, #ffffff, #f2f2f2);
+ background-image: -o-linear-gradient(top, #ffffff, #f2f2f2);
+ background-image: linear-gradient(to bottom, #ffffff, #f2f2f2);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0);
+ border: 1px solid #d4d4d4;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
+ -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
+ *zoom: 1;
+}
+.navbar-inner:before,
+.navbar-inner:after {
+ display: table;
+ content: "";
+ line-height: 0;
+}
+.navbar-inner:after {
+ clear: both;
+}
+.navbar .container {
+ width: auto;
+}
+.nav-collapse.collapse {
+ height: auto;
+ overflow: visible;
+}
+.navbar .brand {
+ float: left;
+ display: block;
+ padding: 10px 20px 10px;
+ margin-left: -20px;
+ font-size: 20px;
+ font-weight: 200;
+ color: #777777;
+ text-shadow: 0 1px 0 #ffffff;
+}
+.navbar .brand:hover,
+.navbar .brand:focus {
+ text-decoration: none;
+}
+.navbar-text {
+ margin-bottom: 0;
+ line-height: 40px;
+ color: #777777;
+}
+.navbar-link {
+ color: #777777;
+}
+.navbar-link:hover,
+.navbar-link:focus {
+ color: #333333;
+}
+.navbar .divider-vertical {
+ height: 40px;
+ margin: 0 9px;
+ border-left: 1px solid #f2f2f2;
+ border-right: 1px solid #ffffff;
+}
+.navbar .btn,
+.navbar .btn-group {
+ margin-top: 5px;
+}
+.navbar .btn-group .btn,
+.navbar .input-prepend .btn,
+.navbar .input-append .btn,
+.navbar .input-prepend .btn-group,
+.navbar .input-append .btn-group {
+ margin-top: 0;
+}
+.navbar-form {
+ margin-bottom: 0;
+ *zoom: 1;
+}
+.navbar-form:before,
+.navbar-form:after {
+ display: table;
+ content: "";
+ line-height: 0;
+}
+.navbar-form:after {
+ clear: both;
+}
+.navbar-form input,
+.navbar-form select,
+.navbar-form .radio,
+.navbar-form .checkbox {
+ margin-top: 5px;
+}
+.navbar-form input,
+.navbar-form select,
+.navbar-form .btn {
+ display: inline-block;
+ margin-bottom: 0;
+}
+.navbar-form input[type="image"],
+.navbar-form input[type="checkbox"],
+.navbar-form input[type="radio"] {
+ margin-top: 3px;
+}
+.navbar-form .input-append,
+.navbar-form .input-prepend {
+ margin-top: 5px;
+ white-space: nowrap;
+}
+.navbar-form .input-append input,
+.navbar-form .input-prepend input {
+ margin-top: 0;
+}
+.navbar-search {
+ position: relative;
+ float: left;
+ margin-top: 5px;
+ margin-bottom: 0;
+}
+.navbar-search .search-query {
+ margin-bottom: 0;
+ padding: 4px 14px;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-size: 13px;
+ font-weight: normal;
+ line-height: 1;
+ -webkit-border-radius: 15px;
+ -moz-border-radius: 15px;
+ border-radius: 15px;
+}
+.navbar-static-top {
+ position: static;
+ margin-bottom: 0;
+}
+.navbar-static-top .navbar-inner {
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+.navbar-fixed-top,
+.navbar-fixed-bottom {
+ position: fixed;
+ right: 0;
+ left: 0;
+ z-index: 1030;
+ margin-bottom: 0;
+}
+.navbar-fixed-top .navbar-inner,
+.navbar-static-top .navbar-inner {
+ border-width: 0 0 1px;
+}
+.navbar-fixed-bottom .navbar-inner {
+ border-width: 1px 0 0;
+}
+.navbar-fixed-top .navbar-inner,
+.navbar-fixed-bottom .navbar-inner {
+ padding-left: 0;
+ padding-right: 0;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+.navbar-static-top .container,
+.navbar-fixed-top .container,
+.navbar-fixed-bottom .container {
+ width: 940px;
+}
+.navbar-fixed-top {
+ top: 0;
+}
+.navbar-fixed-top .navbar-inner,
+.navbar-static-top .navbar-inner {
+ -webkit-box-shadow: 0 1px 10px rgba(0,0,0,.1);
+ -moz-box-shadow: 0 1px 10px rgba(0,0,0,.1);
+ box-shadow: 0 1px 10px rgba(0,0,0,.1);
+}
+.navbar-fixed-bottom {
+ bottom: 0;
+}
+.navbar-fixed-bottom .navbar-inner {
+ -webkit-box-shadow: 0 -1px 10px rgba(0,0,0,.1);
+ -moz-box-shadow: 0 -1px 10px rgba(0,0,0,.1);
+ box-shadow: 0 -1px 10px rgba(0,0,0,.1);
+}
+.navbar .nav {
+ position: relative;
+ left: 0;
+ display: block;
+ float: left;
+ margin: 0 10px 0 0;
+}
+.navbar .nav.pull-right {
+ float: right;
+ margin-right: 0;
+}
+.navbar .nav > li {
+ float: left;
+}
+.navbar .nav > li > a {
+ float: none;
+ padding: 10px 15px 10px;
+ color: #777777;
+ text-decoration: none;
+ text-shadow: 0 1px 0 #ffffff;
+}
+.navbar .nav .dropdown-toggle .caret {
+ margin-top: 8px;
+}
+.navbar .nav > li > a:focus,
+.navbar .nav > li > a:hover {
+ background-color: transparent;
+ color: #333333;
+ text-decoration: none;
+}
+.navbar .nav > .active > a,
+.navbar .nav > .active > a:hover,
+.navbar .nav > .active > a:focus {
+ color: #555555;
+ text-decoration: none;
+ background-color: #e5e5e5;
+ -webkit-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125);
+ -moz-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125);
+ box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125);
+}
+.navbar .btn-navbar {
+ display: none;
+ float: right;
+ padding: 7px 10px;
+ margin-left: 5px;
+ margin-right: 5px;
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #ededed;
+ background-image: -moz-linear-gradient(top, #f2f2f2, #e5e5e5);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f2f2f2), to(#e5e5e5));
+ background-image: -webkit-linear-gradient(top, #f2f2f2, #e5e5e5);
+ background-image: -o-linear-gradient(top, #f2f2f2, #e5e5e5);
+ background-image: linear-gradient(to bottom, #f2f2f2, #e5e5e5);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffe5e5e5', GradientType=0);
+ border-color: #e5e5e5 #e5e5e5 #bfbfbf;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ *background-color: #e5e5e5;
+ /* Darken IE7 buttons by default so they stand out more given they won't have borders */
+
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+ -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075);
+ -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075);
+ box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075);
+}
+.navbar .btn-navbar:hover,
+.navbar .btn-navbar:focus,
+.navbar .btn-navbar:active,
+.navbar .btn-navbar.active,
+.navbar .btn-navbar.disabled,
+.navbar .btn-navbar[disabled] {
+ color: #ffffff;
+ background-color: #e5e5e5;
+ *background-color: #d9d9d9;
+}
+.navbar .btn-navbar:active,
+.navbar .btn-navbar.active {
+ background-color: #cccccc \9;
+}
+.navbar .btn-navbar .icon-bar {
+ display: block;
+ width: 18px;
+ height: 2px;
+ background-color: #f5f5f5;
+ -webkit-border-radius: 1px;
+ -moz-border-radius: 1px;
+ border-radius: 1px;
+ -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
+ -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
+ box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
+}
+.btn-navbar .icon-bar + .icon-bar {
+ margin-top: 3px;
+}
+.navbar .nav > li > .dropdown-menu:before {
+ content: '';
+ display: inline-block;
+ border-left: 7px solid transparent;
+ border-right: 7px solid transparent;
+ border-bottom: 7px solid #ccc;
+ border-bottom-color: rgba(0, 0, 0, 0.2);
+ position: absolute;
+ top: -7px;
+ left: 9px;
+}
+.navbar .nav > li > .dropdown-menu:after {
+ content: '';
+ display: inline-block;
+ border-left: 6px solid transparent;
+ border-right: 6px solid transparent;
+ border-bottom: 6px solid #ffffff;
+ position: absolute;
+ top: -6px;
+ left: 10px;
+}
+.navbar-fixed-bottom .nav > li > .dropdown-menu:before {
+ border-top: 7px solid #ccc;
+ border-top-color: rgba(0, 0, 0, 0.2);
+ border-bottom: 0;
+ bottom: -7px;
+ top: auto;
+}
+.navbar-fixed-bottom .nav > li > .dropdown-menu:after {
+ border-top: 6px solid #ffffff;
+ border-bottom: 0;
+ bottom: -6px;
+ top: auto;
+}
+.navbar .nav li.dropdown > a:hover .caret,
+.navbar .nav li.dropdown > a:focus .caret {
+ border-top-color: #333333;
+ border-bottom-color: #333333;
+}
+.navbar .nav li.dropdown.open > .dropdown-toggle,
+.navbar .nav li.dropdown.active > .dropdown-toggle,
+.navbar .nav li.dropdown.open.active > .dropdown-toggle {
+ background-color: #e5e5e5;
+ color: #555555;
+}
+.navbar .nav li.dropdown > .dropdown-toggle .caret {
+ border-top-color: #777777;
+ border-bottom-color: #777777;
+}
+.navbar .nav li.dropdown.open > .dropdown-toggle .caret,
+.navbar .nav li.dropdown.active > .dropdown-toggle .caret,
+.navbar .nav li.dropdown.open.active > .dropdown-toggle .caret {
+ border-top-color: #555555;
+ border-bottom-color: #555555;
+}
+.navbar .pull-right > li > .dropdown-menu,
+.navbar .nav > li > .dropdown-menu.pull-right {
+ left: auto;
+ right: 0;
+}
+.navbar .pull-right > li > .dropdown-menu:before,
+.navbar .nav > li > .dropdown-menu.pull-right:before {
+ left: auto;
+ right: 12px;
+}
+.navbar .pull-right > li > .dropdown-menu:after,
+.navbar .nav > li > .dropdown-menu.pull-right:after {
+ left: auto;
+ right: 13px;
+}
+.navbar .pull-right > li > .dropdown-menu .dropdown-menu,
+.navbar .nav > li > .dropdown-menu.pull-right .dropdown-menu {
+ left: auto;
+ right: 100%;
+ margin-left: 0;
+ margin-right: -1px;
+ -webkit-border-radius: 6px 0 6px 6px;
+ -moz-border-radius: 6px 0 6px 6px;
+ border-radius: 6px 0 6px 6px;
+}
+.navbar-inverse .navbar-inner {
+ background-color: #1b1b1b;
+ background-image: -moz-linear-gradient(top, #222222, #111111);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#222222), to(#111111));
+ background-image: -webkit-linear-gradient(top, #222222, #111111);
+ background-image: -o-linear-gradient(top, #222222, #111111);
+ background-image: linear-gradient(to bottom, #222222, #111111);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0);
+ border-color: #252525;
+}
+.navbar-inverse .brand,
+.navbar-inverse .nav > li > a {
+ color: #999999;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+}
+.navbar-inverse .brand:hover,
+.navbar-inverse .nav > li > a:hover,
+.navbar-inverse .brand:focus,
+.navbar-inverse .nav > li > a:focus {
+ color: #ffffff;
+}
+.navbar-inverse .brand {
+ color: #999999;
+}
+.navbar-inverse .navbar-text {
+ color: #999999;
+}
+.navbar-inverse .nav > li > a:focus,
+.navbar-inverse .nav > li > a:hover {
+ background-color: transparent;
+ color: #ffffff;
+}
+.navbar-inverse .nav .active > a,
+.navbar-inverse .nav .active > a:hover,
+.navbar-inverse .nav .active > a:focus {
+ color: #ffffff;
+ background-color: #111111;
+}
+.navbar-inverse .navbar-link {
+ color: #999999;
+}
+.navbar-inverse .navbar-link:hover,
+.navbar-inverse .navbar-link:focus {
+ color: #ffffff;
+}
+.navbar-inverse .divider-vertical {
+ border-left-color: #111111;
+ border-right-color: #222222;
+}
+.navbar-inverse .nav li.dropdown.open > .dropdown-toggle,
+.navbar-inverse .nav li.dropdown.active > .dropdown-toggle,
+.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle {
+ background-color: #111111;
+ color: #ffffff;
+}
+.navbar-inverse .nav li.dropdown > a:hover .caret,
+.navbar-inverse .nav li.dropdown > a:focus .caret {
+ border-top-color: #ffffff;
+ border-bottom-color: #ffffff;
+}
+.navbar-inverse .nav li.dropdown > .dropdown-toggle .caret {
+ border-top-color: #999999;
+ border-bottom-color: #999999;
+}
+.navbar-inverse .nav li.dropdown.open > .dropdown-toggle .caret,
+.navbar-inverse .nav li.dropdown.active > .dropdown-toggle .caret,
+.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle .caret {
+ border-top-color: #ffffff;
+ border-bottom-color: #ffffff;
+}
+.navbar-inverse .navbar-search .search-query {
+ color: #ffffff;
+ background-color: #515151;
+ border-color: #111111;
+ -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15);
+ -moz-box-shadow: inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15);
+ box-shadow: inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15);
+ -webkit-transition: none;
+ -moz-transition: none;
+ -o-transition: none;
+ transition: none;
+}
+.navbar-inverse .navbar-search .search-query:-moz-placeholder {
+ color: #cccccc;
+}
+.navbar-inverse .navbar-search .search-query:-ms-input-placeholder {
+ color: #cccccc;
+}
+.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder {
+ color: #cccccc;
+}
+.navbar-inverse .navbar-search .search-query:focus,
+.navbar-inverse .navbar-search .search-query.focused {
+ padding: 5px 15px;
+ color: #333333;
+ text-shadow: 0 1px 0 #ffffff;
+ background-color: #ffffff;
+ border: 0;
+ -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
+ -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
+ box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
+ outline: 0;
+}
+.navbar-inverse .btn-navbar {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #0e0e0e;
+ background-image: -moz-linear-gradient(top, #151515, #040404);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#151515), to(#040404));
+ background-image: -webkit-linear-gradient(top, #151515, #040404);
+ background-image: -o-linear-gradient(top, #151515, #040404);
+ background-image: linear-gradient(to bottom, #151515, #040404);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515', endColorstr='#ff040404', GradientType=0);
+ border-color: #040404 #040404 #000000;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ *background-color: #040404;
+ /* Darken IE7 buttons by default so they stand out more given they won't have borders */
+
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+}
+.navbar-inverse .btn-navbar:hover,
+.navbar-inverse .btn-navbar:focus,
+.navbar-inverse .btn-navbar:active,
+.navbar-inverse .btn-navbar.active,
+.navbar-inverse .btn-navbar.disabled,
+.navbar-inverse .btn-navbar[disabled] {
+ color: #ffffff;
+ background-color: #040404;
+ *background-color: #000000;
+}
+.navbar-inverse .btn-navbar:active,
+.navbar-inverse .btn-navbar.active {
+ background-color: #000000 \9;
+}
+.breadcrumb {
+ padding: 8px 15px;
+ margin: 0 0 20px;
+ list-style: none;
+ background-color: #f5f5f5;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+.breadcrumb > li {
+ display: inline-block;
+ *display: inline;
+ /* IE7 inline-block hack */
+
+ *zoom: 1;
+ text-shadow: 0 1px 0 #ffffff;
+}
+.breadcrumb > li > .divider {
+ padding: 0 5px;
+ color: #ccc;
+}
+.breadcrumb > .active {
+ color: #999999;
+}
+.pagination {
+ margin: 20px 0;
+}
+.pagination ul {
+ display: inline-block;
+ *display: inline;
+ /* IE7 inline-block hack */
+
+ *zoom: 1;
+ margin-left: 0;
+ margin-bottom: 0;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+.pagination ul > li {
+ display: inline;
+}
+.pagination ul > li > a,
+.pagination ul > li > span {
+ float: left;
+ padding: 4px 12px;
+ line-height: 20px;
+ text-decoration: none;
+ background-color: #ffffff;
+ border: 1px solid #dddddd;
+ border-left-width: 0;
+}
+.pagination ul > li > a:hover,
+.pagination ul > li > a:focus,
+.pagination ul > .active > a,
+.pagination ul > .active > span {
+ background-color: #f5f5f5;
+}
+.pagination ul > .active > a,
+.pagination ul > .active > span {
+ color: #999999;
+ cursor: default;
+}
+.pagination ul > .disabled > span,
+.pagination ul > .disabled > a,
+.pagination ul > .disabled > a:hover,
+.pagination ul > .disabled > a:focus {
+ color: #999999;
+ background-color: transparent;
+ cursor: default;
+}
+.pagination ul > li:first-child > a,
+.pagination ul > li:first-child > span {
+ border-left-width: 1px;
+ -webkit-border-top-left-radius: 4px;
+ -moz-border-radius-topleft: 4px;
+ border-top-left-radius: 4px;
+ -webkit-border-bottom-left-radius: 4px;
+ -moz-border-radius-bottomleft: 4px;
+ border-bottom-left-radius: 4px;
+}
+.pagination ul > li:last-child > a,
+.pagination ul > li:last-child > span {
+ -webkit-border-top-right-radius: 4px;
+ -moz-border-radius-topright: 4px;
+ border-top-right-radius: 4px;
+ -webkit-border-bottom-right-radius: 4px;
+ -moz-border-radius-bottomright: 4px;
+ border-bottom-right-radius: 4px;
+}
+.pagination-centered {
+ text-align: center;
+}
+.pagination-right {
+ text-align: right;
+}
+.pagination-large ul > li > a,
+.pagination-large ul > li > span {
+ padding: 11px 19px;
+ font-size: 17.5px;
+}
+.pagination-large ul > li:first-child > a,
+.pagination-large ul > li:first-child > span {
+ -webkit-border-top-left-radius: 6px;
+ -moz-border-radius-topleft: 6px;
+ border-top-left-radius: 6px;
+ -webkit-border-bottom-left-radius: 6px;
+ -moz-border-radius-bottomleft: 6px;
+ border-bottom-left-radius: 6px;
+}
+.pagination-large ul > li:last-child > a,
+.pagination-large ul > li:last-child > span {
+ -webkit-border-top-right-radius: 6px;
+ -moz-border-radius-topright: 6px;
+ border-top-right-radius: 6px;
+ -webkit-border-bottom-right-radius: 6px;
+ -moz-border-radius-bottomright: 6px;
+ border-bottom-right-radius: 6px;
+}
+.pagination-mini ul > li:first-child > a,
+.pagination-small ul > li:first-child > a,
+.pagination-mini ul > li:first-child > span,
+.pagination-small ul > li:first-child > span {
+ -webkit-border-top-left-radius: 3px;
+ -moz-border-radius-topleft: 3px;
+ border-top-left-radius: 3px;
+ -webkit-border-bottom-left-radius: 3px;
+ -moz-border-radius-bottomleft: 3px;
+ border-bottom-left-radius: 3px;
+}
+.pagination-mini ul > li:last-child > a,
+.pagination-small ul > li:last-child > a,
+.pagination-mini ul > li:last-child > span,
+.pagination-small ul > li:last-child > span {
+ -webkit-border-top-right-radius: 3px;
+ -moz-border-radius-topright: 3px;
+ border-top-right-radius: 3px;
+ -webkit-border-bottom-right-radius: 3px;
+ -moz-border-radius-bottomright: 3px;
+ border-bottom-right-radius: 3px;
+}
+.pagination-small ul > li > a,
+.pagination-small ul > li > span {
+ padding: 2px 10px;
+ font-size: 11.9px;
+}
+.pagination-mini ul > li > a,
+.pagination-mini ul > li > span {
+ padding: 0 6px;
+ font-size: 10.5px;
+}
+.pager {
+ margin: 20px 0;
+ list-style: none;
+ text-align: center;
+ *zoom: 1;
+}
+.pager:before,
+.pager:after {
+ display: table;
+ content: "";
+ line-height: 0;
+}
+.pager:after {
+ clear: both;
+}
+.pager li {
+ display: inline;
+}
+.pager li > a,
+.pager li > span {
+ display: inline-block;
+ padding: 5px 14px;
+ background-color: #fff;
+ border: 1px solid #ddd;
+ -webkit-border-radius: 15px;
+ -moz-border-radius: 15px;
+ border-radius: 15px;
+}
+.pager li > a:hover,
+.pager li > a:focus {
+ text-decoration: none;
+ background-color: #f5f5f5;
+}
+.pager .next > a,
+.pager .next > span {
+ float: right;
+}
+.pager .previous > a,
+.pager .previous > span {
+ float: left;
+}
+.pager .disabled > a,
+.pager .disabled > a:hover,
+.pager .disabled > a:focus,
+.pager .disabled > span {
+ color: #999999;
+ background-color: #fff;
+ cursor: default;
+}
+.thumbnails {
+ margin-left: -20px;
+ list-style: none;
+ *zoom: 1;
+}
+.thumbnails:before,
+.thumbnails:after {
+ display: table;
+ content: "";
+ line-height: 0;
+}
+.thumbnails:after {
+ clear: both;
+}
+.row-fluid .thumbnails {
+ margin-left: 0;
+}
+.thumbnails > li {
+ float: left;
+ margin-bottom: 20px;
+ margin-left: 20px;
+}
+.thumbnail {
+ display: block;
+ padding: 4px;
+ line-height: 20px;
+ border: 1px solid #ddd;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
+ -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
+ -webkit-transition: all 0.2s ease-in-out;
+ -moz-transition: all 0.2s ease-in-out;
+ -o-transition: all 0.2s ease-in-out;
+ transition: all 0.2s ease-in-out;
+}
+a.thumbnail:hover,
+a.thumbnail:focus {
+ border-color: #0088cc;
+ -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
+ -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
+ box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
+}
+.thumbnail > img {
+ display: block;
+ max-width: 100%;
+ margin-left: auto;
+ margin-right: auto;
+}
+.thumbnail .caption {
+ padding: 9px;
+ color: #555555;
+}
+.alert {
+ padding: 8px 35px 8px 14px;
+ margin-bottom: 20px;
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+ background-color: #fcf8e3;
+ border: 1px solid #fbeed5;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+.alert,
+.alert h4 {
+ color: #c09853;
+}
+.alert h4 {
+ margin: 0;
+}
+.alert .close {
+ position: relative;
+ top: -2px;
+ right: -21px;
+ line-height: 20px;
+}
+.alert-success {
+ background-color: #dff0d8;
+ border-color: #d6e9c6;
+ color: #468847;
+}
+.alert-success h4 {
+ color: #468847;
+}
+.alert-danger,
+.alert-error {
+ background-color: #f2dede;
+ border-color: #eed3d7;
+ color: #b94a48;
+}
+.alert-danger h4,
+.alert-error h4 {
+ color: #b94a48;
+}
+.alert-info {
+ background-color: #d9edf7;
+ border-color: #bce8f1;
+ color: #3a87ad;
+}
+.alert-info h4 {
+ color: #3a87ad;
+}
+.alert-block {
+ padding-top: 14px;
+ padding-bottom: 14px;
+}
+.alert-block > p,
+.alert-block > ul {
+ margin-bottom: 0;
+}
+.alert-block p + p {
+ margin-top: 5px;
+}
+@-webkit-keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+ to {
+ background-position: 0 0;
+ }
+}
+@-moz-keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+ to {
+ background-position: 0 0;
+ }
+}
+@-ms-keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+ to {
+ background-position: 0 0;
+ }
+}
+@-o-keyframes progress-bar-stripes {
+ from {
+ background-position: 0 0;
+ }
+ to {
+ background-position: 40px 0;
+ }
+}
+@keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+ to {
+ background-position: 0 0;
+ }
+}
+.progress {
+ overflow: hidden;
+ height: 20px;
+ margin-bottom: 20px;
+ background-color: #f7f7f7;
+ background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));
+ background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9);
+ background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9);
+ background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0);
+ -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+ -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+.progress .bar {
+ width: 0%;
+ height: 100%;
+ color: #ffffff;
+ float: left;
+ font-size: 12px;
+ text-align: center;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #0e90d2;
+ background-image: -moz-linear-gradient(top, #149bdf, #0480be);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));
+ background-image: -webkit-linear-gradient(top, #149bdf, #0480be);
+ background-image: -o-linear-gradient(top, #149bdf, #0480be);
+ background-image: linear-gradient(to bottom, #149bdf, #0480be);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0);
+ -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ -webkit-transition: width 0.6s ease;
+ -moz-transition: width 0.6s ease;
+ -o-transition: width 0.6s ease;
+ transition: width 0.6s ease;
+}
+.progress .bar + .bar {
+ -webkit-box-shadow: inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15);
+ -moz-box-shadow: inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15);
+ box-shadow: inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15);
+}
+.progress-striped .bar {
+ background-color: #149bdf;
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ -webkit-background-size: 40px 40px;
+ -moz-background-size: 40px 40px;
+ -o-background-size: 40px 40px;
+ background-size: 40px 40px;
+}
+.progress.active .bar {
+ -webkit-animation: progress-bar-stripes 2s linear infinite;
+ -moz-animation: progress-bar-stripes 2s linear infinite;
+ -ms-animation: progress-bar-stripes 2s linear infinite;
+ -o-animation: progress-bar-stripes 2s linear infinite;
+ animation: progress-bar-stripes 2s linear infinite;
+}
+.progress-danger .bar,
+.progress .bar-danger {
+ background-color: #dd514c;
+ background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));
+ background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
+ background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
+ background-image: linear-gradient(to bottom, #ee5f5b, #c43c35);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0);
+}
+.progress-danger.progress-striped .bar,
+.progress-striped .bar-danger {
+ background-color: #ee5f5b;
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+.progress-success .bar,
+.progress .bar-success {
+ background-color: #5eb95e;
+ background-image: -moz-linear-gradient(top, #62c462, #57a957);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));
+ background-image: -webkit-linear-gradient(top, #62c462, #57a957);
+ background-image: -o-linear-gradient(top, #62c462, #57a957);
+ background-image: linear-gradient(to bottom, #62c462, #57a957);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0);
+}
+.progress-success.progress-striped .bar,
+.progress-striped .bar-success {
+ background-color: #62c462;
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+.progress-info .bar,
+.progress .bar-info {
+ background-color: #4bb1cf;
+ background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));
+ background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);
+ background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
+ background-image: linear-gradient(to bottom, #5bc0de, #339bb9);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0);
+}
+.progress-info.progress-striped .bar,
+.progress-striped .bar-info {
+ background-color: #5bc0de;
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+.progress-warning .bar,
+.progress .bar-warning {
+ background-color: #faa732;
+ background-image: -moz-linear-gradient(top, #fbb450, #f89406);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));
+ background-image: -webkit-linear-gradient(top, #fbb450, #f89406);
+ background-image: -o-linear-gradient(top, #fbb450, #f89406);
+ background-image: linear-gradient(to bottom, #fbb450, #f89406);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0);
+}
+.progress-warning.progress-striped .bar,
+.progress-striped .bar-warning {
+ background-color: #fbb450;
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+.hero-unit {
+ padding: 60px;
+ margin-bottom: 30px;
+ font-size: 18px;
+ font-weight: 200;
+ line-height: 30px;
+ color: inherit;
+ background-color: #eeeeee;
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+}
+.hero-unit h1 {
+ margin-bottom: 0;
+ font-size: 60px;
+ line-height: 1;
+ color: inherit;
+ letter-spacing: -1px;
+}
+.hero-unit li {
+ line-height: 30px;
+}
+.media,
+.media-body {
+ overflow: hidden;
+ *overflow: visible;
+ zoom: 1;
+}
+.media,
+.media .media {
+ margin-top: 15px;
+}
+.media:first-child {
+ margin-top: 0;
+}
+.media-object {
+ display: block;
+}
+.media-heading {
+ margin: 0 0 5px;
+}
+.media > .pull-left {
+ margin-right: 10px;
+}
+.media > .pull-right {
+ margin-left: 10px;
+}
+.media-list {
+ margin-left: 0;
+ list-style: none;
+}
+.tooltip {
+ position: absolute;
+ z-index: 1030;
+ display: block;
+ visibility: visible;
+ font-size: 11px;
+ line-height: 1.4;
+ opacity: 0;
+ filter: alpha(opacity=0);
+}
+.tooltip.in {
+ opacity: 0.8;
+ filter: alpha(opacity=80);
+}
+.tooltip.top {
+ margin-top: -3px;
+ padding: 5px 0;
+}
+.tooltip.right {
+ margin-left: 3px;
+ padding: 0 5px;
+}
+.tooltip.bottom {
+ margin-top: 3px;
+ padding: 5px 0;
+}
+.tooltip.left {
+ margin-left: -3px;
+ padding: 0 5px;
+}
+.tooltip-inner {
+ max-width: 200px;
+ padding: 8px;
+ color: #ffffff;
+ text-align: center;
+ text-decoration: none;
+ background-color: #000000;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+.tooltip-arrow {
+ position: absolute;
+ width: 0;
+ height: 0;
+ border-color: transparent;
+ border-style: solid;
+}
+.tooltip.top .tooltip-arrow {
+ bottom: 0;
+ left: 50%;
+ margin-left: -5px;
+ border-width: 5px 5px 0;
+ border-top-color: #000000;
+}
+.tooltip.right .tooltip-arrow {
+ top: 50%;
+ left: 0;
+ margin-top: -5px;
+ border-width: 5px 5px 5px 0;
+ border-right-color: #000000;
+}
+.tooltip.left .tooltip-arrow {
+ top: 50%;
+ right: 0;
+ margin-top: -5px;
+ border-width: 5px 0 5px 5px;
+ border-left-color: #000000;
+}
+.tooltip.bottom .tooltip-arrow {
+ top: 0;
+ left: 50%;
+ margin-left: -5px;
+ border-width: 0 5px 5px;
+ border-bottom-color: #000000;
+}
+.popover {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 1010;
+ display: none;
+ max-width: 276px;
+ padding: 1px;
+ text-align: left;
+ background-color: #ffffff;
+ -webkit-background-clip: padding-box;
+ -moz-background-clip: padding;
+ background-clip: padding-box;
+ border: 1px solid #ccc;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+ -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ white-space: normal;
+}
+.popover.top {
+ margin-top: -10px;
+}
+.popover.right {
+ margin-left: 10px;
+}
+.popover.bottom {
+ margin-top: 10px;
+}
+.popover.left {
+ margin-left: -10px;
+}
+.popover-title {
+ margin: 0;
+ padding: 8px 14px;
+ font-size: 14px;
+ font-weight: normal;
+ line-height: 18px;
+ background-color: #f7f7f7;
+ border-bottom: 1px solid #ebebeb;
+ -webkit-border-radius: 5px 5px 0 0;
+ -moz-border-radius: 5px 5px 0 0;
+ border-radius: 5px 5px 0 0;
+}
+.popover-title:empty {
+ display: none;
+}
+.popover-content {
+ padding: 9px 14px;
+}
+.popover .arrow,
+.popover .arrow:after {
+ position: absolute;
+ display: block;
+ width: 0;
+ height: 0;
+ border-color: transparent;
+ border-style: solid;
+}
+.popover .arrow {
+ border-width: 11px;
+}
+.popover .arrow:after {
+ border-width: 10px;
+ content: "";
+}
+.popover.top .arrow {
+ left: 50%;
+ margin-left: -11px;
+ border-bottom-width: 0;
+ border-top-color: #999;
+ border-top-color: rgba(0, 0, 0, 0.25);
+ bottom: -11px;
+}
+.popover.top .arrow:after {
+ bottom: 1px;
+ margin-left: -10px;
+ border-bottom-width: 0;
+ border-top-color: #ffffff;
+}
+.popover.right .arrow {
+ top: 50%;
+ left: -11px;
+ margin-top: -11px;
+ border-left-width: 0;
+ border-right-color: #999;
+ border-right-color: rgba(0, 0, 0, 0.25);
+}
+.popover.right .arrow:after {
+ left: 1px;
+ bottom: -10px;
+ border-left-width: 0;
+ border-right-color: #ffffff;
+}
+.popover.bottom .arrow {
+ left: 50%;
+ margin-left: -11px;
+ border-top-width: 0;
+ border-bottom-color: #999;
+ border-bottom-color: rgba(0, 0, 0, 0.25);
+ top: -11px;
+}
+.popover.bottom .arrow:after {
+ top: 1px;
+ margin-left: -10px;
+ border-top-width: 0;
+ border-bottom-color: #ffffff;
+}
+.popover.left .arrow {
+ top: 50%;
+ right: -11px;
+ margin-top: -11px;
+ border-right-width: 0;
+ border-left-color: #999;
+ border-left-color: rgba(0, 0, 0, 0.25);
+}
+.popover.left .arrow:after {
+ right: 1px;
+ border-right-width: 0;
+ border-left-color: #ffffff;
+ bottom: -10px;
+}
+.modal-backdrop {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1040;
+ background-color: #000000;
+}
+.modal-backdrop.fade {
+ opacity: 0;
+}
+.modal-backdrop,
+.modal-backdrop.fade.in {
+ opacity: 0.8;
+ filter: alpha(opacity=80);
+}
+.modal {
+ position: fixed;
+ top: 10%;
+ left: 50%;
+ z-index: 1050;
+ width: 560px;
+ margin-left: -280px;
+ background-color: #ffffff;
+ border: 1px solid #999;
+ border: 1px solid rgba(0, 0, 0, 0.3);
+ *border: 1px solid #999;
+ /* IE6-7 */
+
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+ -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
+ -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
+ box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
+ -webkit-background-clip: padding-box;
+ -moz-background-clip: padding-box;
+ background-clip: padding-box;
+ outline: none;
+}
+.modal.fade {
+ -webkit-transition: opacity .3s linear, top .3s ease-out;
+ -moz-transition: opacity .3s linear, top .3s ease-out;
+ -o-transition: opacity .3s linear, top .3s ease-out;
+ transition: opacity .3s linear, top .3s ease-out;
+ top: -25%;
+}
+.modal.fade.in {
+ top: 10%;
+}
+.modal-header {
+ padding: 9px 15px;
+ border-bottom: 1px solid #eee;
+}
+.modal-header .close {
+ margin-top: 2px;
+}
+.modal-header h3 {
+ margin: 0;
+ line-height: 30px;
+}
+.modal-body {
+ position: relative;
+ overflow-y: auto;
+ max-height: 400px;
+ padding: 15px;
+}
+.modal-form {
+ margin-bottom: 0;
+}
+.modal-footer {
+ padding: 14px 15px 15px;
+ margin-bottom: 0;
+ text-align: right;
+ background-color: #f5f5f5;
+ border-top: 1px solid #ddd;
+ -webkit-border-radius: 0 0 6px 6px;
+ -moz-border-radius: 0 0 6px 6px;
+ border-radius: 0 0 6px 6px;
+ -webkit-box-shadow: inset 0 1px 0 #ffffff;
+ -moz-box-shadow: inset 0 1px 0 #ffffff;
+ box-shadow: inset 0 1px 0 #ffffff;
+ *zoom: 1;
+}
+.modal-footer:before,
+.modal-footer:after {
+ display: table;
+ content: "";
+ line-height: 0;
+}
+.modal-footer:after {
+ clear: both;
+}
+.modal-footer .btn + .btn {
+ margin-left: 5px;
+ margin-bottom: 0;
+}
+.modal-footer .btn-group .btn + .btn {
+ margin-left: -1px;
+}
+.modal-footer .btn-block + .btn-block {
+ margin-left: 0;
+}
+.dropup,
+.dropdown {
+ position: relative;
+}
+.dropdown-toggle {
+ *margin-bottom: -3px;
+}
+.dropdown-toggle:active,
+.open .dropdown-toggle {
+ outline: 0;
+}
+.caret {
+ display: inline-block;
+ width: 0;
+ height: 0;
+ vertical-align: top;
+ border-top: 4px solid #000000;
+ border-right: 4px solid transparent;
+ border-left: 4px solid transparent;
+ content: "";
+}
+.dropdown .caret {
+ margin-top: 8px;
+ margin-left: 2px;
+}
+.dropdown-menu {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ z-index: 1000;
+ display: none;
+ float: left;
+ min-width: 160px;
+ padding: 5px 0;
+ margin: 2px 0 0;
+ list-style: none;
+ background-color: #ffffff;
+ border: 1px solid #ccc;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ *border-right-width: 2px;
+ *border-bottom-width: 2px;
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+ -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ -webkit-background-clip: padding-box;
+ -moz-background-clip: padding;
+ background-clip: padding-box;
+}
+.dropdown-menu.pull-right {
+ right: 0;
+ left: auto;
+}
+.dropdown-menu .divider {
+ *width: 100%;
+ height: 1px;
+ margin: 9px 1px;
+ *margin: -5px 0 5px;
+ overflow: hidden;
+ background-color: #e5e5e5;
+ border-bottom: 1px solid #ffffff;
+}
+.dropdown-menu > li > a {
+ display: block;
+ padding: 3px 20px;
+ clear: both;
+ font-weight: normal;
+ line-height: 20px;
+ color: #333333;
+ white-space: nowrap;
+}
+.dropdown-menu > li > a:hover,
+.dropdown-menu > li > a:focus,
+.dropdown-submenu:hover > a,
+.dropdown-submenu:focus > a {
+ text-decoration: none;
+ color: #ffffff;
+ background-color: #0081c2;
+ background-image: -moz-linear-gradient(top, #0088cc, #0077b3);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));
+ background-image: -webkit-linear-gradient(top, #0088cc, #0077b3);
+ background-image: -o-linear-gradient(top, #0088cc, #0077b3);
+ background-image: linear-gradient(to bottom, #0088cc, #0077b3);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);
+}
+.dropdown-menu > .active > a,
+.dropdown-menu > .active > a:hover,
+.dropdown-menu > .active > a:focus {
+ color: #ffffff;
+ text-decoration: none;
+ outline: 0;
+ background-color: #0081c2;
+ background-image: -moz-linear-gradient(top, #0088cc, #0077b3);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));
+ background-image: -webkit-linear-gradient(top, #0088cc, #0077b3);
+ background-image: -o-linear-gradient(top, #0088cc, #0077b3);
+ background-image: linear-gradient(to bottom, #0088cc, #0077b3);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);
+}
+.dropdown-menu > .disabled > a,
+.dropdown-menu > .disabled > a:hover,
+.dropdown-menu > .disabled > a:focus {
+ color: #999999;
+}
+.dropdown-menu > .disabled > a:hover,
+.dropdown-menu > .disabled > a:focus {
+ text-decoration: none;
+ background-color: transparent;
+ background-image: none;
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+ cursor: default;
+}
+.open {
+ *z-index: 1000;
+}
+.open > .dropdown-menu {
+ display: block;
+}
+.dropdown-backdrop {
+ position: fixed;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ top: 0;
+ z-index: 990;
+}
+.pull-right > .dropdown-menu {
+ right: 0;
+ left: auto;
+}
+.dropup .caret,
+.navbar-fixed-bottom .dropdown .caret {
+ border-top: 0;
+ border-bottom: 4px solid #000000;
+ content: "";
+}
+.dropup .dropdown-menu,
+.navbar-fixed-bottom .dropdown .dropdown-menu {
+ top: auto;
+ bottom: 100%;
+ margin-bottom: 1px;
+}
+.dropdown-submenu {
+ position: relative;
+}
+.dropdown-submenu > .dropdown-menu {
+ top: 0;
+ left: 100%;
+ margin-top: -6px;
+ margin-left: -1px;
+ -webkit-border-radius: 0 6px 6px 6px;
+ -moz-border-radius: 0 6px 6px 6px;
+ border-radius: 0 6px 6px 6px;
+}
+.dropdown-submenu:hover > .dropdown-menu {
+ display: block;
+}
+.dropup .dropdown-submenu > .dropdown-menu {
+ top: auto;
+ bottom: 0;
+ margin-top: 0;
+ margin-bottom: -2px;
+ -webkit-border-radius: 5px 5px 5px 0;
+ -moz-border-radius: 5px 5px 5px 0;
+ border-radius: 5px 5px 5px 0;
+}
+.dropdown-submenu > a:after {
+ display: block;
+ content: " ";
+ float: right;
+ width: 0;
+ height: 0;
+ border-color: transparent;
+ border-style: solid;
+ border-width: 5px 0 5px 5px;
+ border-left-color: #cccccc;
+ margin-top: 5px;
+ margin-right: -10px;
+}
+.dropdown-submenu:hover > a:after {
+ border-left-color: #ffffff;
+}
+.dropdown-submenu.pull-left {
+ float: none;
+}
+.dropdown-submenu.pull-left > .dropdown-menu {
+ left: -100%;
+ margin-left: 10px;
+ -webkit-border-radius: 6px 0 6px 6px;
+ -moz-border-radius: 6px 0 6px 6px;
+ border-radius: 6px 0 6px 6px;
+}
+.dropdown .dropdown-menu .nav-header {
+ padding-left: 20px;
+ padding-right: 20px;
+}
+.typeahead {
+ z-index: 1051;
+ margin-top: 2px;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+.accordion {
+ margin-bottom: 20px;
+}
+.accordion-group {
+ margin-bottom: 2px;
+ border: 1px solid #e5e5e5;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+.accordion-heading {
+ border-bottom: 0;
+}
+.accordion-heading .accordion-toggle {
+ display: block;
+ padding: 8px 15px;
+}
+.accordion-toggle {
+ cursor: pointer;
+}
+.accordion-inner {
+ padding: 9px 15px;
+ border-top: 1px solid #e5e5e5;
+}
+.carousel {
+ position: relative;
+ margin-bottom: 20px;
+ line-height: 1;
+}
+.carousel-inner {
+ overflow: hidden;
+ width: 100%;
+ position: relative;
+}
+.carousel-inner > .item {
+ display: none;
+ position: relative;
+ -webkit-transition: 0.6s ease-in-out left;
+ -moz-transition: 0.6s ease-in-out left;
+ -o-transition: 0.6s ease-in-out left;
+ transition: 0.6s ease-in-out left;
+}
+.carousel-inner > .item > img,
+.carousel-inner > .item > a > img {
+ display: block;
+ line-height: 1;
+}
+.carousel-inner > .active,
+.carousel-inner > .next,
+.carousel-inner > .prev {
+ display: block;
+}
+.carousel-inner > .active {
+ left: 0;
+}
+.carousel-inner > .next,
+.carousel-inner > .prev {
+ position: absolute;
+ top: 0;
+ width: 100%;
+}
+.carousel-inner > .next {
+ left: 100%;
+}
+.carousel-inner > .prev {
+ left: -100%;
+}
+.carousel-inner > .next.left,
+.carousel-inner > .prev.right {
+ left: 0;
+}
+.carousel-inner > .active.left {
+ left: -100%;
+}
+.carousel-inner > .active.right {
+ left: 100%;
+}
+.carousel-control {
+ position: absolute;
+ top: 40%;
+ left: 15px;
+ width: 40px;
+ height: 40px;
+ margin-top: -20px;
+ font-size: 60px;
+ font-weight: 100;
+ line-height: 30px;
+ color: #ffffff;
+ text-align: center;
+ background: #222222;
+ border: 3px solid #ffffff;
+ -webkit-border-radius: 23px;
+ -moz-border-radius: 23px;
+ border-radius: 23px;
+ opacity: 0.5;
+ filter: alpha(opacity=50);
+}
+.carousel-control.right {
+ left: auto;
+ right: 15px;
+}
+.carousel-control:hover,
+.carousel-control:focus {
+ color: #ffffff;
+ text-decoration: none;
+ opacity: 0.9;
+ filter: alpha(opacity=90);
+}
+.carousel-indicators {
+ position: absolute;
+ top: 15px;
+ right: 15px;
+ z-index: 5;
+ margin: 0;
+ list-style: none;
+}
+.carousel-indicators li {
+ display: block;
+ float: left;
+ width: 10px;
+ height: 10px;
+ margin-left: 5px;
+ text-indent: -999px;
+ background-color: #ccc;
+ background-color: rgba(255, 255, 255, 0.25);
+ border-radius: 5px;
+}
+.carousel-indicators .active {
+ background-color: #fff;
+}
+.carousel-caption {
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ padding: 15px;
+ background: #333333;
+ background: rgba(0, 0, 0, 0.75);
+}
+.carousel-caption h4,
+.carousel-caption p {
+ color: #ffffff;
+ line-height: 20px;
+}
+.carousel-caption h4 {
+ margin: 0 0 5px;
+}
+.carousel-caption p {
+ margin-bottom: 0;
+}
+.well {
+ min-height: 20px;
+ padding: 19px;
+ margin-bottom: 20px;
+ background-color: #f5f5f5;
+ border: 1px solid #e3e3e3;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
+}
+.well blockquote {
+ border-color: #ddd;
+ border-color: rgba(0, 0, 0, 0.15);
+}
+.well-large {
+ padding: 24px;
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+}
+.well-small {
+ padding: 9px;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+}
+.close {
+ float: right;
+ font-size: 20px;
+ font-weight: bold;
+ line-height: 20px;
+ color: #000000;
+ text-shadow: 0 1px 0 #ffffff;
+ opacity: 0.2;
+ filter: alpha(opacity=20);
+}
+.close:hover,
+.close:focus {
+ color: #000000;
+ text-decoration: none;
+ cursor: pointer;
+ opacity: 0.4;
+ filter: alpha(opacity=40);
+}
+button.close {
+ padding: 0;
+ cursor: pointer;
+ background: transparent;
+ border: 0;
+ -webkit-appearance: none;
+}
+.pull-right {
+ float: right;
+}
+.pull-left {
+ float: left;
+}
+.hide {
+ display: none;
+}
+.show {
+ display: block;
+}
+.invisible {
+ visibility: hidden;
+}
+.affix {
+ position: fixed;
+}
+.fade {
+ opacity: 0;
+ -webkit-transition: opacity 0.15s linear;
+ -moz-transition: opacity 0.15s linear;
+ -o-transition: opacity 0.15s linear;
+ transition: opacity 0.15s linear;
+}
+.fade.in {
+ opacity: 1;
+}
+.collapse {
+ position: relative;
+ height: 0;
+ overflow: hidden;
+ -webkit-transition: height 0.35s ease;
+ -moz-transition: height 0.35s ease;
+ -o-transition: height 0.35s ease;
+ transition: height 0.35s ease;
+}
+.collapse.in {
+ height: auto;
+}
+@-ms-viewport {
+ width: device-width;
+}
+.hidden {
+ display: none;
+ visibility: hidden;
+}
+.visible-phone {
+ display: none !important;
+}
+.visible-tablet {
+ display: none !important;
+}
+.hidden-desktop {
+ display: none !important;
+}
+.visible-desktop {
+ display: inherit !important;
+}
+@media (min-width: 768px) and (max-width: 979px) {
+ .hidden-desktop {
+ display: inherit !important;
+ }
+ .visible-desktop {
+ display: none !important ;
+ }
+ .visible-tablet {
+ display: inherit !important;
+ }
+ .hidden-tablet {
+ display: none !important;
+ }
+}
+@media (max-width: 767px) {
+ .hidden-desktop {
+ display: inherit !important;
+ }
+ .visible-desktop {
+ display: none !important;
+ }
+ .visible-phone {
+ display: inherit !important;
+ }
+ .hidden-phone {
+ display: none !important;
+ }
+}
+.visible-print {
+ display: none !important;
+}
+@media print {
+ .visible-print {
+ display: inherit !important;
+ }
+ .hidden-print {
+ display: none !important;
+ }
+}
+@media (max-width: 767px) {
+ body {
+ padding-left: 20px;
+ padding-right: 20px;
+ }
+ .navbar-fixed-top,
+ .navbar-fixed-bottom,
+ .navbar-static-top {
+ margin-left: -20px;
+ margin-right: -20px;
+ }
+ .container-fluid {
+ padding: 0;
+ }
+ .dl-horizontal dt {
+ float: none;
+ clear: none;
+ width: auto;
+ text-align: left;
+ }
+ .dl-horizontal dd {
+ margin-left: 0;
+ }
+ .container {
+ width: auto;
+ }
+ .row-fluid {
+ width: 100%;
+ }
+ .row,
+ .thumbnails {
+ margin-left: 0;
+ }
+ .thumbnails > li {
+ float: none;
+ margin-left: 0;
+ }
+ [class*="span"],
+ .uneditable-input[class*="span"],
+ .row-fluid [class*="span"] {
+ float: none;
+ display: block;
+ width: 100%;
+ margin-left: 0;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ }
+ .span12,
+ .row-fluid .span12 {
+ width: 100%;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ }
+ .row-fluid [class*="offset"]:first-child {
+ margin-left: 0;
+ }
+ .input-large,
+ .input-xlarge,
+ .input-xxlarge,
+ input[class*="span"],
+ select[class*="span"],
+ textarea[class*="span"],
+ .uneditable-input {
+ display: block;
+ width: 100%;
+ min-height: 30px;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ }
+ .input-prepend input,
+ .input-append input,
+ .input-prepend input[class*="span"],
+ .input-append input[class*="span"] {
+ display: inline-block;
+ width: auto;
+ }
+ .controls-row [class*="span"] + [class*="span"] {
+ margin-left: 0;
+ }
+ .modal {
+ position: fixed;
+ top: 20px;
+ left: 20px;
+ right: 20px;
+ width: auto;
+ margin: 0;
+ }
+ .modal.fade {
+ top: -100px;
+ }
+ .modal.fade.in {
+ top: 20px;
+ }
+}
+@media (max-width: 480px) {
+ .nav-collapse {
+ -webkit-transform: translate3d(0, 0, 0);
+ }
+ .page-header h1 small {
+ display: block;
+ line-height: 20px;
+ }
+ input[type="checkbox"],
+ input[type="radio"] {
+ border: 1px solid #ccc;
+ }
+ .form-horizontal .control-label {
+ float: none;
+ width: auto;
+ padding-top: 0;
+ text-align: left;
+ }
+ .form-horizontal .controls {
+ margin-left: 0;
+ }
+ .form-horizontal .control-list {
+ padding-top: 0;
+ }
+ .form-horizontal .form-actions {
+ padding-left: 10px;
+ padding-right: 10px;
+ }
+ .media .pull-left,
+ .media .pull-right {
+ float: none;
+ display: block;
+ margin-bottom: 10px;
+ }
+ .media-object {
+ margin-right: 0;
+ margin-left: 0;
+ }
+ .modal {
+ top: 10px;
+ left: 10px;
+ right: 10px;
+ }
+ .modal-header .close {
+ padding: 10px;
+ margin: -10px;
+ }
+ .carousel-caption {
+ position: static;
+ }
+}
+@media (min-width: 768px) and (max-width: 979px) {
+ .row {
+ margin-left: -20px;
+ *zoom: 1;
+ }
+ .row:before,
+ .row:after {
+ display: table;
+ content: "";
+ line-height: 0;
+ }
+ .row:after {
+ clear: both;
+ }
+ [class*="span"] {
+ float: left;
+ min-height: 1px;
+ margin-left: 20px;
+ }
+ .container,
+ .navbar-static-top .container,
+ .navbar-fixed-top .container,
+ .navbar-fixed-bottom .container {
+ width: 724px;
+ }
+ .span12 {
+ width: 724px;
+ }
+ .span11 {
+ width: 662px;
+ }
+ .span10 {
+ width: 600px;
+ }
+ .span9 {
+ width: 538px;
+ }
+ .span8 {
+ width: 476px;
+ }
+ .span7 {
+ width: 414px;
+ }
+ .span6 {
+ width: 352px;
+ }
+ .span5 {
+ width: 290px;
+ }
+ .span4 {
+ width: 228px;
+ }
+ .span3 {
+ width: 166px;
+ }
+ .span2 {
+ width: 104px;
+ }
+ .span1 {
+ width: 42px;
+ }
+ .offset12 {
+ margin-left: 764px;
+ }
+ .offset11 {
+ margin-left: 702px;
+ }
+ .offset10 {
+ margin-left: 640px;
+ }
+ .offset9 {
+ margin-left: 578px;
+ }
+ .offset8 {
+ margin-left: 516px;
+ }
+ .offset7 {
+ margin-left: 454px;
+ }
+ .offset6 {
+ margin-left: 392px;
+ }
+ .offset5 {
+ margin-left: 330px;
+ }
+ .offset4 {
+ margin-left: 268px;
+ }
+ .offset3 {
+ margin-left: 206px;
+ }
+ .offset2 {
+ margin-left: 144px;
+ }
+ .offset1 {
+ margin-left: 82px;
+ }
+ .row-fluid {
+ width: 100%;
+ *zoom: 1;
+ }
+ .row-fluid:before,
+ .row-fluid:after {
+ display: table;
+ content: "";
+ line-height: 0;
+ }
+ .row-fluid:after {
+ clear: both;
+ }
+ .row-fluid [class*="span"] {
+ display: block;
+ width: 100%;
+ min-height: 30px;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ float: left;
+ margin-left: 2.7624309392265194%;
+ *margin-left: 2.709239449864817%;
+ }
+ .row-fluid [class*="span"]:first-child {
+ margin-left: 0;
+ }
+ .row-fluid .controls-row [class*="span"] + [class*="span"] {
+ margin-left: 2.7624309392265194%;
+ }
+ .row-fluid .span12 {
+ width: 100%;
+ *width: 99.94680851063829%;
+ }
+ .row-fluid .span11 {
+ width: 91.43646408839778%;
+ *width: 91.38327259903608%;
+ }
+ .row-fluid .span10 {
+ width: 82.87292817679558%;
+ *width: 82.81973668743387%;
+ }
+ .row-fluid .span9 {
+ width: 74.30939226519337%;
+ *width: 74.25620077583166%;
+ }
+ .row-fluid .span8 {
+ width: 65.74585635359117%;
+ *width: 65.69266486422946%;
+ }
+ .row-fluid .span7 {
+ width: 57.18232044198895%;
+ *width: 57.12912895262725%;
+ }
+ .row-fluid .span6 {
+ width: 48.61878453038674%;
+ *width: 48.56559304102504%;
+ }
+ .row-fluid .span5 {
+ width: 40.05524861878453%;
+ *width: 40.00205712942283%;
+ }
+ .row-fluid .span4 {
+ width: 31.491712707182323%;
+ *width: 31.43852121782062%;
+ }
+ .row-fluid .span3 {
+ width: 22.92817679558011%;
+ *width: 22.87498530621841%;
+ }
+ .row-fluid .span2 {
+ width: 14.3646408839779%;
+ *width: 14.311449394616199%;
+ }
+ .row-fluid .span1 {
+ width: 5.801104972375691%;
+ *width: 5.747913483013988%;
+ }
+ .row-fluid .offset12 {
+ margin-left: 105.52486187845304%;
+ *margin-left: 105.41847889972962%;
+ }
+ .row-fluid .offset12:first-child {
+ margin-left: 102.76243093922652%;
+ *margin-left: 102.6560479605031%;
+ }
+ .row-fluid .offset11 {
+ margin-left: 96.96132596685082%;
+ *margin-left: 96.8549429881274%;
+ }
+ .row-fluid .offset11:first-child {
+ margin-left: 94.1988950276243%;
+ *margin-left: 94.09251204890089%;
+ }
+ .row-fluid .offset10 {
+ margin-left: 88.39779005524862%;
+ *margin-left: 88.2914070765252%;
+ }
+ .row-fluid .offset10:first-child {
+ margin-left: 85.6353591160221%;
+ *margin-left: 85.52897613729868%;
+ }
+ .row-fluid .offset9 {
+ margin-left: 79.8342541436464%;
+ *margin-left: 79.72787116492299%;
+ }
+ .row-fluid .offset9:first-child {
+ margin-left: 77.07182320441989%;
+ *margin-left: 76.96544022569647%;
+ }
+ .row-fluid .offset8 {
+ margin-left: 71.2707182320442%;
+ *margin-left: 71.16433525332079%;
+ }
+ .row-fluid .offset8:first-child {
+ margin-left: 68.50828729281768%;
+ *margin-left: 68.40190431409427%;
+ }
+ .row-fluid .offset7 {
+ margin-left: 62.70718232044199%;
+ *margin-left: 62.600799341718584%;
+ }
+ .row-fluid .offset7:first-child {
+ margin-left: 59.94475138121547%;
+ *margin-left: 59.838368402492065%;
+ }
+ .row-fluid .offset6 {
+ margin-left: 54.14364640883978%;
+ *margin-left: 54.037263430116376%;
+ }
+ .row-fluid .offset6:first-child {
+ margin-left: 51.38121546961326%;
+ *margin-left: 51.27483249088986%;
+ }
+ .row-fluid .offset5 {
+ margin-left: 45.58011049723757%;
+ *margin-left: 45.47372751851417%;
+ }
+ .row-fluid .offset5:first-child {
+ margin-left: 42.81767955801105%;
+ *margin-left: 42.71129657928765%;
+ }
+ .row-fluid .offset4 {
+ margin-left: 37.01657458563536%;
+ *margin-left: 36.91019160691196%;
+ }
+ .row-fluid .offset4:first-child {
+ margin-left: 34.25414364640884%;
+ *margin-left: 34.14776066768544%;
+ }
+ .row-fluid .offset3 {
+ margin-left: 28.45303867403315%;
+ *margin-left: 28.346655695309746%;
+ }
+ .row-fluid .offset3:first-child {
+ margin-left: 25.69060773480663%;
+ *margin-left: 25.584224756083227%;
+ }
+ .row-fluid .offset2 {
+ margin-left: 19.88950276243094%;
+ *margin-left: 19.783119783707537%;
+ }
+ .row-fluid .offset2:first-child {
+ margin-left: 17.12707182320442%;
+ *margin-left: 17.02068884448102%;
+ }
+ .row-fluid .offset1 {
+ margin-left: 11.32596685082873%;
+ *margin-left: 11.219583872105325%;
+ }
+ .row-fluid .offset1:first-child {
+ margin-left: 8.56353591160221%;
+ *margin-left: 8.457152932878806%;
+ }
+ input,
+ textarea,
+ .uneditable-input {
+ margin-left: 0;
+ }
+ .controls-row [class*="span"] + [class*="span"] {
+ margin-left: 20px;
+ }
+ input.span12,
+ textarea.span12,
+ .uneditable-input.span12 {
+ width: 710px;
+ }
+ input.span11,
+ textarea.span11,
+ .uneditable-input.span11 {
+ width: 648px;
+ }
+ input.span10,
+ textarea.span10,
+ .uneditable-input.span10 {
+ width: 586px;
+ }
+ input.span9,
+ textarea.span9,
+ .uneditable-input.span9 {
+ width: 524px;
+ }
+ input.span8,
+ textarea.span8,
+ .uneditable-input.span8 {
+ width: 462px;
+ }
+ input.span7,
+ textarea.span7,
+ .uneditable-input.span7 {
+ width: 400px;
+ }
+ input.span6,
+ textarea.span6,
+ .uneditable-input.span6 {
+ width: 338px;
+ }
+ input.span5,
+ textarea.span5,
+ .uneditable-input.span5 {
+ width: 276px;
+ }
+ input.span4,
+ textarea.span4,
+ .uneditable-input.span4 {
+ width: 214px;
+ }
+ input.span3,
+ textarea.span3,
+ .uneditable-input.span3 {
+ width: 152px;
+ }
+ input.span2,
+ textarea.span2,
+ .uneditable-input.span2 {
+ width: 90px;
+ }
+ input.span1,
+ textarea.span1,
+ .uneditable-input.span1 {
+ width: 28px;
+ }
+}
+@media (min-width: 1200px) {
+ .row {
+ margin-left: -30px;
+ *zoom: 1;
+ }
+ .row:before,
+ .row:after {
+ display: table;
+ content: "";
+ line-height: 0;
+ }
+ .row:after {
+ clear: both;
+ }
+ [class*="span"] {
+ float: left;
+ min-height: 1px;
+ margin-left: 30px;
+ }
+ .container,
+ .navbar-static-top .container,
+ .navbar-fixed-top .container,
+ .navbar-fixed-bottom .container {
+ width: 1170px;
+ }
+ .span12 {
+ width: 1170px;
+ }
+ .span11 {
+ width: 1070px;
+ }
+ .span10 {
+ width: 970px;
+ }
+ .span9 {
+ width: 870px;
+ }
+ .span8 {
+ width: 770px;
+ }
+ .span7 {
+ width: 670px;
+ }
+ .span6 {
+ width: 570px;
+ }
+ .span5 {
+ width: 470px;
+ }
+ .span4 {
+ width: 370px;
+ }
+ .span3 {
+ width: 270px;
+ }
+ .span2 {
+ width: 170px;
+ }
+ .span1 {
+ width: 70px;
+ }
+ .offset12 {
+ margin-left: 1230px;
+ }
+ .offset11 {
+ margin-left: 1130px;
+ }
+ .offset10 {
+ margin-left: 1030px;
+ }
+ .offset9 {
+ margin-left: 930px;
+ }
+ .offset8 {
+ margin-left: 830px;
+ }
+ .offset7 {
+ margin-left: 730px;
+ }
+ .offset6 {
+ margin-left: 630px;
+ }
+ .offset5 {
+ margin-left: 530px;
+ }
+ .offset4 {
+ margin-left: 430px;
+ }
+ .offset3 {
+ margin-left: 330px;
+ }
+ .offset2 {
+ margin-left: 230px;
+ }
+ .offset1 {
+ margin-left: 130px;
+ }
+ .row-fluid {
+ width: 100%;
+ *zoom: 1;
+ }
+ .row-fluid:before,
+ .row-fluid:after {
+ display: table;
+ content: "";
+ line-height: 0;
+ }
+ .row-fluid:after {
+ clear: both;
+ }
+ .row-fluid [class*="span"] {
+ display: block;
+ width: 100%;
+ min-height: 30px;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ float: left;
+ margin-left: 2.564102564102564%;
+ *margin-left: 2.5109110747408616%;
+ }
+ .row-fluid [class*="span"]:first-child {
+ margin-left: 0;
+ }
+ .row-fluid .controls-row [class*="span"] + [class*="span"] {
+ margin-left: 2.564102564102564%;
+ }
+ .row-fluid .span12 {
+ width: 100%;
+ *width: 99.94680851063829%;
+ }
+ .row-fluid .span11 {
+ width: 91.45299145299145%;
+ *width: 91.39979996362975%;
+ }
+ .row-fluid .span10 {
+ width: 82.90598290598291%;
+ *width: 82.8527914166212%;
+ }
+ .row-fluid .span9 {
+ width: 74.35897435897436%;
+ *width: 74.30578286961266%;
+ }
+ .row-fluid .span8 {
+ width: 65.81196581196582%;
+ *width: 65.75877432260411%;
+ }
+ .row-fluid .span7 {
+ width: 57.26495726495726%;
+ *width: 57.21176577559556%;
+ }
+ .row-fluid .span6 {
+ width: 48.717948717948715%;
+ *width: 48.664757228587014%;
+ }
+ .row-fluid .span5 {
+ width: 40.17094017094017%;
+ *width: 40.11774868157847%;
+ }
+ .row-fluid .span4 {
+ width: 31.623931623931625%;
+ *width: 31.570740134569924%;
+ }
+ .row-fluid .span3 {
+ width: 23.076923076923077%;
+ *width: 23.023731587561375%;
+ }
+ .row-fluid .span2 {
+ width: 14.52991452991453%;
+ *width: 14.476723040552828%;
+ }
+ .row-fluid .span1 {
+ width: 5.982905982905983%;
+ *width: 5.929714493544281%;
+ }
+ .row-fluid .offset12 {
+ margin-left: 105.12820512820512%;
+ *margin-left: 105.02182214948171%;
+ }
+ .row-fluid .offset12:first-child {
+ margin-left: 102.56410256410257%;
+ *margin-left: 102.45771958537915%;
+ }
+ .row-fluid .offset11 {
+ margin-left: 96.58119658119658%;
+ *margin-left: 96.47481360247316%;
+ }
+ .row-fluid .offset11:first-child {
+ margin-left: 94.01709401709402%;
+ *margin-left: 93.91071103837061%;
+ }
+ .row-fluid .offset10 {
+ margin-left: 88.03418803418803%;
+ *margin-left: 87.92780505546462%;
+ }
+ .row-fluid .offset10:first-child {
+ margin-left: 85.47008547008548%;
+ *margin-left: 85.36370249136206%;
+ }
+ .row-fluid .offset9 {
+ margin-left: 79.48717948717949%;
+ *margin-left: 79.38079650845607%;
+ }
+ .row-fluid .offset9:first-child {
+ margin-left: 76.92307692307693%;
+ *margin-left: 76.81669394435352%;
+ }
+ .row-fluid .offset8 {
+ margin-left: 70.94017094017094%;
+ *margin-left: 70.83378796144753%;
+ }
+ .row-fluid .offset8:first-child {
+ margin-left: 68.37606837606839%;
+ *margin-left: 68.26968539734497%;
+ }
+ .row-fluid .offset7 {
+ margin-left: 62.393162393162385%;
+ *margin-left: 62.28677941443899%;
+ }
+ .row-fluid .offset7:first-child {
+ margin-left: 59.82905982905982%;
+ *margin-left: 59.72267685033642%;
+ }
+ .row-fluid .offset6 {
+ margin-left: 53.84615384615384%;
+ *margin-left: 53.739770867430444%;
+ }
+ .row-fluid .offset6:first-child {
+ margin-left: 51.28205128205128%;
+ *margin-left: 51.175668303327875%;
+ }
+ .row-fluid .offset5 {
+ margin-left: 45.299145299145295%;
+ *margin-left: 45.1927623204219%;
+ }
+ .row-fluid .offset5:first-child {
+ margin-left: 42.73504273504273%;
+ *margin-left: 42.62865975631933%;
+ }
+ .row-fluid .offset4 {
+ margin-left: 36.75213675213675%;
+ *margin-left: 36.645753773413354%;
+ }
+ .row-fluid .offset4:first-child {
+ margin-left: 34.18803418803419%;
+ *margin-left: 34.081651209310785%;
+ }
+ .row-fluid .offset3 {
+ margin-left: 28.205128205128204%;
+ *margin-left: 28.0987452264048%;
+ }
+ .row-fluid .offset3:first-child {
+ margin-left: 25.641025641025642%;
+ *margin-left: 25.53464266230224%;
+ }
+ .row-fluid .offset2 {
+ margin-left: 19.65811965811966%;
+ *margin-left: 19.551736679396257%;
+ }
+ .row-fluid .offset2:first-child {
+ margin-left: 17.094017094017094%;
+ *margin-left: 16.98763411529369%;
+ }
+ .row-fluid .offset1 {
+ margin-left: 11.11111111111111%;
+ *margin-left: 11.004728132387708%;
+ }
+ .row-fluid .offset1:first-child {
+ margin-left: 8.547008547008547%;
+ *margin-left: 8.440625568285142%;
+ }
+ input,
+ textarea,
+ .uneditable-input {
+ margin-left: 0;
+ }
+ .controls-row [class*="span"] + [class*="span"] {
+ margin-left: 30px;
+ }
+ input.span12,
+ textarea.span12,
+ .uneditable-input.span12 {
+ width: 1156px;
+ }
+ input.span11,
+ textarea.span11,
+ .uneditable-input.span11 {
+ width: 1056px;
+ }
+ input.span10,
+ textarea.span10,
+ .uneditable-input.span10 {
+ width: 956px;
+ }
+ input.span9,
+ textarea.span9,
+ .uneditable-input.span9 {
+ width: 856px;
+ }
+ input.span8,
+ textarea.span8,
+ .uneditable-input.span8 {
+ width: 756px;
+ }
+ input.span7,
+ textarea.span7,
+ .uneditable-input.span7 {
+ width: 656px;
+ }
+ input.span6,
+ textarea.span6,
+ .uneditable-input.span6 {
+ width: 556px;
+ }
+ input.span5,
+ textarea.span5,
+ .uneditable-input.span5 {
+ width: 456px;
+ }
+ input.span4,
+ textarea.span4,
+ .uneditable-input.span4 {
+ width: 356px;
+ }
+ input.span3,
+ textarea.span3,
+ .uneditable-input.span3 {
+ width: 256px;
+ }
+ input.span2,
+ textarea.span2,
+ .uneditable-input.span2 {
+ width: 156px;
+ }
+ input.span1,
+ textarea.span1,
+ .uneditable-input.span1 {
+ width: 56px;
+ }
+ .thumbnails {
+ margin-left: -30px;
+ }
+ .thumbnails > li {
+ margin-left: 30px;
+ }
+ .row-fluid .thumbnails {
+ margin-left: 0;
+ }
+}
+@media (max-width: 979px) {
+ body {
+ padding-top: 0;
+ }
+ .navbar-fixed-top,
+ .navbar-fixed-bottom {
+ position: static;
+ }
+ .navbar-fixed-top {
+ margin-bottom: 20px;
+ }
+ .navbar-fixed-bottom {
+ margin-top: 20px;
+ }
+ .navbar-fixed-top .navbar-inner,
+ .navbar-fixed-bottom .navbar-inner {
+ padding: 5px;
+ }
+ .navbar .container {
+ width: auto;
+ padding: 0;
+ }
+ .navbar .brand {
+ padding-left: 10px;
+ padding-right: 10px;
+ margin: 0 0 0 -5px;
+ }
+ .nav-collapse {
+ clear: both;
+ }
+ .nav-collapse .nav {
+ float: none;
+ margin: 0 0 10px;
+ }
+ .nav-collapse .nav > li {
+ float: none;
+ }
+ .nav-collapse .nav > li > a {
+ margin-bottom: 2px;
+ }
+ .nav-collapse .nav > .divider-vertical {
+ display: none;
+ }
+ .nav-collapse .nav .nav-header {
+ color: #777777;
+ text-shadow: none;
+ }
+ .nav-collapse .nav > li > a,
+ .nav-collapse .dropdown-menu a {
+ padding: 9px 15px;
+ font-weight: bold;
+ color: #777777;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+ }
+ .nav-collapse .btn {
+ padding: 4px 10px 4px;
+ font-weight: normal;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ }
+ .nav-collapse .dropdown-menu li + li a {
+ margin-bottom: 2px;
+ }
+ .nav-collapse .nav > li > a:hover,
+ .nav-collapse .nav > li > a:focus,
+ .nav-collapse .dropdown-menu a:hover,
+ .nav-collapse .dropdown-menu a:focus {
+ background-color: #f2f2f2;
+ }
+ .navbar-inverse .nav-collapse .nav > li > a,
+ .navbar-inverse .nav-collapse .dropdown-menu a {
+ color: #999999;
+ }
+ .navbar-inverse .nav-collapse .nav > li > a:hover,
+ .navbar-inverse .nav-collapse .nav > li > a:focus,
+ .navbar-inverse .nav-collapse .dropdown-menu a:hover,
+ .navbar-inverse .nav-collapse .dropdown-menu a:focus {
+ background-color: #111111;
+ }
+ .nav-collapse.in .btn-group {
+ margin-top: 5px;
+ padding: 0;
+ }
+ .nav-collapse .dropdown-menu {
+ position: static;
+ top: auto;
+ left: auto;
+ float: none;
+ display: none;
+ max-width: none;
+ margin: 0 15px;
+ padding: 0;
+ background-color: transparent;
+ border: none;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+ }
+ .nav-collapse .open > .dropdown-menu {
+ display: block;
+ }
+ .nav-collapse .dropdown-menu:before,
+ .nav-collapse .dropdown-menu:after {
+ display: none;
+ }
+ .nav-collapse .dropdown-menu .divider {
+ display: none;
+ }
+ .nav-collapse .nav > li > .dropdown-menu:before,
+ .nav-collapse .nav > li > .dropdown-menu:after {
+ display: none;
+ }
+ .nav-collapse .navbar-form,
+ .nav-collapse .navbar-search {
+ float: none;
+ padding: 10px 15px;
+ margin: 10px 0;
+ border-top: 1px solid #f2f2f2;
+ border-bottom: 1px solid #f2f2f2;
+ -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1);
+ -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1);
+ box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1);
+ }
+ .navbar-inverse .nav-collapse .navbar-form,
+ .navbar-inverse .nav-collapse .navbar-search {
+ border-top-color: #111111;
+ border-bottom-color: #111111;
+ }
+ .navbar .nav-collapse .nav.pull-right {
+ float: none;
+ margin-left: 0;
+ }
+ .nav-collapse,
+ .nav-collapse.collapse {
+ overflow: hidden;
+ height: 0;
+ }
+ .navbar .btn-navbar {
+ display: block;
+ }
+ .navbar-static .navbar-inner {
+ padding-left: 10px;
+ padding-right: 10px;
+ }
+}
+@media (min-width: 980px) {
+ .nav-collapse.collapse {
+ height: auto !important;
+ overflow: visible !important;
+ }
+}
Added: tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/nuiton-js-bootstrap/img/glyphicons-halflings-white.png
===================================================================
(Binary files differ)
Property changes on: tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/nuiton-js-bootstrap/img/glyphicons-halflings-white.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Added: tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/nuiton-js-bootstrap/img/glyphicons-halflings.png
===================================================================
(Binary files differ)
Property changes on: tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/nuiton-js-bootstrap/img/glyphicons-halflings.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Added: tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/nuiton-js-bootstrap/js/bootstrap.js
===================================================================
--- tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/nuiton-js-bootstrap/js/bootstrap.js (rev 0)
+++ tags/nuiton-js-bootstrap-2.3.2-1/src/main/resources/nuiton-js-bootstrap/js/bootstrap.js 2013-06-17 15:04:40 UTC (rev 90)
@@ -0,0 +1,2291 @@
+/* ===================================================
+ * bootstrap-transition.js v2.3.2
+ * http://twitter.github.com/bootstrap/javascript.html#transitions
+ * ===================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================== */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* CSS TRANSITION SUPPORT (http://www.modernizr.com/)
+ * ======================================================= */
+
+ $(function () {
+
+ $.support.transition = (function () {
+
+ var transitionEnd = (function () {
+
+ var el = document.createElement('bootstrap')
+ , transEndEventNames = {
+ 'WebkitTransition' : 'webkitTransitionEnd'
+ , 'MozTransition' : 'transitionend'
+ , 'OTransition' : 'oTransitionEnd otransitionend'
+ , 'transition' : 'transitionend'
+ }
+ , name
+
+ for (name in transEndEventNames){
+ if (el.style[name] !== undefined) {
+ return transEndEventNames[name]
+ }
+ }
+
+ }())
+
+ return transitionEnd && {
+ end: transitionEnd
+ }
+
+ })()
+
+ })
+
+}(window.jQuery);
+/* =========================================================
+ * bootstrap-modal.js v2.3.2
+ * http://twitter.github.com/bootstrap/javascript.html#modals
+ * =========================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================= */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* MODAL CLASS DEFINITION
+ * ====================== */
+
+ var Modal = function (element, options) {
+ this.options = options
+ this.$element = $(element)
+ .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this))
+ this.options.remote && this.$element.find('.modal-body').load(this.options.remote)
+ }
+
+ Modal.prototype = {
+
+ constructor: Modal
+
+ , toggle: function () {
+ return this[!this.isShown ? 'show' : 'hide']()
+ }
+
+ , show: function () {
+ var that = this
+ , e = $.Event('show')
+
+ this.$element.trigger(e)
+
+ if (this.isShown || e.isDefaultPrevented()) return
+
+ this.isShown = true
+
+ this.escape()
+
+ this.backdrop(function () {
+ var transition = $.support.transition && that.$element.hasClass('fade')
+
+ if (!that.$element.parent().length) {
+ that.$element.appendTo(document.body) //don't move modals dom position
+ }
+
+ that.$element.show()
+
+ if (transition) {
+ that.$element[0].offsetWidth // force reflow
+ }
+
+ that.$element
+ .addClass('in')
+ .attr('aria-hidden', false)
+
+ that.enforceFocus()
+
+ transition ?
+ that.$element.one($.support.transition.end, function () { that.$element.focus().trigger('shown') }) :
+ that.$element.focus().trigger('shown')
+
+ })
+ }
+
+ , hide: function (e) {
+ e && e.preventDefault()
+
+ var that = this
+
+ e = $.Event('hide')
+
+ this.$element.trigger(e)
+
+ if (!this.isShown || e.isDefaultPrevented()) return
+
+ this.isShown = false
+
+ this.escape()
+
+ $(document).off('focusin.modal')
+
+ this.$element
+ .removeClass('in')
+ .attr('aria-hidden', true)
+
+ $.support.transition && this.$element.hasClass('fade') ?
+ this.hideWithTransition() :
+ this.hideModal()
+ }
+
+ , enforceFocus: function () {
+ var that = this
+ $(document).on('focusin.modal', function (e) {
+ if (that.$element[0] !== e.target && !that.$element.has(e.target).length) {
+ that.$element.focus()
+ }
+ })
+ }
+
+ , escape: function () {
+ var that = this
+ if (this.isShown && this.options.keyboard) {
+ this.$element.on('keyup.dismiss.modal', function ( e ) {
+ e.which == 27 && that.hide()
+ })
+ } else if (!this.isShown) {
+ this.$element.off('keyup.dismiss.modal')
+ }
+ }
+
+ , hideWithTransition: function () {
+ var that = this
+ , timeout = setTimeout(function () {
+ that.$element.off($.support.transition.end)
+ that.hideModal()
+ }, 500)
+
+ this.$element.one($.support.transition.end, function () {
+ clearTimeout(timeout)
+ that.hideModal()
+ })
+ }
+
+ , hideModal: function () {
+ var that = this
+ this.$element.hide()
+ this.backdrop(function () {
+ that.removeBackdrop()
+ that.$element.trigger('hidden')
+ })
+ }
+
+ , removeBackdrop: function () {
+ this.$backdrop && this.$backdrop.remove()
+ this.$backdrop = null
+ }
+
+ , backdrop: function (callback) {
+ var that = this
+ , animate = this.$element.hasClass('fade') ? 'fade' : ''
+
+ if (this.isShown && this.options.backdrop) {
+ var doAnimate = $.support.transition && animate
+
+ this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
+ .appendTo(document.body)
+
+ this.$backdrop.click(
+ this.options.backdrop == 'static' ?
+ $.proxy(this.$element[0].focus, this.$element[0])
+ : $.proxy(this.hide, this)
+ )
+
+ if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
+
+ this.$backdrop.addClass('in')
+
+ if (!callback) return
+
+ doAnimate ?
+ this.$backdrop.one($.support.transition.end, callback) :
+ callback()
+
+ } else if (!this.isShown && this.$backdrop) {
+ this.$backdrop.removeClass('in')
+
+ $.support.transition && this.$element.hasClass('fade')?
+ this.$backdrop.one($.support.transition.end, callback) :
+ callback()
+
+ } else if (callback) {
+ callback()
+ }
+ }
+ }
+
+
+ /* MODAL PLUGIN DEFINITION
+ * ======================= */
+
+ var old = $.fn.modal
+
+ $.fn.modal = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('modal')
+ , options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option)
+ if (!data) $this.data('modal', (data = new Modal(this, options)))
+ if (typeof option == 'string') data[option]()
+ else if (options.show) data.show()
+ })
+ }
+
+ $.fn.modal.defaults = {
+ backdrop: true
+ , keyboard: true
+ , show: true
+ }
+
+ $.fn.modal.Constructor = Modal
+
+
+ /* MODAL NO CONFLICT
+ * ================= */
+
+ $.fn.modal.noConflict = function () {
+ $.fn.modal = old
+ return this
+ }
+
+
+ /* MODAL DATA-API
+ * ============== */
+
+ $(document).on('click.modal.data-api', '[data-toggle="modal"]', function (e) {
+ var $this = $(this)
+ , href = $this.attr('href')
+ , $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) //strip for ie7
+ , option = $target.data('modal') ? 'toggle' : $.extend({ remote:!/#/.test(href) && href }, $target.data(), $this.data())
+
+ e.preventDefault()
+
+ $target
+ .modal(option)
+ .one('hide', function () {
+ $this.focus()
+ })
+ })
+
+}(window.jQuery);
+
+/* ============================================================
+ * bootstrap-dropdown.js v2.3.2
+ * http://twitter.github.com/bootstrap/javascript.html#dropdowns
+ * ============================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============================================================ */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* DROPDOWN CLASS DEFINITION
+ * ========================= */
+
+ var toggle = '[data-toggle=dropdown]'
+ , Dropdown = function (element) {
+ var $el = $(element).on('click.dropdown.data-api', this.toggle)
+ $('html').on('click.dropdown.data-api', function () {
+ $el.parent().removeClass('open')
+ })
+ }
+
+ Dropdown.prototype = {
+
+ constructor: Dropdown
+
+ , toggle: function (e) {
+ var $this = $(this)
+ , $parent
+ , isActive
+
+ if ($this.is('.disabled, :disabled')) return
+
+ $parent = getParent($this)
+
+ isActive = $parent.hasClass('open')
+
+ clearMenus()
+
+ if (!isActive) {
+ if ('ontouchstart' in document.documentElement) {
+ // if mobile we we use a backdrop because click events don't delegate
+ $('<div class="dropdown-backdrop"/>').insertBefore($(this)).on('click', clearMenus)
+ }
+ $parent.toggleClass('open')
+ }
+
+ $this.focus()
+
+ return false
+ }
+
+ , keydown: function (e) {
+ var $this
+ , $items
+ , $active
+ , $parent
+ , isActive
+ , index
+
+ if (!/(38|40|27)/.test(e.keyCode)) return
+
+ $this = $(this)
+
+ e.preventDefault()
+ e.stopPropagation()
+
+ if ($this.is('.disabled, :disabled')) return
+
+ $parent = getParent($this)
+
+ isActive = $parent.hasClass('open')
+
+ if (!isActive || (isActive && e.keyCode == 27)) {
+ if (e.which == 27) $parent.find(toggle).focus()
+ return $this.click()
+ }
+
+ $items = $('[role=menu] li:not(.divider):visible a', $parent)
+
+ if (!$items.length) return
+
+ index = $items.index($items.filter(':focus'))
+
+ if (e.keyCode == 38 && index > 0) index-- // up
+ if (e.keyCode == 40 && index < $items.length - 1) index++ // down
+ if (!~index) index = 0
+
+ $items
+ .eq(index)
+ .focus()
+ }
+
+ }
+
+ function clearMenus() {
+ $('.dropdown-backdrop').remove()
+ $(toggle).each(function () {
+ getParent($(this)).removeClass('open')
+ })
+ }
+
+ function getParent($this) {
+ var selector = $this.attr('data-target')
+ , $parent
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+ }
+
+ $parent = selector && $(selector)
+
+ if (!$parent || !$parent.length) $parent = $this.parent()
+
+ return $parent
+ }
+
+
+ /* DROPDOWN PLUGIN DEFINITION
+ * ========================== */
+
+ var old = $.fn.dropdown
+
+ $.fn.dropdown = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('dropdown')
+ if (!data) $this.data('dropdown', (data = new Dropdown(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+
+ $.fn.dropdown.Constructor = Dropdown
+
+
+ /* DROPDOWN NO CONFLICT
+ * ==================== */
+
+ $.fn.dropdown.noConflict = function () {
+ $.fn.dropdown = old
+ return this
+ }
+
+
+ /* APPLY TO STANDARD DROPDOWN ELEMENTS
+ * =================================== */
+
+ $(document)
+ .on('click.dropdown.data-api', clearMenus)
+ .on('click.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
+ .on('click.dropdown.data-api' , toggle, Dropdown.prototype.toggle)
+ .on('keydown.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
+
+}(window.jQuery);
+
+/* =============================================================
+ * bootstrap-scrollspy.js v2.3.2
+ * http://twitter.github.com/bootstrap/javascript.html#scrollspy
+ * =============================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============================================================== */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* SCROLLSPY CLASS DEFINITION
+ * ========================== */
+
+ function ScrollSpy(element, options) {
+ var process = $.proxy(this.process, this)
+ , $element = $(element).is('body') ? $(window) : $(element)
+ , href
+ this.options = $.extend({}, $.fn.scrollspy.defaults, options)
+ this.$scrollElement = $element.on('scroll.scroll-spy.data-api', process)
+ this.selector = (this.options.target
+ || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
+ || '') + ' .nav li > a'
+ this.$body = $('body')
+ this.refresh()
+ this.process()
+ }
+
+ ScrollSpy.prototype = {
+
+ constructor: ScrollSpy
+
+ , refresh: function () {
+ var self = this
+ , $targets
+
+ this.offsets = $([])
+ this.targets = $([])
+
+ $targets = this.$body
+ .find(this.selector)
+ .map(function () {
+ var $el = $(this)
+ , href = $el.data('target') || $el.attr('href')
+ , $href = /^#\w/.test(href) && $(href)
+ return ( $href
+ && $href.length
+ && [[ $href.position().top + (!$.isWindow(self.$scrollElement.get(0)) && self.$scrollElement.scrollTop()), href ]] ) || null
+ })
+ .sort(function (a, b) { return a[0] - b[0] })
+ .each(function () {
+ self.offsets.push(this[0])
+ self.targets.push(this[1])
+ })
+ }
+
+ , process: function () {
+ var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
+ , scrollHeight = this.$scrollElement[0].scrollHeight || this.$body[0].scrollHeight
+ , maxScroll = scrollHeight - this.$scrollElement.height()
+ , offsets = this.offsets
+ , targets = this.targets
+ , activeTarget = this.activeTarget
+ , i
+
+ if (scrollTop >= maxScroll) {
+ return activeTarget != (i = targets.last()[0])
+ && this.activate ( i )
+ }
+
+ for (i = offsets.length; i--;) {
+ activeTarget != targets[i]
+ && scrollTop >= offsets[i]
+ && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
+ && this.activate( targets[i] )
+ }
+ }
+
+ , activate: function (target) {
+ var active
+ , selector
+
+ this.activeTarget = target
+
+ $(this.selector)
+ .parent('.active')
+ .removeClass('active')
+
+ selector = this.selector
+ + '[data-target="' + target + '"],'
+ + this.selector + '[href="' + target + '"]'
+
+ active = $(selector)
+ .parent('li')
+ .addClass('active')
+
+ if (active.parent('.dropdown-menu').length) {
+ active = active.closest('li.dropdown').addClass('active')
+ }
+
+ active.trigger('activate')
+ }
+
+ }
+
+
+ /* SCROLLSPY PLUGIN DEFINITION
+ * =========================== */
+
+ var old = $.fn.scrollspy
+
+ $.fn.scrollspy = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('scrollspy')
+ , options = typeof option == 'object' && option
+ if (!data) $this.data('scrollspy', (data = new ScrollSpy(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.scrollspy.Constructor = ScrollSpy
+
+ $.fn.scrollspy.defaults = {
+ offset: 10
+ }
+
+
+ /* SCROLLSPY NO CONFLICT
+ * ===================== */
+
+ $.fn.scrollspy.noConflict = function () {
+ $.fn.scrollspy = old
+ return this
+ }
+
+
+ /* SCROLLSPY DATA-API
+ * ================== */
+
+ $(window).on('load', function () {
+ $('[data-spy="scroll"]').each(function () {
+ var $spy = $(this)
+ $spy.scrollspy($spy.data())
+ })
+ })
+
+}(window.jQuery);
+/* ========================================================
+ * bootstrap-tab.js v2.3.2
+ * http://twitter.github.com/bootstrap/javascript.html#tabs
+ * ========================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================== */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* TAB CLASS DEFINITION
+ * ==================== */
+
+ var Tab = function (element) {
+ this.element = $(element)
+ }
+
+ Tab.prototype = {
+
+ constructor: Tab
+
+ , show: function () {
+ var $this = this.element
+ , $ul = $this.closest('ul:not(.dropdown-menu)')
+ , selector = $this.attr('data-target')
+ , previous
+ , $target
+ , e
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+ }
+
+ if ( $this.parent('li').hasClass('active') ) return
+
+ previous = $ul.find('.active:last a')[0]
+
+ e = $.Event('show', {
+ relatedTarget: previous
+ })
+
+ $this.trigger(e)
+
+ if (e.isDefaultPrevented()) return
+
+ $target = $(selector)
+
+ this.activate($this.parent('li'), $ul)
+ this.activate($target, $target.parent(), function () {
+ $this.trigger({
+ type: 'shown'
+ , relatedTarget: previous
+ })
+ })
+ }
+
+ , activate: function ( element, container, callback) {
+ var $active = container.find('> .active')
+ , transition = callback
+ && $.support.transition
+ && $active.hasClass('fade')
+
+ function next() {
+ $active
+ .removeClass('active')
+ .find('> .dropdown-menu > .active')
+ .removeClass('active')
+
+ element.addClass('active')
+
+ if (transition) {
+ element[0].offsetWidth // reflow for transition
+ element.addClass('in')
+ } else {
+ element.removeClass('fade')
+ }
+
+ if ( element.parent('.dropdown-menu') ) {
+ element.closest('li.dropdown').addClass('active')
+ }
+
+ callback && callback()
+ }
+
+ transition ?
+ $active.one($.support.transition.end, next) :
+ next()
+
+ $active.removeClass('in')
+ }
+ }
+
+
+ /* TAB PLUGIN DEFINITION
+ * ===================== */
+
+ var old = $.fn.tab
+
+ $.fn.tab = function ( option ) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('tab')
+ if (!data) $this.data('tab', (data = new Tab(this)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.tab.Constructor = Tab
+
+
+ /* TAB NO CONFLICT
+ * =============== */
+
+ $.fn.tab.noConflict = function () {
+ $.fn.tab = old
+ return this
+ }
+
+
+ /* TAB DATA-API
+ * ============ */
+
+ $(document).on('click.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
+ e.preventDefault()
+ $(this).tab('show')
+ })
+
+}(window.jQuery);
+/* ===========================================================
+ * bootstrap-tooltip.js v2.3.2
+ * http://twitter.github.com/bootstrap/javascript.html#tooltips
+ * Inspired by the original jQuery.tipsy by Jason Frame
+ * ===========================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================== */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* TOOLTIP PUBLIC CLASS DEFINITION
+ * =============================== */
+
+ var Tooltip = function (element, options) {
+ this.init('tooltip', element, options)
+ }
+
+ Tooltip.prototype = {
+
+ constructor: Tooltip
+
+ , init: function (type, element, options) {
+ var eventIn
+ , eventOut
+ , triggers
+ , trigger
+ , i
+
+ this.type = type
+ this.$element = $(element)
+ this.options = this.getOptions(options)
+ this.enabled = true
+
+ triggers = this.options.trigger.split(' ')
+
+ for (i = triggers.length; i--;) {
+ trigger = triggers[i]
+ if (trigger == 'click') {
+ this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
+ } else if (trigger != 'manual') {
+ eventIn = trigger == 'hover' ? 'mouseenter' : 'focus'
+ eventOut = trigger == 'hover' ? 'mouseleave' : 'blur'
+ this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
+ this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
+ }
+ }
+
+ this.options.selector ?
+ (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
+ this.fixTitle()
+ }
+
+ , getOptions: function (options) {
+ options = $.extend({}, $.fn[this.type].defaults, this.$element.data(), options)
+
+ if (options.delay && typeof options.delay == 'number') {
+ options.delay = {
+ show: options.delay
+ , hide: options.delay
+ }
+ }
+
+ return options
+ }
+
+ , enter: function (e) {
+ var defaults = $.fn[this.type].defaults
+ , options = {}
+ , self
+
+ this._options && $.each(this._options, function (key, value) {
+ if (defaults[key] != value) options[key] = value
+ }, this)
+
+ self = $(e.currentTarget)[this.type](options).data(this.type)
+
+ if (!self.options.delay || !self.options.delay.show) return self.show()
+
+ clearTimeout(this.timeout)
+ self.hoverState = 'in'
+ this.timeout = setTimeout(function() {
+ if (self.hoverState == 'in') self.show()
+ }, self.options.delay.show)
+ }
+
+ , leave: function (e) {
+ var self = $(e.currentTarget)[this.type](this._options).data(this.type)
+
+ if (this.timeout) clearTimeout(this.timeout)
+ if (!self.options.delay || !self.options.delay.hide) return self.hide()
+
+ self.hoverState = 'out'
+ this.timeout = setTimeout(function() {
+ if (self.hoverState == 'out') self.hide()
+ }, self.options.delay.hide)
+ }
+
+ , show: function () {
+ var $tip
+ , pos
+ , actualWidth
+ , actualHeight
+ , placement
+ , tp
+ , e = $.Event('show')
+
+ if (this.hasContent() && this.enabled) {
+ this.$element.trigger(e)
+ if (e.isDefaultPrevented()) return
+ $tip = this.tip()
+ this.setContent()
+
+ if (this.options.animation) {
+ $tip.addClass('fade')
+ }
+
+ placement = typeof this.options.placement == 'function' ?
+ this.options.placement.call(this, $tip[0], this.$element[0]) :
+ this.options.placement
+
+ $tip
+ .detach()
+ .css({ top: 0, left: 0, display: 'block' })
+
+ this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
+
+ pos = this.getPosition()
+
+ actualWidth = $tip[0].offsetWidth
+ actualHeight = $tip[0].offsetHeight
+
+ switch (placement) {
+ case 'bottom':
+ tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
+ break
+ case 'top':
+ tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}
+ break
+ case 'left':
+ tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}
+ break
+ case 'right':
+ tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}
+ break
+ }
+
+ this.applyPlacement(tp, placement)
+ this.$element.trigger('shown')
+ }
+ }
+
+ , applyPlacement: function(offset, placement){
+ var $tip = this.tip()
+ , width = $tip[0].offsetWidth
+ , height = $tip[0].offsetHeight
+ , actualWidth
+ , actualHeight
+ , delta
+ , replace
+
+ $tip
+ .offset(offset)
+ .addClass(placement)
+ .addClass('in')
+
+ actualWidth = $tip[0].offsetWidth
+ actualHeight = $tip[0].offsetHeight
+
+ if (placement == 'top' && actualHeight != height) {
+ offset.top = offset.top + height - actualHeight
+ replace = true
+ }
+
+ if (placement == 'bottom' || placement == 'top') {
+ delta = 0
+
+ if (offset.left < 0){
+ delta = offset.left * -2
+ offset.left = 0
+ $tip.offset(offset)
+ actualWidth = $tip[0].offsetWidth
+ actualHeight = $tip[0].offsetHeight
+ }
+
+ this.replaceArrow(delta - width + actualWidth, actualWidth, 'left')
+ } else {
+ this.replaceArrow(actualHeight - height, actualHeight, 'top')
+ }
+
+ if (replace) $tip.offset(offset)
+ }
+
+ , replaceArrow: function(delta, dimension, position){
+ this
+ .arrow()
+ .css(position, delta ? (50 * (1 - delta / dimension) + "%") : '')
+ }
+
+ , setContent: function () {
+ var $tip = this.tip()
+ , title = this.getTitle()
+
+ $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
+ $tip.removeClass('fade in top bottom left right')
+ }
+
+ , hide: function () {
+ var that = this
+ , $tip = this.tip()
+ , e = $.Event('hide')
+
+ this.$element.trigger(e)
+ if (e.isDefaultPrevented()) return
+
+ $tip.removeClass('in')
+
+ function removeWithAnimation() {
+ var timeout = setTimeout(function () {
+ $tip.off($.support.transition.end).detach()
+ }, 500)
+
+ $tip.one($.support.transition.end, function () {
+ clearTimeout(timeout)
+ $tip.detach()
+ })
+ }
+
+ $.support.transition && this.$tip.hasClass('fade') ?
+ removeWithAnimation() :
+ $tip.detach()
+
+ this.$element.trigger('hidden')
+
+ return this
+ }
+
+ , fixTitle: function () {
+ var $e = this.$element
+ if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
+ $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
+ }
+ }
+
+ , hasContent: function () {
+ return this.getTitle()
+ }
+
+ , getPosition: function () {
+ var el = this.$element[0]
+ return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : {
+ width: el.offsetWidth
+ , height: el.offsetHeight
+ }, this.$element.offset())
+ }
+
+ , getTitle: function () {
+ var title
+ , $e = this.$element
+ , o = this.options
+
+ title = $e.attr('data-original-title')
+ || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
+
+ return title
+ }
+
+ , tip: function () {
+ return this.$tip = this.$tip || $(this.options.template)
+ }
+
+ , arrow: function(){
+ return this.$arrow = this.$arrow || this.tip().find(".tooltip-arrow")
+ }
+
+ , validate: function () {
+ if (!this.$element[0].parentNode) {
+ this.hide()
+ this.$element = null
+ this.options = null
+ }
+ }
+
+ , enable: function () {
+ this.enabled = true
+ }
+
+ , disable: function () {
+ this.enabled = false
+ }
+
+ , toggleEnabled: function () {
+ this.enabled = !this.enabled
+ }
+
+ , toggle: function (e) {
+ var self = e ? $(e.currentTarget)[this.type](this._options).data(this.type) : this
+ self.tip().hasClass('in') ? self.hide() : self.show()
+ }
+
+ , destroy: function () {
+ this.hide().$element.off('.' + this.type).removeData(this.type)
+ }
+
+ }
+
+
+ /* TOOLTIP PLUGIN DEFINITION
+ * ========================= */
+
+ var old = $.fn.tooltip
+
+ $.fn.tooltip = function ( option ) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('tooltip')
+ , options = typeof option == 'object' && option
+ if (!data) $this.data('tooltip', (data = new Tooltip(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.tooltip.Constructor = Tooltip
+
+ $.fn.tooltip.defaults = {
+ animation: true
+ , placement: 'top'
+ , selector: false
+ , template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
+ , trigger: 'hover focus'
+ , title: ''
+ , delay: 0
+ , html: false
+ , container: false
+ }
+
+
+ /* TOOLTIP NO CONFLICT
+ * =================== */
+
+ $.fn.tooltip.noConflict = function () {
+ $.fn.tooltip = old
+ return this
+ }
+
+}(window.jQuery);
+
+/* ===========================================================
+ * bootstrap-popover.js v2.3.2
+ * http://twitter.github.com/bootstrap/javascript.html#popovers
+ * ===========================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * =========================================================== */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* POPOVER PUBLIC CLASS DEFINITION
+ * =============================== */
+
+ var Popover = function (element, options) {
+ this.init('popover', element, options)
+ }
+
+
+ /* NOTE: POPOVER EXTENDS BOOTSTRAP-TOOLTIP.js
+ ========================================== */
+
+ Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype, {
+
+ constructor: Popover
+
+ , setContent: function () {
+ var $tip = this.tip()
+ , title = this.getTitle()
+ , content = this.getContent()
+
+ $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
+ $tip.find('.popover-content')[this.options.html ? 'html' : 'text'](content)
+
+ $tip.removeClass('fade top bottom left right in')
+ }
+
+ , hasContent: function () {
+ return this.getTitle() || this.getContent()
+ }
+
+ , getContent: function () {
+ var content
+ , $e = this.$element
+ , o = this.options
+
+ content = (typeof o.content == 'function' ? o.content.call($e[0]) : o.content)
+ || $e.attr('data-content')
+
+ return content
+ }
+
+ , tip: function () {
+ if (!this.$tip) {
+ this.$tip = $(this.options.template)
+ }
+ return this.$tip
+ }
+
+ , destroy: function () {
+ this.hide().$element.off('.' + this.type).removeData(this.type)
+ }
+
+ })
+
+
+ /* POPOVER PLUGIN DEFINITION
+ * ======================= */
+
+ var old = $.fn.popover
+
+ $.fn.popover = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('popover')
+ , options = typeof option == 'object' && option
+ if (!data) $this.data('popover', (data = new Popover(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.popover.Constructor = Popover
+
+ $.fn.popover.defaults = $.extend({} , $.fn.tooltip.defaults, {
+ placement: 'right'
+ , trigger: 'click'
+ , content: ''
+ , template: '<div class="popover"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
+ })
+
+
+ /* POPOVER NO CONFLICT
+ * =================== */
+
+ $.fn.popover.noConflict = function () {
+ $.fn.popover = old
+ return this
+ }
+
+}(window.jQuery);
+
+/* ==========================================================
+ * bootstrap-affix.js v2.3.2
+ * http://twitter.github.com/bootstrap/javascript.html#affix
+ * ==========================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================== */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* AFFIX CLASS DEFINITION
+ * ====================== */
+
+ var Affix = function (element, options) {
+ this.options = $.extend({}, $.fn.affix.defaults, options)
+ this.$window = $(window)
+ .on('scroll.affix.data-api', $.proxy(this.checkPosition, this))
+ .on('click.affix.data-api', $.proxy(function () { setTimeout($.proxy(this.checkPosition, this), 1) }, this))
+ this.$element = $(element)
+ this.checkPosition()
+ }
+
+ Affix.prototype.checkPosition = function () {
+ if (!this.$element.is(':visible')) return
+
+ var scrollHeight = $(document).height()
+ , scrollTop = this.$window.scrollTop()
+ , position = this.$element.offset()
+ , offset = this.options.offset
+ , offsetBottom = offset.bottom
+ , offsetTop = offset.top
+ , reset = 'affix affix-top affix-bottom'
+ , affix
+
+ if (typeof offset != 'object') offsetBottom = offsetTop = offset
+ if (typeof offsetTop == 'function') offsetTop = offset.top()
+ if (typeof offsetBottom == 'function') offsetBottom = offset.bottom()
+
+ affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ?
+ false : offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ?
+ 'bottom' : offsetTop != null && scrollTop <= offsetTop ?
+ 'top' : false
+
+ if (this.affixed === affix) return
+
+ this.affixed = affix
+ this.unpin = affix == 'bottom' ? position.top - scrollTop : null
+
+ this.$element.removeClass(reset).addClass('affix' + (affix ? '-' + affix : ''))
+ }
+
+
+ /* AFFIX PLUGIN DEFINITION
+ * ======================= */
+
+ var old = $.fn.affix
+
+ $.fn.affix = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('affix')
+ , options = typeof option == 'object' && option
+ if (!data) $this.data('affix', (data = new Affix(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.affix.Constructor = Affix
+
+ $.fn.affix.defaults = {
+ offset: 0
+ }
+
+
+ /* AFFIX NO CONFLICT
+ * ================= */
+
+ $.fn.affix.noConflict = function () {
+ $.fn.affix = old
+ return this
+ }
+
+
+ /* AFFIX DATA-API
+ * ============== */
+
+ $(window).on('load', function () {
+ $('[data-spy="affix"]').each(function () {
+ var $spy = $(this)
+ , data = $spy.data()
+
+ data.offset = data.offset || {}
+
+ data.offsetBottom && (data.offset.bottom = data.offsetBottom)
+ data.offsetTop && (data.offset.top = data.offsetTop)
+
+ $spy.affix(data)
+ })
+ })
+
+
+}(window.jQuery);
+/* ==========================================================
+ * bootstrap-alert.js v2.3.2
+ * http://twitter.github.com/bootstrap/javascript.html#alerts
+ * ==========================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================== */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* ALERT CLASS DEFINITION
+ * ====================== */
+
+ var dismiss = '[data-dismiss="alert"]'
+ , Alert = function (el) {
+ $(el).on('click', dismiss, this.close)
+ }
+
+ Alert.prototype.close = function (e) {
+ var $this = $(this)
+ , selector = $this.attr('data-target')
+ , $parent
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+ }
+
+ $parent = $(selector)
+
+ e && e.preventDefault()
+
+ $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent())
+
+ $parent.trigger(e = $.Event('close'))
+
+ if (e.isDefaultPrevented()) return
+
+ $parent.removeClass('in')
+
+ function removeElement() {
+ $parent
+ .trigger('closed')
+ .remove()
+ }
+
+ $.support.transition && $parent.hasClass('fade') ?
+ $parent.on($.support.transition.end, removeElement) :
+ removeElement()
+ }
+
+
+ /* ALERT PLUGIN DEFINITION
+ * ======================= */
+
+ var old = $.fn.alert
+
+ $.fn.alert = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('alert')
+ if (!data) $this.data('alert', (data = new Alert(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+
+ $.fn.alert.Constructor = Alert
+
+
+ /* ALERT NO CONFLICT
+ * ================= */
+
+ $.fn.alert.noConflict = function () {
+ $.fn.alert = old
+ return this
+ }
+
+
+ /* ALERT DATA-API
+ * ============== */
+
+ $(document).on('click.alert.data-api', dismiss, Alert.prototype.close)
+
+}(window.jQuery);
+/* ============================================================
+ * bootstrap-button.js v2.3.2
+ * http://twitter.github.com/bootstrap/javascript.html#buttons
+ * ============================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============================================================ */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* BUTTON PUBLIC CLASS DEFINITION
+ * ============================== */
+
+ var Button = function (element, options) {
+ this.$element = $(element)
+ this.options = $.extend({}, $.fn.button.defaults, options)
+ }
+
+ Button.prototype.setState = function (state) {
+ var d = 'disabled'
+ , $el = this.$element
+ , data = $el.data()
+ , val = $el.is('input') ? 'val' : 'html'
+
+ state = state + 'Text'
+ data.resetText || $el.data('resetText', $el[val]())
+
+ $el[val](data[state] || this.options[state])
+
+ // push to event loop to allow forms to submit
+ setTimeout(function () {
+ state == 'loadingText' ?
+ $el.addClass(d).attr(d, d) :
+ $el.removeClass(d).removeAttr(d)
+ }, 0)
+ }
+
+ Button.prototype.toggle = function () {
+ var $parent = this.$element.closest('[data-toggle="buttons-radio"]')
+
+ $parent && $parent
+ .find('.active')
+ .removeClass('active')
+
+ this.$element.toggleClass('active')
+ }
+
+
+ /* BUTTON PLUGIN DEFINITION
+ * ======================== */
+
+ var old = $.fn.button
+
+ $.fn.button = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('button')
+ , options = typeof option == 'object' && option
+ if (!data) $this.data('button', (data = new Button(this, options)))
+ if (option == 'toggle') data.toggle()
+ else if (option) data.setState(option)
+ })
+ }
+
+ $.fn.button.defaults = {
+ loadingText: 'loading...'
+ }
+
+ $.fn.button.Constructor = Button
+
+
+ /* BUTTON NO CONFLICT
+ * ================== */
+
+ $.fn.button.noConflict = function () {
+ $.fn.button = old
+ return this
+ }
+
+
+ /* BUTTON DATA-API
+ * =============== */
+
+ $(document).on('click.button.data-api', '[data-toggle^=button]', function (e) {
+ var $btn = $(e.target)
+ if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
+ $btn.button('toggle')
+ })
+
+}(window.jQuery);
+/* =============================================================
+ * bootstrap-collapse.js v2.3.2
+ * http://twitter.github.com/bootstrap/javascript.html#collapse
+ * =============================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============================================================ */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* COLLAPSE PUBLIC CLASS DEFINITION
+ * ================================ */
+
+ var Collapse = function (element, options) {
+ this.$element = $(element)
+ this.options = $.extend({}, $.fn.collapse.defaults, options)
+
+ if (this.options.parent) {
+ this.$parent = $(this.options.parent)
+ }
+
+ this.options.toggle && this.toggle()
+ }
+
+ Collapse.prototype = {
+
+ constructor: Collapse
+
+ , dimension: function () {
+ var hasWidth = this.$element.hasClass('width')
+ return hasWidth ? 'width' : 'height'
+ }
+
+ , show: function () {
+ var dimension
+ , scroll
+ , actives
+ , hasData
+
+ if (this.transitioning || this.$element.hasClass('in')) return
+
+ dimension = this.dimension()
+ scroll = $.camelCase(['scroll', dimension].join('-'))
+ actives = this.$parent && this.$parent.find('> .accordion-group > .in')
+
+ if (actives && actives.length) {
+ hasData = actives.data('collapse')
+ if (hasData && hasData.transitioning) return
+ actives.collapse('hide')
+ hasData || actives.data('collapse', null)
+ }
+
+ this.$element[dimension](0)
+ this.transition('addClass', $.Event('show'), 'shown')
+ $.support.transition && this.$element[dimension](this.$element[0][scroll])
+ }
+
+ , hide: function () {
+ var dimension
+ if (this.transitioning || !this.$element.hasClass('in')) return
+ dimension = this.dimension()
+ this.reset(this.$element[dimension]())
+ this.transition('removeClass', $.Event('hide'), 'hidden')
+ this.$element[dimension](0)
+ }
+
+ , reset: function (size) {
+ var dimension = this.dimension()
+
+ this.$element
+ .removeClass('collapse')
+ [dimension](size || 'auto')
+ [0].offsetWidth
+
+ this.$element[size !== null ? 'addClass' : 'removeClass']('collapse')
+
+ return this
+ }
+
+ , transition: function (method, startEvent, completeEvent) {
+ var that = this
+ , complete = function () {
+ if (startEvent.type == 'show') that.reset()
+ that.transitioning = 0
+ that.$element.trigger(completeEvent)
+ }
+
+ this.$element.trigger(startEvent)
+
+ if (startEvent.isDefaultPrevented()) return
+
+ this.transitioning = 1
+
+ this.$element[method]('in')
+
+ $.support.transition && this.$element.hasClass('collapse') ?
+ this.$element.one($.support.transition.end, complete) :
+ complete()
+ }
+
+ , toggle: function () {
+ this[this.$element.hasClass('in') ? 'hide' : 'show']()
+ }
+
+ }
+
+
+ /* COLLAPSE PLUGIN DEFINITION
+ * ========================== */
+
+ var old = $.fn.collapse
+
+ $.fn.collapse = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('collapse')
+ , options = $.extend({}, $.fn.collapse.defaults, $this.data(), typeof option == 'object' && option)
+ if (!data) $this.data('collapse', (data = new Collapse(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.collapse.defaults = {
+ toggle: true
+ }
+
+ $.fn.collapse.Constructor = Collapse
+
+
+ /* COLLAPSE NO CONFLICT
+ * ==================== */
+
+ $.fn.collapse.noConflict = function () {
+ $.fn.collapse = old
+ return this
+ }
+
+
+ /* COLLAPSE DATA-API
+ * ================= */
+
+ $(document).on('click.collapse.data-api', '[data-toggle=collapse]', function (e) {
+ var $this = $(this), href
+ , target = $this.attr('data-target')
+ || e.preventDefault()
+ || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
+ , option = $(target).data('collapse') ? 'toggle' : $this.data()
+ $this[$(target).hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
+ $(target).collapse(option)
+ })
+
+}(window.jQuery);
+/* ==========================================================
+ * bootstrap-carousel.js v2.3.2
+ * http://twitter.github.com/bootstrap/javascript.html#carousel
+ * ==========================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================== */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* CAROUSEL CLASS DEFINITION
+ * ========================= */
+
+ var Carousel = function (element, options) {
+ this.$element = $(element)
+ this.$indicators = this.$element.find('.carousel-indicators')
+ this.options = options
+ this.options.pause == 'hover' && this.$element
+ .on('mouseenter', $.proxy(this.pause, this))
+ .on('mouseleave', $.proxy(this.cycle, this))
+ }
+
+ Carousel.prototype = {
+
+ cycle: function (e) {
+ if (!e) this.paused = false
+ if (this.interval) clearInterval(this.interval);
+ this.options.interval
+ && !this.paused
+ && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
+ return this
+ }
+
+ , getActiveIndex: function () {
+ this.$active = this.$element.find('.item.active')
+ this.$items = this.$active.parent().children()
+ return this.$items.index(this.$active)
+ }
+
+ , to: function (pos) {
+ var activeIndex = this.getActiveIndex()
+ , that = this
+
+ if (pos > (this.$items.length - 1) || pos < 0) return
+
+ if (this.sliding) {
+ return this.$element.one('slid', function () {
+ that.to(pos)
+ })
+ }
+
+ if (activeIndex == pos) {
+ return this.pause().cycle()
+ }
+
+ return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos]))
+ }
+
+ , pause: function (e) {
+ if (!e) this.paused = true
+ if (this.$element.find('.next, .prev').length && $.support.transition.end) {
+ this.$element.trigger($.support.transition.end)
+ this.cycle(true)
+ }
+ clearInterval(this.interval)
+ this.interval = null
+ return this
+ }
+
+ , next: function () {
+ if (this.sliding) return
+ return this.slide('next')
+ }
+
+ , prev: function () {
+ if (this.sliding) return
+ return this.slide('prev')
+ }
+
+ , slide: function (type, next) {
+ var $active = this.$element.find('.item.active')
+ , $next = next || $active[type]()
+ , isCycling = this.interval
+ , direction = type == 'next' ? 'left' : 'right'
+ , fallback = type == 'next' ? 'first' : 'last'
+ , that = this
+ , e
+
+ this.sliding = true
+
+ isCycling && this.pause()
+
+ $next = $next.length ? $next : this.$element.find('.item')[fallback]()
+
+ e = $.Event('slide', {
+ relatedTarget: $next[0]
+ , direction: direction
+ })
+
+ if ($next.hasClass('active')) return
+
+ if (this.$indicators.length) {
+ this.$indicators.find('.active').removeClass('active')
+ this.$element.one('slid', function () {
+ var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()])
+ $nextIndicator && $nextIndicator.addClass('active')
+ })
+ }
+
+ if ($.support.transition && this.$element.hasClass('slide')) {
+ this.$element.trigger(e)
+ if (e.isDefaultPrevented()) return
+ $next.addClass(type)
+ $next[0].offsetWidth // force reflow
+ $active.addClass(direction)
+ $next.addClass(direction)
+ this.$element.one($.support.transition.end, function () {
+ $next.removeClass([type, direction].join(' ')).addClass('active')
+ $active.removeClass(['active', direction].join(' '))
+ that.sliding = false
+ setTimeout(function () { that.$element.trigger('slid') }, 0)
+ })
+ } else {
+ this.$element.trigger(e)
+ if (e.isDefaultPrevented()) return
+ $active.removeClass('active')
+ $next.addClass('active')
+ this.sliding = false
+ this.$element.trigger('slid')
+ }
+
+ isCycling && this.cycle()
+
+ return this
+ }
+
+ }
+
+
+ /* CAROUSEL PLUGIN DEFINITION
+ * ========================== */
+
+ var old = $.fn.carousel
+
+ $.fn.carousel = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('carousel')
+ , options = $.extend({}, $.fn.carousel.defaults, typeof option == 'object' && option)
+ , action = typeof option == 'string' ? option : options.slide
+ if (!data) $this.data('carousel', (data = new Carousel(this, options)))
+ if (typeof option == 'number') data.to(option)
+ else if (action) data[action]()
+ else if (options.interval) data.pause().cycle()
+ })
+ }
+
+ $.fn.carousel.defaults = {
+ interval: 5000
+ , pause: 'hover'
+ }
+
+ $.fn.carousel.Constructor = Carousel
+
+
+ /* CAROUSEL NO CONFLICT
+ * ==================== */
+
+ $.fn.carousel.noConflict = function () {
+ $.fn.carousel = old
+ return this
+ }
+
+ /* CAROUSEL DATA-API
+ * ================= */
+
+ $(document).on('click.carousel.data-api', '[data-slide], [data-slide-to]', function (e) {
+ var $this = $(this), href
+ , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
+ , options = $.extend({}, $target.data(), $this.data())
+ , slideIndex
+
+ $target.carousel(options)
+
+ if (slideIndex = $this.attr('data-slide-to')) {
+ $target.data('carousel').pause().to(slideIndex).cycle()
+ }
+
+ e.preventDefault()
+ })
+
+}(window.jQuery);
+/* =============================================================
+ * bootstrap-typeahead.js v2.3.2
+ * http://twitter.github.com/bootstrap/javascript.html#typeahead
+ * =============================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============================================================ */
+
+
+!function($){
+
+ "use strict"; // jshint ;_;
+
+
+ /* TYPEAHEAD PUBLIC CLASS DEFINITION
+ * ================================= */
+
+ var Typeahead = function (element, options) {
+ this.$element = $(element)
+ this.options = $.extend({}, $.fn.typeahead.defaults, options)
+ this.matcher = this.options.matcher || this.matcher
+ this.sorter = this.options.sorter || this.sorter
+ this.highlighter = this.options.highlighter || this.highlighter
+ this.updater = this.options.updater || this.updater
+ this.source = this.options.source
+ this.$menu = $(this.options.menu)
+ this.shown = false
+ this.listen()
+ }
+
+ Typeahead.prototype = {
+
+ constructor: Typeahead
+
+ , select: function () {
+ var val = this.$menu.find('.active').attr('data-value')
+ this.$element
+ .val(this.updater(val))
+ .change()
+ return this.hide()
+ }
+
+ , updater: function (item) {
+ return item
+ }
+
+ , show: function () {
+ var pos = $.extend({}, this.$element.position(), {
+ height: this.$element[0].offsetHeight
+ })
+
+ this.$menu
+ .insertAfter(this.$element)
+ .css({
+ top: pos.top + pos.height
+ , left: pos.left
+ })
+ .show()
+
+ this.shown = true
+ return this
+ }
+
+ , hide: function () {
+ this.$menu.hide()
+ this.shown = false
+ return this
+ }
+
+ , lookup: function (event) {
+ var items
+
+ this.query = this.$element.val()
+
+ if (!this.query || this.query.length < this.options.minLength) {
+ return this.shown ? this.hide() : this
+ }
+
+ items = $.isFunction(this.source) ? this.source(this.query, $.proxy(this.process, this)) : this.source
+
+ return items ? this.process(items) : this
+ }
+
+ , process: function (items) {
+ var that = this
+
+ items = $.grep(items, function (item) {
+ return that.matcher(item)
+ })
+
+ items = this.sorter(items)
+
+ if (!items.length) {
+ return this.shown ? this.hide() : this
+ }
+
+ return this.render(items.slice(0, this.options.items)).show()
+ }
+
+ , matcher: function (item) {
+ return ~item.toLowerCase().indexOf(this.query.toLowerCase())
+ }
+
+ , sorter: function (items) {
+ var beginswith = []
+ , caseSensitive = []
+ , caseInsensitive = []
+ , item
+
+ while (item = items.shift()) {
+ if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item)
+ else if (~item.indexOf(this.query)) caseSensitive.push(item)
+ else caseInsensitive.push(item)
+ }
+
+ return beginswith.concat(caseSensitive, caseInsensitive)
+ }
+
+ , highlighter: function (item) {
+ var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&')
+ return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
+ return '<strong>' + match + '</strong>'
+ })
+ }
+
+ , render: function (items) {
+ var that = this
+
+ items = $(items).map(function (i, item) {
+ i = $(that.options.item).attr('data-value', item)
+ i.find('a').html(that.highlighter(item))
+ return i[0]
+ })
+
+ items.first().addClass('active')
+ this.$menu.html(items)
+ return this
+ }
+
+ , next: function (event) {
+ var active = this.$menu.find('.active').removeClass('active')
+ , next = active.next()
+
+ if (!next.length) {
+ next = $(this.$menu.find('li')[0])
+ }
+
+ next.addClass('active')
+ }
+
+ , prev: function (event) {
+ var active = this.$menu.find('.active').removeClass('active')
+ , prev = active.prev()
+
+ if (!prev.length) {
+ prev = this.$menu.find('li').last()
+ }
+
+ prev.addClass('active')
+ }
+
+ , listen: function () {
+ this.$element
+ .on('focus', $.proxy(this.focus, this))
+ .on('blur', $.proxy(this.blur, this))
+ .on('keypress', $.proxy(this.keypress, this))
+ .on('keyup', $.proxy(this.keyup, this))
+
+ if (this.eventSupported('keydown')) {
+ this.$element.on('keydown', $.proxy(this.keydown, this))
+ }
+
+ this.$menu
+ .on('click', $.proxy(this.click, this))
+ .on('mouseenter', 'li', $.proxy(this.mouseenter, this))
+ .on('mouseleave', 'li', $.proxy(this.mouseleave, this))
+ }
+
+ , eventSupported: function(eventName) {
+ var isSupported = eventName in this.$element
+ if (!isSupported) {
+ this.$element.setAttribute(eventName, 'return;')
+ isSupported = typeof this.$element[eventName] === 'function'
+ }
+ return isSupported
+ }
+
+ , move: function (e) {
+ if (!this.shown) return
+
+ switch(e.keyCode) {
+ case 9: // tab
+ case 13: // enter
+ case 27: // escape
+ e.preventDefault()
+ break
+
+ case 38: // up arrow
+ e.preventDefault()
+ this.prev()
+ break
+
+ case 40: // down arrow
+ e.preventDefault()
+ this.next()
+ break
+ }
+
+ e.stopPropagation()
+ }
+
+ , keydown: function (e) {
+ this.suppressKeyPressRepeat = ~$.inArray(e.keyCode, [40,38,9,13,27])
+ this.move(e)
+ }
+
+ , keypress: function (e) {
+ if (this.suppressKeyPressRepeat) return
+ this.move(e)
+ }
+
+ , keyup: function (e) {
+ switch(e.keyCode) {
+ case 40: // down arrow
+ case 38: // up arrow
+ case 16: // shift
+ case 17: // ctrl
+ case 18: // alt
+ break
+
+ case 9: // tab
+ case 13: // enter
+ if (!this.shown) return
+ this.select()
+ break
+
+ case 27: // escape
+ if (!this.shown) return
+ this.hide()
+ break
+
+ default:
+ this.lookup()
+ }
+
+ e.stopPropagation()
+ e.preventDefault()
+ }
+
+ , focus: function (e) {
+ this.focused = true
+ }
+
+ , blur: function (e) {
+ this.focused = false
+ if (!this.mousedover && this.shown) this.hide()
+ }
+
+ , click: function (e) {
+ e.stopPropagation()
+ e.preventDefault()
+ this.select()
+ this.$element.focus()
+ }
+
+ , mouseenter: function (e) {
+ this.mousedover = true
+ this.$menu.find('.active').removeClass('active')
+ $(e.currentTarget).addClass('active')
+ }
+
+ , mouseleave: function (e) {
+ this.mousedover = false
+ if (!this.focused && this.shown) this.hide()
+ }
+
+ }
+
+
+ /* TYPEAHEAD PLUGIN DEFINITION
+ * =========================== */
+
+ var old = $.fn.typeahead
+
+ $.fn.typeahead = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('typeahead')
+ , options = typeof option == 'object' && option
+ if (!data) $this.data('typeahead', (data = new Typeahead(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.typeahead.defaults = {
+ source: []
+ , items: 8
+ , menu: '<ul class="typeahead dropdown-menu"></ul>'
+ , item: '<li><a href="#"></a></li>'
+ , minLength: 1
+ }
+
+ $.fn.typeahead.Constructor = Typeahead
+
+
+ /* TYPEAHEAD NO CONFLICT
+ * =================== */
+
+ $.fn.typeahead.noConflict = function () {
+ $.fn.typeahead = old
+ return this
+ }
+
+
+ /* TYPEAHEAD DATA-API
+ * ================== */
+
+ $(document).on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) {
+ var $this = $(this)
+ if ($this.data('typeahead')) return
+ $this.typeahead($this.data())
+ })
+
+}(window.jQuery);
1
0