├── LICENSE.txt
├── README.md
├── binary_demo
├── binary-app.js
├── binary-app.tag
├── calcstore.js
├── index.html
├── js
│ ├── es5-shim.js
│ ├── html5-shiv.js
│ └── riot.min.js
└── riotcontrol.js
├── bower.json
├── demo
├── index.html
├── js
│ ├── es5-shim.js
│ ├── html5-shiv.js
│ └── riot.js
├── riotcontrol.js
├── todo.css
├── todo.js
├── todo.tag
├── todoapp.js
├── todoapp.tag
└── todostore.js
├── package.json
├── riotcontrol.js
└── routing_demo
├── index.html
├── item-app.js
├── item-app.tag
├── item-detail.js
├── item-detail.tag
├── itemstore.js
├── js
├── es5-shim.js
├── html5-shiv.js
└── riot.js
├── riotcontrol.js
└── todo.css
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Jim Sparkman + contributors
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | RiotControl
2 | ============
3 |
4 | __UPDATE:__ Experimental multi-threaded version under [dev branch](https://github.com/jimsparkman/RiotControl/tree/dev).
5 |
6 | ```
7 | npm install riotcontrol
8 | ```
9 |
10 | A Simplistic Central Event Controller / Dispatcher For [RiotJS](https://github.com/muut/riotjs), Inspired By Facebook's [Flux](https://github.com/facebook/flux) Architecture Pattern.
11 |
12 | RiotControl is, in the spirit of Riot itself, extremely lightweight. It forgoes elements of Flux, to favor small and simple applications. RiotControl passes events from views to stores, and back, relying heavily on Riot's observerable API. Stores can talk to many views, and views can talk to many stores.
13 |
14 | Example data flow
15 | -------
16 |
17 | Given the following:
18 |
19 | - A TodoList view (Riot tag)
20 | - Triggers actions/events through RiotControl and listens for data change events.
21 | - A TodoStore (generic JS data store)
22 | - Mix of model manager/MVC-pattern controller that listens for actions/events, performs business logic, and dispatches data changed events.
23 |
24 | Possible data flow:
25 |
26 | 1. TodoList view triggers 'todo_remove' event to RiotControl.
27 | 2. RiotControl passes event along to stores.
28 | 3. TodoStore implements a 'todo_remove' event handler, talks to back-end server.
29 | 4. TodoStore triggers 'todos_changed' event, with new data. (new list with the todo removed)
30 | 5. TodoList view implements a 'todos_changed' event handler, receiving new data, and updating the UI.
31 |
32 | This encourages loosely coupled components. Stores house application logic and domain-specific data. Views comprise the user interface. Either side can be swapped out without interfering with the other. For example, a store that saves to local storage can be easily swapped for one that saves to a back-end service instead.
33 |
34 | Demos
35 | ============
36 |
37 | [TodoList](http://jimsparkman.github.io/RiotControl/demo/)
38 |
39 | Reference demo/todostore.js and todo.tag to understand how this works.
40 |
41 | [URL Routing Example](http://jimsparkman.github.io/RiotControl/routing_demo/)
42 |
43 | Reference routing_demo/index.html, itemstore.js, and item-app.tag
44 |
45 | [Binary Calculator](http://jimsparkman.github.io/RiotControl/binary_demo/)
46 |
47 | Things People Have Built
48 | ============
49 |
50 | [Flux Catalog](https://github.com/txchen/feplay/tree/gh-pages/riot_flux)
51 |
52 | RiotJS version of the flux-comparison catalog.
53 |
54 | [Where Da Movies At](https://github.com/derekr/wheredamoviesat)
55 |
56 | Map of all movies in a given location.
57 |
58 | Usage
59 | ============
60 |
61 | Requires Riot 2.0+
62 |
63 | Include riotcontrol.js, or it's few lines of code, in your project.
64 |
65 | API
66 | ============
67 |
68 | Register the store in central dispatch, where store is a riot.observable(). Generally, all stores should be created and registered before the Riot app is mounted.
69 |
70 | ```javascript
71 | RiotControl.addStore(store)
72 |
73 | // Example, at start of application:
74 | var todoStore = new TodoStore() // Create a store instance.
75 | RiotControl.addStore(todoStore) // Register the store in central dispatch.
76 | ```
77 |
78 | Trigger event on all stores registered in central dispatch. Essentially, a 'broadcast' version of Riot's el.trigger() API.
79 |
80 | ```javascript
81 | RiotControl.trigger(event)
82 | RiotControl.trigger(event, arg1 ... argN)
83 |
84 | // Example, inside Riot view (tag):
85 | RiotControl.trigger('todo_add', { title: self.text })
86 | ```
87 |
88 | Listen for event, and execute callback when it is triggered. This applies to all stores registered, so that you may receive the same event from multiple sources.
89 |
90 | ```javascript
91 | RiotControl.on(event, callback)
92 |
93 | // Example, inside Riot view (tag):
94 | RiotControl.on('todos_changed', function(items) {
95 | self.items = items
96 | self.update()
97 | })
98 | ```
99 |
100 | Remove event listener.
101 |
102 | ```javascript
103 | RiotControl.off(event)
104 |
105 | RiotControl.off(event, callback)
106 | ```
107 |
108 | Same as RiotControl.on(), executes once.
109 |
110 | ```javascript
111 | RiotControl.one(event, callback)
112 | ```
113 |
--------------------------------------------------------------------------------
/binary_demo/binary-app.js:
--------------------------------------------------------------------------------
1 | riot.tag('binary-app', '
Binary Calculator Enter hex: 0x { value } { pos }
', function(opts) {
2 |
3 | var self = this
4 | self.bitlist = []
5 |
6 | this.hexChanged = function(e) {
7 | e.target.value = e.target.value.toUpperCase()
8 | RiotControl.trigger('hex_changed', e.target.value)
9 | }.bind(this);
10 |
11 | this.toggle = function(e) {
12 | RiotControl.trigger('bit_changed', e.item.pos)
13 | }.bind(this);
14 |
15 | RiotControl.on('binary_changed', function(bitlist) {
16 | self.bitlist = bitlist
17 | riot.update()
18 | })
19 |
20 | RiotControl.on('hex_forced_change', function(hexVal) {
21 | self.input.value = hexVal.toUpperCase()
22 | RiotControl.trigger('hex_changed', self.input.value)
23 | })
24 |
25 |
26 | });
--------------------------------------------------------------------------------
/binary_demo/binary-app.tag:
--------------------------------------------------------------------------------
1 |
2 |
3 | Binary Calculator
4 | Enter hex:
5 |
6 | 0x
7 |
8 |
9 | { value }
10 |
11 |
12 |
13 | { pos }
14 |
15 |
16 |
17 |
18 | var self = this
19 | self.bitlist = []
20 |
21 | hexChanged(e) {
22 | e.target.value = e.target.value.toUpperCase()
23 | RiotControl.trigger('hex_changed', e.target.value)
24 | }
25 |
26 | toggle(e) {
27 | RiotControl.trigger('bit_changed', e.item.pos)
28 | }
29 |
30 | RiotControl.on('binary_changed', function(bitlist) {
31 | self.bitlist = bitlist
32 | riot.update()
33 | })
34 |
35 | RiotControl.on('hex_forced_change', function(hexVal) {
36 | self.input.value = hexVal.toUpperCase()
37 | RiotControl.trigger('hex_changed', self.input.value)
38 | })
39 |
40 |
--------------------------------------------------------------------------------
/binary_demo/calcstore.js:
--------------------------------------------------------------------------------
1 | function CalculatorStore() {
2 | riot.observable(this)
3 |
4 | var self = this
5 |
6 | self.hex = ''
7 | self.max = 7
8 | self.bitlist = []
9 |
10 | self.convert = function(num, currentRadix, resultRadix) {
11 | return parseInt(num, currentRadix).toString(resultRadix)
12 | }
13 |
14 | self.on('hex_changed', function(hexVal) {
15 | self.hex = hexVal
16 | var bNum = parseInt(hexVal,16)
17 | self.bitlist = []
18 | for (var i = 0; i<=self.max; i++) {
19 | self.bitlist.push({ pos: self.max-i, value: ((1 << self.max-i) & bNum) > 0 ? 1 : 0})
20 | }
21 |
22 | self.trigger('binary_changed', self.bitlist)
23 | })
24 |
25 | self.on('bit_changed', function(bitPos) {
26 | var mask = (1 << bitPos)
27 | var newHex = parseInt(self.hex,16) ^ mask
28 | self.trigger('hex_forced_change', self.convert(newHex, 10, 16))
29 | })
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/binary_demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | RiotControl Demo
7 |
8 |
9 |
14 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | View on GitHub
35 |
36 |
37 |
38 |
39 |
40 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/binary_demo/js/es5-shim.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * https://github.com/es-shims/es5-shim
3 | * @license es5-shim Copyright 2009-2014 by contributors, MIT License
4 | * see https://github.com/es-shims/es5-shim/blob/master/LICENSE
5 | */
6 |
7 | // vim: ts=4 sts=4 sw=4 expandtab
8 |
9 | //Add semicolon to prevent IIFE from being passed as argument to concatenated code.
10 | ;
11 |
12 | // UMD (Universal Module Definition)
13 | // see https://github.com/umdjs/umd/blob/master/returnExports.js
14 | (function (root, factory) {
15 | if (typeof define === 'function' && define.amd) {
16 | // AMD. Register as an anonymous module.
17 | define(factory);
18 | } else if (typeof exports === 'object') {
19 | // Node. Does not work with strict CommonJS, but
20 | // only CommonJS-like enviroments that support module.exports,
21 | // like Node.
22 | module.exports = factory();
23 | } else {
24 | // Browser globals (root is window)
25 | root.returnExports = factory();
26 | }
27 | }(this, function () {
28 |
29 | /**
30 | * Brings an environment as close to ECMAScript 5 compliance
31 | * as is possible with the facilities of erstwhile engines.
32 | *
33 | * Annotated ES5: http://es5.github.com/ (specific links below)
34 | * ES5 Spec: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf
35 | * Required reading: http://javascriptweblog.wordpress.com/2011/12/05/extending-javascript-natives/
36 | */
37 |
38 | // Shortcut to an often accessed properties, in order to avoid multiple
39 | // dereference that costs universally.
40 | var ArrayPrototype = Array.prototype;
41 | var ObjectPrototype = Object.prototype;
42 | var FunctionPrototype = Function.prototype;
43 | var StringPrototype = String.prototype;
44 | var NumberPrototype = Number.prototype;
45 | var array_slice = ArrayPrototype.slice;
46 | var array_splice = ArrayPrototype.splice;
47 | var array_push = ArrayPrototype.push;
48 | var array_unshift = ArrayPrototype.unshift;
49 | var call = FunctionPrototype.call;
50 |
51 | // Having a toString local variable name breaks in Opera so use _toString.
52 | var _toString = ObjectPrototype.toString;
53 |
54 | var isFunction = function (val) {
55 | return ObjectPrototype.toString.call(val) === '[object Function]';
56 | };
57 | var isRegex = function (val) {
58 | return ObjectPrototype.toString.call(val) === '[object RegExp]';
59 | };
60 | var isArray = function isArray(obj) {
61 | return _toString.call(obj) === '[object Array]';
62 | };
63 | var isString = function isString(obj) {
64 | return _toString.call(obj) === '[object String]';
65 | };
66 | var isArguments = function isArguments(value) {
67 | var str = _toString.call(value);
68 | var isArgs = str === '[object Arguments]';
69 | if (!isArgs) {
70 | isArgs = !isArray(value)
71 | && value !== null
72 | && typeof value === 'object'
73 | && typeof value.length === 'number'
74 | && value.length >= 0
75 | && isFunction(value.callee);
76 | }
77 | return isArgs;
78 | };
79 |
80 | var supportsDescriptors = Object.defineProperty && (function () {
81 | try {
82 | Object.defineProperty({}, 'x', {});
83 | return true;
84 | } catch (e) { /* this is ES3 */
85 | return false;
86 | }
87 | }());
88 |
89 | // Define configurable, writable and non-enumerable props
90 | // if they don't exist.
91 | var defineProperty;
92 | if (supportsDescriptors) {
93 | defineProperty = function (object, name, method, forceAssign) {
94 | if (!forceAssign && (name in object)) { return; }
95 | Object.defineProperty(object, name, {
96 | configurable: true,
97 | enumerable: false,
98 | writable: true,
99 | value: method
100 | });
101 | };
102 | } else {
103 | defineProperty = function (object, name, method, forceAssign) {
104 | if (!forceAssign && (name in object)) { return; }
105 | object[name] = method;
106 | };
107 | }
108 | var defineProperties = function (object, map, forceAssign) {
109 | for (var name in map) {
110 | if (ObjectPrototype.hasOwnProperty.call(map, name)) {
111 | defineProperty(object, name, map[name], forceAssign);
112 | }
113 | }
114 | };
115 |
116 | //
117 | // Util
118 | // ======
119 | //
120 |
121 | // ES5 9.4
122 | // http://es5.github.com/#x9.4
123 | // http://jsperf.com/to-integer
124 |
125 | function toInteger(num) {
126 | var n = +num;
127 | if (n !== n) { // isNaN
128 | n = 0;
129 | } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
130 | n = (n > 0 || -1) * Math.floor(Math.abs(n));
131 | }
132 | return n;
133 | }
134 |
135 | function isPrimitive(input) {
136 | var type = typeof input;
137 | return (
138 | input === null ||
139 | type === 'undefined' ||
140 | type === 'boolean' ||
141 | type === 'number' ||
142 | type === 'string'
143 | );
144 | }
145 |
146 | function toPrimitive(input) {
147 | var val, valueOf, toStr;
148 | if (isPrimitive(input)) {
149 | return input;
150 | }
151 | valueOf = input.valueOf;
152 | if (isFunction(valueOf)) {
153 | val = valueOf.call(input);
154 | if (isPrimitive(val)) {
155 | return val;
156 | }
157 | }
158 | toStr = input.toString;
159 | if (isFunction(toStr)) {
160 | val = toStr.call(input);
161 | if (isPrimitive(val)) {
162 | return val;
163 | }
164 | }
165 | throw new TypeError();
166 | }
167 |
168 | // ES5 9.9
169 | // http://es5.github.com/#x9.9
170 | var toObject = function (o) {
171 | if (o == null) { // this matches both null and undefined
172 | throw new TypeError("can't convert " + o + ' to object');
173 | }
174 | return Object(o);
175 | };
176 |
177 | var ToUint32 = function ToUint32(x) {
178 | return x >>> 0;
179 | };
180 |
181 | //
182 | // Function
183 | // ========
184 | //
185 |
186 | // ES-5 15.3.4.5
187 | // http://es5.github.com/#x15.3.4.5
188 |
189 | function Empty() {}
190 |
191 | defineProperties(FunctionPrototype, {
192 | bind: function bind(that) { // .length is 1
193 | // 1. Let Target be the this value.
194 | var target = this;
195 | // 2. If IsCallable(Target) is false, throw a TypeError exception.
196 | if (!isFunction(target)) {
197 | throw new TypeError('Function.prototype.bind called on incompatible ' + target);
198 | }
199 | // 3. Let A be a new (possibly empty) internal list of all of the
200 | // argument values provided after thisArg (arg1, arg2 etc), in order.
201 | // XXX slicedArgs will stand in for "A" if used
202 | var args = array_slice.call(arguments, 1); // for normal call
203 | // 4. Let F be a new native ECMAScript object.
204 | // 11. Set the [[Prototype]] internal property of F to the standard
205 | // built-in Function prototype object as specified in 15.3.3.1.
206 | // 12. Set the [[Call]] internal property of F as described in
207 | // 15.3.4.5.1.
208 | // 13. Set the [[Construct]] internal property of F as described in
209 | // 15.3.4.5.2.
210 | // 14. Set the [[HasInstance]] internal property of F as described in
211 | // 15.3.4.5.3.
212 | var binder = function () {
213 |
214 | if (this instanceof bound) {
215 | // 15.3.4.5.2 [[Construct]]
216 | // When the [[Construct]] internal method of a function object,
217 | // F that was created using the bind function is called with a
218 | // list of arguments ExtraArgs, the following steps are taken:
219 | // 1. Let target be the value of F's [[TargetFunction]]
220 | // internal property.
221 | // 2. If target has no [[Construct]] internal method, a
222 | // TypeError exception is thrown.
223 | // 3. Let boundArgs be the value of F's [[BoundArgs]] internal
224 | // property.
225 | // 4. Let args be a new list containing the same values as the
226 | // list boundArgs in the same order followed by the same
227 | // values as the list ExtraArgs in the same order.
228 | // 5. Return the result of calling the [[Construct]] internal
229 | // method of target providing args as the arguments.
230 |
231 | var result = target.apply(
232 | this,
233 | args.concat(array_slice.call(arguments))
234 | );
235 | if (Object(result) === result) {
236 | return result;
237 | }
238 | return this;
239 |
240 | } else {
241 | // 15.3.4.5.1 [[Call]]
242 | // When the [[Call]] internal method of a function object, F,
243 | // which was created using the bind function is called with a
244 | // this value and a list of arguments ExtraArgs, the following
245 | // steps are taken:
246 | // 1. Let boundArgs be the value of F's [[BoundArgs]] internal
247 | // property.
248 | // 2. Let boundThis be the value of F's [[BoundThis]] internal
249 | // property.
250 | // 3. Let target be the value of F's [[TargetFunction]] internal
251 | // property.
252 | // 4. Let args be a new list containing the same values as the
253 | // list boundArgs in the same order followed by the same
254 | // values as the list ExtraArgs in the same order.
255 | // 5. Return the result of calling the [[Call]] internal method
256 | // of target providing boundThis as the this value and
257 | // providing args as the arguments.
258 |
259 | // equiv: target.call(this, ...boundArgs, ...args)
260 | return target.apply(
261 | that,
262 | args.concat(array_slice.call(arguments))
263 | );
264 |
265 | }
266 |
267 | };
268 |
269 | // 15. If the [[Class]] internal property of Target is "Function", then
270 | // a. Let L be the length property of Target minus the length of A.
271 | // b. Set the length own property of F to either 0 or L, whichever is
272 | // larger.
273 | // 16. Else set the length own property of F to 0.
274 |
275 | var boundLength = Math.max(0, target.length - args.length);
276 |
277 | // 17. Set the attributes of the length own property of F to the values
278 | // specified in 15.3.5.1.
279 | var boundArgs = [];
280 | for (var i = 0; i < boundLength; i++) {
281 | boundArgs.push('$' + i);
282 | }
283 |
284 | // XXX Build a dynamic function with desired amount of arguments is the only
285 | // way to set the length property of a function.
286 | // In environments where Content Security Policies enabled (Chrome extensions,
287 | // for ex.) all use of eval or Function costructor throws an exception.
288 | // However in all of these environments Function.prototype.bind exists
289 | // and so this code will never be executed.
290 | var bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this, arguments); }')(binder);
291 |
292 | if (target.prototype) {
293 | Empty.prototype = target.prototype;
294 | bound.prototype = new Empty();
295 | // Clean up dangling references.
296 | Empty.prototype = null;
297 | }
298 |
299 | // TODO
300 | // 18. Set the [[Extensible]] internal property of F to true.
301 |
302 | // TODO
303 | // 19. Let thrower be the [[ThrowTypeError]] function Object (13.2.3).
304 | // 20. Call the [[DefineOwnProperty]] internal method of F with
305 | // arguments "caller", PropertyDescriptor {[[Get]]: thrower, [[Set]]:
306 | // thrower, [[Enumerable]]: false, [[Configurable]]: false}, and
307 | // false.
308 | // 21. Call the [[DefineOwnProperty]] internal method of F with
309 | // arguments "arguments", PropertyDescriptor {[[Get]]: thrower,
310 | // [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: false},
311 | // and false.
312 |
313 | // TODO
314 | // NOTE Function objects created using Function.prototype.bind do not
315 | // have a prototype property or the [[Code]], [[FormalParameters]], and
316 | // [[Scope]] internal properties.
317 | // XXX can't delete prototype in pure-js.
318 |
319 | // 22. Return F.
320 | return bound;
321 | }
322 | });
323 |
324 | // _Please note: Shortcuts are defined after `Function.prototype.bind` as we
325 | // us it in defining shortcuts.
326 | var owns = call.bind(ObjectPrototype.hasOwnProperty);
327 |
328 | // If JS engine supports accessors creating shortcuts.
329 | var defineGetter;
330 | var defineSetter;
331 | var lookupGetter;
332 | var lookupSetter;
333 | var supportsAccessors;
334 | if ((supportsAccessors = owns(ObjectPrototype, '__defineGetter__'))) {
335 | defineGetter = call.bind(ObjectPrototype.__defineGetter__);
336 | defineSetter = call.bind(ObjectPrototype.__defineSetter__);
337 | lookupGetter = call.bind(ObjectPrototype.__lookupGetter__);
338 | lookupSetter = call.bind(ObjectPrototype.__lookupSetter__);
339 | }
340 |
341 | //
342 | // Array
343 | // =====
344 | //
345 |
346 | // ES5 15.4.4.12
347 | // http://es5.github.com/#x15.4.4.12
348 | var spliceNoopReturnsEmptyArray = (function () {
349 | var a = [1, 2];
350 | var result = a.splice();
351 | return a.length === 2 && isArray(result) && result.length === 0;
352 | }());
353 | defineProperties(ArrayPrototype, {
354 | // Safari 5.0 bug where .splice() returns undefined
355 | splice: function splice(start, deleteCount) {
356 | if (arguments.length === 0) {
357 | return [];
358 | } else {
359 | return array_splice.apply(this, arguments);
360 | }
361 | }
362 | }, spliceNoopReturnsEmptyArray);
363 |
364 | var spliceWorksWithEmptyObject = (function () {
365 | var obj = {};
366 | ArrayPrototype.splice.call(obj, 0, 0, 1);
367 | return obj.length === 1;
368 | }());
369 | defineProperties(ArrayPrototype, {
370 | splice: function splice(start, deleteCount) {
371 | if (arguments.length === 0) { return []; }
372 | var args = arguments;
373 | this.length = Math.max(toInteger(this.length), 0);
374 | if (arguments.length > 0 && typeof deleteCount !== 'number') {
375 | args = array_slice.call(arguments);
376 | if (args.length < 2) {
377 | args.push(this.length - start);
378 | } else {
379 | args[1] = toInteger(deleteCount);
380 | }
381 | }
382 | return array_splice.apply(this, args);
383 | }
384 | }, !spliceWorksWithEmptyObject);
385 |
386 | // ES5 15.4.4.12
387 | // http://es5.github.com/#x15.4.4.13
388 | // Return len+argCount.
389 | // [bugfix, ielt8]
390 | // IE < 8 bug: [].unshift(0) === undefined but should be "1"
391 | var hasUnshiftReturnValueBug = [].unshift(0) !== 1;
392 | defineProperties(ArrayPrototype, {
393 | unshift: function () {
394 | array_unshift.apply(this, arguments);
395 | return this.length;
396 | }
397 | }, hasUnshiftReturnValueBug);
398 |
399 | // ES5 15.4.3.2
400 | // http://es5.github.com/#x15.4.3.2
401 | // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/isArray
402 | defineProperties(Array, { isArray: isArray });
403 |
404 | // The IsCallable() check in the Array functions
405 | // has been replaced with a strict check on the
406 | // internal class of the object to trap cases where
407 | // the provided function was actually a regular
408 | // expression literal, which in V8 and
409 | // JavaScriptCore is a typeof "function". Only in
410 | // V8 are regular expression literals permitted as
411 | // reduce parameters, so it is desirable in the
412 | // general case for the shim to match the more
413 | // strict and common behavior of rejecting regular
414 | // expressions.
415 |
416 | // ES5 15.4.4.18
417 | // http://es5.github.com/#x15.4.4.18
418 | // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/forEach
419 |
420 | // Check failure of by-index access of string characters (IE < 9)
421 | // and failure of `0 in boxedString` (Rhino)
422 | var boxedString = Object('a');
423 | var splitString = boxedString[0] !== 'a' || !(0 in boxedString);
424 |
425 | var properlyBoxesContext = function properlyBoxed(method) {
426 | // Check node 0.6.21 bug where third parameter is not boxed
427 | var properlyBoxesNonStrict = true;
428 | var properlyBoxesStrict = true;
429 | if (method) {
430 | method.call('foo', function (_, __, context) {
431 | if (typeof context !== 'object') { properlyBoxesNonStrict = false; }
432 | });
433 |
434 | method.call([1], function () {
435 | 'use strict';
436 | properlyBoxesStrict = typeof this === 'string';
437 | }, 'x');
438 | }
439 | return !!method && properlyBoxesNonStrict && properlyBoxesStrict;
440 | };
441 |
442 | defineProperties(ArrayPrototype, {
443 | forEach: function forEach(fun /*, thisp*/) {
444 | var object = toObject(this),
445 | self = splitString && isString(this) ? this.split('') : object,
446 | thisp = arguments[1],
447 | i = -1,
448 | length = self.length >>> 0;
449 |
450 | // If no callback function or if callback is not a callable function
451 | if (!isFunction(fun)) {
452 | throw new TypeError(); // TODO message
453 | }
454 |
455 | while (++i < length) {
456 | if (i in self) {
457 | // Invoke the callback function with call, passing arguments:
458 | // context, property value, property key, thisArg object
459 | // context
460 | fun.call(thisp, self[i], i, object);
461 | }
462 | }
463 | }
464 | }, !properlyBoxesContext(ArrayPrototype.forEach));
465 |
466 | // ES5 15.4.4.19
467 | // http://es5.github.com/#x15.4.4.19
468 | // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map
469 | defineProperties(ArrayPrototype, {
470 | map: function map(fun /*, thisp*/) {
471 | var object = toObject(this),
472 | self = splitString && isString(this) ? this.split('') : object,
473 | length = self.length >>> 0,
474 | result = Array(length),
475 | thisp = arguments[1];
476 |
477 | // If no callback function or if callback is not a callable function
478 | if (!isFunction(fun)) {
479 | throw new TypeError(fun + ' is not a function');
480 | }
481 |
482 | for (var i = 0; i < length; i++) {
483 | if (i in self) {
484 | result[i] = fun.call(thisp, self[i], i, object);
485 | }
486 | }
487 | return result;
488 | }
489 | }, !properlyBoxesContext(ArrayPrototype.map));
490 |
491 | // ES5 15.4.4.20
492 | // http://es5.github.com/#x15.4.4.20
493 | // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/filter
494 | defineProperties(ArrayPrototype, {
495 | filter: function filter(fun /*, thisp */) {
496 | var object = toObject(this),
497 | self = splitString && isString(this) ? this.split('') : object,
498 | length = self.length >>> 0,
499 | result = [],
500 | value,
501 | thisp = arguments[1];
502 |
503 | // If no callback function or if callback is not a callable function
504 | if (!isFunction(fun)) {
505 | throw new TypeError(fun + ' is not a function');
506 | }
507 |
508 | for (var i = 0; i < length; i++) {
509 | if (i in self) {
510 | value = self[i];
511 | if (fun.call(thisp, value, i, object)) {
512 | result.push(value);
513 | }
514 | }
515 | }
516 | return result;
517 | }
518 | }, !properlyBoxesContext(ArrayPrototype.filter));
519 |
520 | // ES5 15.4.4.16
521 | // http://es5.github.com/#x15.4.4.16
522 | // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/every
523 | defineProperties(ArrayPrototype, {
524 | every: function every(fun /*, thisp */) {
525 | var object = toObject(this),
526 | self = splitString && isString(this) ? this.split('') : object,
527 | length = self.length >>> 0,
528 | thisp = arguments[1];
529 |
530 | // If no callback function or if callback is not a callable function
531 | if (!isFunction(fun)) {
532 | throw new TypeError(fun + ' is not a function');
533 | }
534 |
535 | for (var i = 0; i < length; i++) {
536 | if (i in self && !fun.call(thisp, self[i], i, object)) {
537 | return false;
538 | }
539 | }
540 | return true;
541 | }
542 | }, !properlyBoxesContext(ArrayPrototype.every));
543 |
544 | // ES5 15.4.4.17
545 | // http://es5.github.com/#x15.4.4.17
546 | // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/some
547 | defineProperties(ArrayPrototype, {
548 | some: function some(fun /*, thisp */) {
549 | var object = toObject(this),
550 | self = splitString && isString(this) ? this.split('') : object,
551 | length = self.length >>> 0,
552 | thisp = arguments[1];
553 |
554 | // If no callback function or if callback is not a callable function
555 | if (!isFunction(fun)) {
556 | throw new TypeError(fun + ' is not a function');
557 | }
558 |
559 | for (var i = 0; i < length; i++) {
560 | if (i in self && fun.call(thisp, self[i], i, object)) {
561 | return true;
562 | }
563 | }
564 | return false;
565 | }
566 | }, !properlyBoxesContext(ArrayPrototype.some));
567 |
568 | // ES5 15.4.4.21
569 | // http://es5.github.com/#x15.4.4.21
570 | // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduce
571 | var reduceCoercesToObject = false;
572 | if (ArrayPrototype.reduce) {
573 | reduceCoercesToObject = typeof ArrayPrototype.reduce.call('es5', function (_, __, ___, list) { return list; }) === 'object';
574 | }
575 | defineProperties(ArrayPrototype, {
576 | reduce: function reduce(fun /*, initial*/) {
577 | var object = toObject(this),
578 | self = splitString && isString(this) ? this.split('') : object,
579 | length = self.length >>> 0;
580 |
581 | // If no callback function or if callback is not a callable function
582 | if (!isFunction(fun)) {
583 | throw new TypeError(fun + ' is not a function');
584 | }
585 |
586 | // no value to return if no initial value and an empty array
587 | if (!length && arguments.length === 1) {
588 | throw new TypeError('reduce of empty array with no initial value');
589 | }
590 |
591 | var i = 0;
592 | var result;
593 | if (arguments.length >= 2) {
594 | result = arguments[1];
595 | } else {
596 | do {
597 | if (i in self) {
598 | result = self[i++];
599 | break;
600 | }
601 |
602 | // if array contains no values, no initial value to return
603 | if (++i >= length) {
604 | throw new TypeError('reduce of empty array with no initial value');
605 | }
606 | } while (true);
607 | }
608 |
609 | for (; i < length; i++) {
610 | if (i in self) {
611 | result = fun.call(void 0, result, self[i], i, object);
612 | }
613 | }
614 |
615 | return result;
616 | }
617 | }, !reduceCoercesToObject);
618 |
619 | // ES5 15.4.4.22
620 | // http://es5.github.com/#x15.4.4.22
621 | // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduceRight
622 | var reduceRightCoercesToObject = false;
623 | if (ArrayPrototype.reduceRight) {
624 | reduceRightCoercesToObject = typeof ArrayPrototype.reduceRight.call('es5', function (_, __, ___, list) { return list; }) === 'object';
625 | }
626 | defineProperties(ArrayPrototype, {
627 | reduceRight: function reduceRight(fun /*, initial*/) {
628 | var object = toObject(this),
629 | self = splitString && isString(this) ? this.split('') : object,
630 | length = self.length >>> 0;
631 |
632 | // If no callback function or if callback is not a callable function
633 | if (!isFunction(fun)) {
634 | throw new TypeError(fun + ' is not a function');
635 | }
636 |
637 | // no value to return if no initial value, empty array
638 | if (!length && arguments.length === 1) {
639 | throw new TypeError('reduceRight of empty array with no initial value');
640 | }
641 |
642 | var result, i = length - 1;
643 | if (arguments.length >= 2) {
644 | result = arguments[1];
645 | } else {
646 | do {
647 | if (i in self) {
648 | result = self[i--];
649 | break;
650 | }
651 |
652 | // if array contains no values, no initial value to return
653 | if (--i < 0) {
654 | throw new TypeError('reduceRight of empty array with no initial value');
655 | }
656 | } while (true);
657 | }
658 |
659 | if (i < 0) {
660 | return result;
661 | }
662 |
663 | do {
664 | if (i in self) {
665 | result = fun.call(void 0, result, self[i], i, object);
666 | }
667 | } while (i--);
668 |
669 | return result;
670 | }
671 | }, !reduceRightCoercesToObject);
672 |
673 | // ES5 15.4.4.14
674 | // http://es5.github.com/#x15.4.4.14
675 | // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf
676 | var hasFirefox2IndexOfBug = Array.prototype.indexOf && [0, 1].indexOf(1, 2) !== -1;
677 | defineProperties(ArrayPrototype, {
678 | indexOf: function indexOf(sought /*, fromIndex */ ) {
679 | var self = splitString && isString(this) ? this.split('') : toObject(this),
680 | length = self.length >>> 0;
681 |
682 | if (!length) {
683 | return -1;
684 | }
685 |
686 | var i = 0;
687 | if (arguments.length > 1) {
688 | i = toInteger(arguments[1]);
689 | }
690 |
691 | // handle negative indices
692 | i = i >= 0 ? i : Math.max(0, length + i);
693 | for (; i < length; i++) {
694 | if (i in self && self[i] === sought) {
695 | return i;
696 | }
697 | }
698 | return -1;
699 | }
700 | }, hasFirefox2IndexOfBug);
701 |
702 | // ES5 15.4.4.15
703 | // http://es5.github.com/#x15.4.4.15
704 | // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/lastIndexOf
705 | var hasFirefox2LastIndexOfBug = Array.prototype.lastIndexOf && [0, 1].lastIndexOf(0, -3) !== -1;
706 | defineProperties(ArrayPrototype, {
707 | lastIndexOf: function lastIndexOf(sought /*, fromIndex */) {
708 | var self = splitString && isString(this) ? this.split('') : toObject(this),
709 | length = self.length >>> 0;
710 |
711 | if (!length) {
712 | return -1;
713 | }
714 | var i = length - 1;
715 | if (arguments.length > 1) {
716 | i = Math.min(i, toInteger(arguments[1]));
717 | }
718 | // handle negative indices
719 | i = i >= 0 ? i : length - Math.abs(i);
720 | for (; i >= 0; i--) {
721 | if (i in self && sought === self[i]) {
722 | return i;
723 | }
724 | }
725 | return -1;
726 | }
727 | }, hasFirefox2LastIndexOfBug);
728 |
729 | //
730 | // Object
731 | // ======
732 | //
733 |
734 | // ES5 15.2.3.14
735 | // http://es5.github.com/#x15.2.3.14
736 |
737 | // http://whattheheadsaid.com/2010/10/a-safer-object-keys-compatibility-implementation
738 | var hasDontEnumBug = !({'toString': null}).propertyIsEnumerable('toString'),
739 | hasProtoEnumBug = (function () {}).propertyIsEnumerable('prototype'),
740 | dontEnums = [
741 | 'toString',
742 | 'toLocaleString',
743 | 'valueOf',
744 | 'hasOwnProperty',
745 | 'isPrototypeOf',
746 | 'propertyIsEnumerable',
747 | 'constructor'
748 | ],
749 | dontEnumsLength = dontEnums.length;
750 |
751 | defineProperties(Object, {
752 | keys: function keys(object) {
753 | var isFn = isFunction(object),
754 | isArgs = isArguments(object),
755 | isObject = object !== null && typeof object === 'object',
756 | isStr = isObject && isString(object);
757 |
758 | if (!isObject && !isFn && !isArgs) {
759 | throw new TypeError('Object.keys called on a non-object');
760 | }
761 |
762 | var theKeys = [];
763 | var skipProto = hasProtoEnumBug && isFn;
764 | if (isStr || isArgs) {
765 | for (var i = 0; i < object.length; ++i) {
766 | theKeys.push(String(i));
767 | }
768 | } else {
769 | for (var name in object) {
770 | if (!(skipProto && name === 'prototype') && owns(object, name)) {
771 | theKeys.push(String(name));
772 | }
773 | }
774 | }
775 |
776 | if (hasDontEnumBug) {
777 | var ctor = object.constructor,
778 | skipConstructor = ctor && ctor.prototype === object;
779 | for (var j = 0; j < dontEnumsLength; j++) {
780 | var dontEnum = dontEnums[j];
781 | if (!(skipConstructor && dontEnum === 'constructor') && owns(object, dontEnum)) {
782 | theKeys.push(dontEnum);
783 | }
784 | }
785 | }
786 | return theKeys;
787 | }
788 | });
789 |
790 | var keysWorksWithArguments = Object.keys && (function () {
791 | // Safari 5.0 bug
792 | return Object.keys(arguments).length === 2;
793 | }(1, 2));
794 | var originalKeys = Object.keys;
795 | defineProperties(Object, {
796 | keys: function keys(object) {
797 | if (isArguments(object)) {
798 | return originalKeys(ArrayPrototype.slice.call(object));
799 | } else {
800 | return originalKeys(object);
801 | }
802 | }
803 | }, !keysWorksWithArguments);
804 |
805 | //
806 | // Date
807 | // ====
808 | //
809 |
810 | // ES5 15.9.5.43
811 | // http://es5.github.com/#x15.9.5.43
812 | // This function returns a String value represent the instance in time
813 | // represented by this Date object. The format of the String is the Date Time
814 | // string format defined in 15.9.1.15. All fields are present in the String.
815 | // The time zone is always UTC, denoted by the suffix Z. If the time value of
816 | // this object is not a finite Number a RangeError exception is thrown.
817 | var negativeDate = -62198755200000;
818 | var negativeYearString = '-000001';
819 | var hasNegativeDateBug = Date.prototype.toISOString && new Date(negativeDate).toISOString().indexOf(negativeYearString) === -1;
820 |
821 | defineProperties(Date.prototype, {
822 | toISOString: function toISOString() {
823 | var result, length, value, year, month;
824 | if (!isFinite(this)) {
825 | throw new RangeError('Date.prototype.toISOString called on non-finite value.');
826 | }
827 |
828 | year = this.getUTCFullYear();
829 |
830 | month = this.getUTCMonth();
831 | // see https://github.com/es-shims/es5-shim/issues/111
832 | year += Math.floor(month / 12);
833 | month = (month % 12 + 12) % 12;
834 |
835 | // the date time string format is specified in 15.9.1.15.
836 | result = [month + 1, this.getUTCDate(), this.getUTCHours(), this.getUTCMinutes(), this.getUTCSeconds()];
837 | year = (
838 | (year < 0 ? '-' : (year > 9999 ? '+' : '')) +
839 | ('00000' + Math.abs(year)).slice(0 <= year && year <= 9999 ? -4 : -6)
840 | );
841 |
842 | length = result.length;
843 | while (length--) {
844 | value = result[length];
845 | // pad months, days, hours, minutes, and seconds to have two
846 | // digits.
847 | if (value < 10) {
848 | result[length] = '0' + value;
849 | }
850 | }
851 | // pad milliseconds to have three digits.
852 | return (
853 | year + '-' + result.slice(0, 2).join('-') +
854 | 'T' + result.slice(2).join(':') + '.' +
855 | ('000' + this.getUTCMilliseconds()).slice(-3) + 'Z'
856 | );
857 | }
858 | }, hasNegativeDateBug);
859 |
860 |
861 | // ES5 15.9.5.44
862 | // http://es5.github.com/#x15.9.5.44
863 | // This function provides a String representation of a Date object for use by
864 | // JSON.stringify (15.12.3).
865 | var dateToJSONIsSupported = false;
866 | try {
867 | dateToJSONIsSupported = (
868 | Date.prototype.toJSON &&
869 | new Date(NaN).toJSON() === null &&
870 | new Date(negativeDate).toJSON().indexOf(negativeYearString) !== -1 &&
871 | Date.prototype.toJSON.call({ // generic
872 | toISOString: function () {
873 | return true;
874 | }
875 | })
876 | );
877 | } catch (e) {
878 | }
879 | if (!dateToJSONIsSupported) {
880 | Date.prototype.toJSON = function toJSON(key) {
881 | // When the toJSON method is called with argument key, the following
882 | // steps are taken:
883 |
884 | // 1. Let O be the result of calling ToObject, giving it the this
885 | // value as its argument.
886 | // 2. Let tv be toPrimitive(O, hint Number).
887 | var o = Object(this),
888 | tv = toPrimitive(o),
889 | toISO;
890 | // 3. If tv is a Number and is not finite, return null.
891 | if (typeof tv === 'number' && !isFinite(tv)) {
892 | return null;
893 | }
894 | // 4. Let toISO be the result of calling the [[Get]] internal method of
895 | // O with argument "toISOString".
896 | toISO = o.toISOString;
897 | // 5. If IsCallable(toISO) is false, throw a TypeError exception.
898 | if (typeof toISO !== 'function') {
899 | throw new TypeError('toISOString property is not callable');
900 | }
901 | // 6. Return the result of calling the [[Call]] internal method of
902 | // toISO with O as the this value and an empty argument list.
903 | return toISO.call(o);
904 |
905 | // NOTE 1 The argument is ignored.
906 |
907 | // NOTE 2 The toJSON function is intentionally generic; it does not
908 | // require that its this value be a Date object. Therefore, it can be
909 | // transferred to other kinds of objects for use as a method. However,
910 | // it does require that any such object have a toISOString method. An
911 | // object is free to use the argument key to filter its
912 | // stringification.
913 | };
914 | }
915 |
916 | // ES5 15.9.4.2
917 | // http://es5.github.com/#x15.9.4.2
918 | // based on work shared by Daniel Friesen (dantman)
919 | // http://gist.github.com/303249
920 | var supportsExtendedYears = Date.parse('+033658-09-27T01:46:40.000Z') === 1e15;
921 | var acceptsInvalidDates = !isNaN(Date.parse('2012-04-04T24:00:00.500Z')) || !isNaN(Date.parse('2012-11-31T23:59:59.000Z'));
922 | var doesNotParseY2KNewYear = isNaN(Date.parse('2000-01-01T00:00:00.000Z'));
923 | if (!Date.parse || doesNotParseY2KNewYear || acceptsInvalidDates || !supportsExtendedYears) {
924 | // XXX global assignment won't work in embeddings that use
925 | // an alternate object for the context.
926 | Date = (function (NativeDate) {
927 |
928 | // Date.length === 7
929 | function Date(Y, M, D, h, m, s, ms) {
930 | var length = arguments.length;
931 | if (this instanceof NativeDate) {
932 | var date = length === 1 && String(Y) === Y ? // isString(Y)
933 | // We explicitly pass it through parse:
934 | new NativeDate(Date.parse(Y)) :
935 | // We have to manually make calls depending on argument
936 | // length here
937 | length >= 7 ? new NativeDate(Y, M, D, h, m, s, ms) :
938 | length >= 6 ? new NativeDate(Y, M, D, h, m, s) :
939 | length >= 5 ? new NativeDate(Y, M, D, h, m) :
940 | length >= 4 ? new NativeDate(Y, M, D, h) :
941 | length >= 3 ? new NativeDate(Y, M, D) :
942 | length >= 2 ? new NativeDate(Y, M) :
943 | length >= 1 ? new NativeDate(Y) :
944 | new NativeDate();
945 | // Prevent mixups with unfixed Date object
946 | date.constructor = Date;
947 | return date;
948 | }
949 | return NativeDate.apply(this, arguments);
950 | }
951 |
952 | // 15.9.1.15 Date Time String Format.
953 | var isoDateExpression = new RegExp('^' +
954 | '(\\d{4}|[\+\-]\\d{6})' + // four-digit year capture or sign +
955 | // 6-digit extended year
956 | '(?:-(\\d{2})' + // optional month capture
957 | '(?:-(\\d{2})' + // optional day capture
958 | '(?:' + // capture hours:minutes:seconds.milliseconds
959 | 'T(\\d{2})' + // hours capture
960 | ':(\\d{2})' + // minutes capture
961 | '(?:' + // optional :seconds.milliseconds
962 | ':(\\d{2})' + // seconds capture
963 | '(?:(\\.\\d{1,}))?' + // milliseconds capture
964 | ')?' +
965 | '(' + // capture UTC offset component
966 | 'Z|' + // UTC capture
967 | '(?:' + // offset specifier +/-hours:minutes
968 | '([-+])' + // sign capture
969 | '(\\d{2})' + // hours offset capture
970 | ':(\\d{2})' + // minutes offset capture
971 | ')' +
972 | ')?)?)?)?' +
973 | '$');
974 |
975 | var months = [
976 | 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
977 | ];
978 |
979 | function dayFromMonth(year, month) {
980 | var t = month > 1 ? 1 : 0;
981 | return (
982 | months[month] +
983 | Math.floor((year - 1969 + t) / 4) -
984 | Math.floor((year - 1901 + t) / 100) +
985 | Math.floor((year - 1601 + t) / 400) +
986 | 365 * (year - 1970)
987 | );
988 | }
989 |
990 | function toUTC(t) {
991 | return Number(new NativeDate(1970, 0, 1, 0, 0, 0, t));
992 | }
993 |
994 | // Copy any custom methods a 3rd party library may have added
995 | for (var key in NativeDate) {
996 | Date[key] = NativeDate[key];
997 | }
998 |
999 | // Copy "native" methods explicitly; they may be non-enumerable
1000 | Date.now = NativeDate.now;
1001 | Date.UTC = NativeDate.UTC;
1002 | Date.prototype = NativeDate.prototype;
1003 | Date.prototype.constructor = Date;
1004 |
1005 | // Upgrade Date.parse to handle simplified ISO 8601 strings
1006 | Date.parse = function parse(string) {
1007 | var match = isoDateExpression.exec(string);
1008 | if (match) {
1009 | // parse months, days, hours, minutes, seconds, and milliseconds
1010 | // provide default values if necessary
1011 | // parse the UTC offset component
1012 | var year = Number(match[1]),
1013 | month = Number(match[2] || 1) - 1,
1014 | day = Number(match[3] || 1) - 1,
1015 | hour = Number(match[4] || 0),
1016 | minute = Number(match[5] || 0),
1017 | second = Number(match[6] || 0),
1018 | millisecond = Math.floor(Number(match[7] || 0) * 1000),
1019 | // When time zone is missed, local offset should be used
1020 | // (ES 5.1 bug)
1021 | // see https://bugs.ecmascript.org/show_bug.cgi?id=112
1022 | isLocalTime = Boolean(match[4] && !match[8]),
1023 | signOffset = match[9] === '-' ? 1 : -1,
1024 | hourOffset = Number(match[10] || 0),
1025 | minuteOffset = Number(match[11] || 0),
1026 | result;
1027 | if (
1028 | hour < (
1029 | minute > 0 || second > 0 || millisecond > 0 ?
1030 | 24 : 25
1031 | ) &&
1032 | minute < 60 && second < 60 && millisecond < 1000 &&
1033 | month > -1 && month < 12 && hourOffset < 24 &&
1034 | minuteOffset < 60 && // detect invalid offsets
1035 | day > -1 &&
1036 | day < (
1037 | dayFromMonth(year, month + 1) -
1038 | dayFromMonth(year, month)
1039 | )
1040 | ) {
1041 | result = (
1042 | (dayFromMonth(year, month) + day) * 24 +
1043 | hour +
1044 | hourOffset * signOffset
1045 | ) * 60;
1046 | result = (
1047 | (result + minute + minuteOffset * signOffset) * 60 +
1048 | second
1049 | ) * 1000 + millisecond;
1050 | if (isLocalTime) {
1051 | result = toUTC(result);
1052 | }
1053 | if (-8.64e15 <= result && result <= 8.64e15) {
1054 | return result;
1055 | }
1056 | }
1057 | return NaN;
1058 | }
1059 | return NativeDate.parse.apply(this, arguments);
1060 | };
1061 |
1062 | return Date;
1063 | })(Date);
1064 | }
1065 |
1066 | // ES5 15.9.4.4
1067 | // http://es5.github.com/#x15.9.4.4
1068 | if (!Date.now) {
1069 | Date.now = function now() {
1070 | return new Date().getTime();
1071 | };
1072 | }
1073 |
1074 |
1075 | //
1076 | // Number
1077 | // ======
1078 | //
1079 |
1080 | // ES5.1 15.7.4.5
1081 | // http://es5.github.com/#x15.7.4.5
1082 | var hasToFixedBugs = NumberPrototype.toFixed && (
1083 | (0.00008).toFixed(3) !== '0.000'
1084 | || (0.9).toFixed(0) !== '1'
1085 | || (1.255).toFixed(2) !== '1.25'
1086 | || (1000000000000000128).toFixed(0) !== '1000000000000000128'
1087 | );
1088 |
1089 | var toFixedHelpers = {
1090 | base: 1e7,
1091 | size: 6,
1092 | data: [0, 0, 0, 0, 0, 0],
1093 | multiply: function multiply(n, c) {
1094 | var i = -1;
1095 | while (++i < toFixedHelpers.size) {
1096 | c += n * toFixedHelpers.data[i];
1097 | toFixedHelpers.data[i] = c % toFixedHelpers.base;
1098 | c = Math.floor(c / toFixedHelpers.base);
1099 | }
1100 | },
1101 | divide: function divide(n) {
1102 | var i = toFixedHelpers.size, c = 0;
1103 | while (--i >= 0) {
1104 | c += toFixedHelpers.data[i];
1105 | toFixedHelpers.data[i] = Math.floor(c / n);
1106 | c = (c % n) * toFixedHelpers.base;
1107 | }
1108 | },
1109 | numToString: function numToString() {
1110 | var i = toFixedHelpers.size;
1111 | var s = '';
1112 | while (--i >= 0) {
1113 | if (s !== '' || i === 0 || toFixedHelpers.data[i] !== 0) {
1114 | var t = String(toFixedHelpers.data[i]);
1115 | if (s === '') {
1116 | s = t;
1117 | } else {
1118 | s += '0000000'.slice(0, 7 - t.length) + t;
1119 | }
1120 | }
1121 | }
1122 | return s;
1123 | },
1124 | pow: function pow(x, n, acc) {
1125 | return (n === 0 ? acc : (n % 2 === 1 ? pow(x, n - 1, acc * x) : pow(x * x, n / 2, acc)));
1126 | },
1127 | log: function log(x) {
1128 | var n = 0;
1129 | while (x >= 4096) {
1130 | n += 12;
1131 | x /= 4096;
1132 | }
1133 | while (x >= 2) {
1134 | n += 1;
1135 | x /= 2;
1136 | }
1137 | return n;
1138 | }
1139 | };
1140 |
1141 | defineProperties(NumberPrototype, {
1142 | toFixed: function toFixed(fractionDigits) {
1143 | var f, x, s, m, e, z, j, k;
1144 |
1145 | // Test for NaN and round fractionDigits down
1146 | f = Number(fractionDigits);
1147 | f = f !== f ? 0 : Math.floor(f);
1148 |
1149 | if (f < 0 || f > 20) {
1150 | throw new RangeError('Number.toFixed called with invalid number of decimals');
1151 | }
1152 |
1153 | x = Number(this);
1154 |
1155 | // Test for NaN
1156 | if (x !== x) {
1157 | return 'NaN';
1158 | }
1159 |
1160 | // If it is too big or small, return the string value of the number
1161 | if (x <= -1e21 || x >= 1e21) {
1162 | return String(x);
1163 | }
1164 |
1165 | s = '';
1166 |
1167 | if (x < 0) {
1168 | s = '-';
1169 | x = -x;
1170 | }
1171 |
1172 | m = '0';
1173 |
1174 | if (x > 1e-21) {
1175 | // 1e-21 < x < 1e21
1176 | // -70 < log2(x) < 70
1177 | e = toFixedHelpers.log(x * toFixedHelpers.pow(2, 69, 1)) - 69;
1178 | z = (e < 0 ? x * toFixedHelpers.pow(2, -e, 1) : x / toFixedHelpers.pow(2, e, 1));
1179 | z *= 0x10000000000000; // Math.pow(2, 52);
1180 | e = 52 - e;
1181 |
1182 | // -18 < e < 122
1183 | // x = z / 2 ^ e
1184 | if (e > 0) {
1185 | toFixedHelpers.multiply(0, z);
1186 | j = f;
1187 |
1188 | while (j >= 7) {
1189 | toFixedHelpers.multiply(1e7, 0);
1190 | j -= 7;
1191 | }
1192 |
1193 | toFixedHelpers.multiply(toFixedHelpers.pow(10, j, 1), 0);
1194 | j = e - 1;
1195 |
1196 | while (j >= 23) {
1197 | toFixedHelpers.divide(1 << 23);
1198 | j -= 23;
1199 | }
1200 |
1201 | toFixedHelpers.divide(1 << j);
1202 | toFixedHelpers.multiply(1, 1);
1203 | toFixedHelpers.divide(2);
1204 | m = toFixedHelpers.numToString();
1205 | } else {
1206 | toFixedHelpers.multiply(0, z);
1207 | toFixedHelpers.multiply(1 << (-e), 0);
1208 | m = toFixedHelpers.numToString() + '0.00000000000000000000'.slice(2, 2 + f);
1209 | }
1210 | }
1211 |
1212 | if (f > 0) {
1213 | k = m.length;
1214 |
1215 | if (k <= f) {
1216 | m = s + '0.0000000000000000000'.slice(0, f - k + 2) + m;
1217 | } else {
1218 | m = s + m.slice(0, k - f) + '.' + m.slice(k - f);
1219 | }
1220 | } else {
1221 | m = s + m;
1222 | }
1223 |
1224 | return m;
1225 | }
1226 | }, hasToFixedBugs);
1227 |
1228 |
1229 | //
1230 | // String
1231 | // ======
1232 | //
1233 |
1234 | // ES5 15.5.4.14
1235 | // http://es5.github.com/#x15.5.4.14
1236 |
1237 | // [bugfix, IE lt 9, firefox 4, Konqueror, Opera, obscure browsers]
1238 | // Many browsers do not split properly with regular expressions or they
1239 | // do not perform the split correctly under obscure conditions.
1240 | // See http://blog.stevenlevithan.com/archives/cross-browser-split
1241 | // I've tested in many browsers and this seems to cover the deviant ones:
1242 | // 'ab'.split(/(?:ab)*/) should be ["", ""], not [""]
1243 | // '.'.split(/(.?)(.?)/) should be ["", ".", "", ""], not ["", ""]
1244 | // 'tesst'.split(/(s)*/) should be ["t", undefined, "e", "s", "t"], not
1245 | // [undefined, "t", undefined, "e", ...]
1246 | // ''.split(/.?/) should be [], not [""]
1247 | // '.'.split(/()()/) should be ["."], not ["", "", "."]
1248 |
1249 | var string_split = StringPrototype.split;
1250 | if (
1251 | 'ab'.split(/(?:ab)*/).length !== 2 ||
1252 | '.'.split(/(.?)(.?)/).length !== 4 ||
1253 | 'tesst'.split(/(s)*/)[1] === 't' ||
1254 | 'test'.split(/(?:)/, -1).length !== 4 ||
1255 | ''.split(/.?/).length ||
1256 | '.'.split(/()()/).length > 1
1257 | ) {
1258 | (function () {
1259 | var compliantExecNpcg = /()??/.exec('')[1] === void 0; // NPCG: nonparticipating capturing group
1260 |
1261 | StringPrototype.split = function (separator, limit) {
1262 | var string = this;
1263 | if (separator === void 0 && limit === 0) {
1264 | return [];
1265 | }
1266 |
1267 | // If `separator` is not a regex, use native split
1268 | if (_toString.call(separator) !== '[object RegExp]') {
1269 | return string_split.call(this, separator, limit);
1270 | }
1271 |
1272 | var output = [],
1273 | flags = (separator.ignoreCase ? 'i' : '') +
1274 | (separator.multiline ? 'm' : '') +
1275 | (separator.extended ? 'x' : '') + // Proposed for ES6
1276 | (separator.sticky ? 'y' : ''), // Firefox 3+
1277 | lastLastIndex = 0,
1278 | // Make `global` and avoid `lastIndex` issues by working with a copy
1279 | separator2, match, lastIndex, lastLength;
1280 | separator = new RegExp(separator.source, flags + 'g');
1281 | string += ''; // Type-convert
1282 | if (!compliantExecNpcg) {
1283 | // Doesn't need flags gy, but they don't hurt
1284 | separator2 = new RegExp('^' + separator.source + '$(?!\\s)', flags);
1285 | }
1286 | /* Values for `limit`, per the spec:
1287 | * If undefined: 4294967295 // Math.pow(2, 32) - 1
1288 | * If 0, Infinity, or NaN: 0
1289 | * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
1290 | * If negative number: 4294967296 - Math.floor(Math.abs(limit))
1291 | * If other: Type-convert, then use the above rules
1292 | */
1293 | limit = limit === void 0 ?
1294 | -1 >>> 0 : // Math.pow(2, 32) - 1
1295 | ToUint32(limit);
1296 | while (match = separator.exec(string)) {
1297 | // `separator.lastIndex` is not reliable cross-browser
1298 | lastIndex = match.index + match[0].length;
1299 | if (lastIndex > lastLastIndex) {
1300 | output.push(string.slice(lastLastIndex, match.index));
1301 | // Fix browsers whose `exec` methods don't consistently return `undefined` for
1302 | // nonparticipating capturing groups
1303 | if (!compliantExecNpcg && match.length > 1) {
1304 | match[0].replace(separator2, function () {
1305 | for (var i = 1; i < arguments.length - 2; i++) {
1306 | if (arguments[i] === void 0) {
1307 | match[i] = void 0;
1308 | }
1309 | }
1310 | });
1311 | }
1312 | if (match.length > 1 && match.index < string.length) {
1313 | ArrayPrototype.push.apply(output, match.slice(1));
1314 | }
1315 | lastLength = match[0].length;
1316 | lastLastIndex = lastIndex;
1317 | if (output.length >= limit) {
1318 | break;
1319 | }
1320 | }
1321 | if (separator.lastIndex === match.index) {
1322 | separator.lastIndex++; // Avoid an infinite loop
1323 | }
1324 | }
1325 | if (lastLastIndex === string.length) {
1326 | if (lastLength || !separator.test('')) {
1327 | output.push('');
1328 | }
1329 | } else {
1330 | output.push(string.slice(lastLastIndex));
1331 | }
1332 | return output.length > limit ? output.slice(0, limit) : output;
1333 | };
1334 | }());
1335 |
1336 | // [bugfix, chrome]
1337 | // If separator is undefined, then the result array contains just one String,
1338 | // which is the this value (converted to a String). If limit is not undefined,
1339 | // then the output array is truncated so that it contains no more than limit
1340 | // elements.
1341 | // "0".split(undefined, 0) -> []
1342 | } else if ('0'.split(void 0, 0).length) {
1343 | StringPrototype.split = function split(separator, limit) {
1344 | if (separator === void 0 && limit === 0) { return []; }
1345 | return string_split.call(this, separator, limit);
1346 | };
1347 | }
1348 |
1349 | var str_replace = StringPrototype.replace;
1350 | var replaceReportsGroupsCorrectly = (function () {
1351 | var groups = [];
1352 | 'x'.replace(/x(.)?/g, function (match, group) {
1353 | groups.push(group);
1354 | });
1355 | return groups.length === 1 && typeof groups[0] === 'undefined';
1356 | }());
1357 |
1358 | if (!replaceReportsGroupsCorrectly) {
1359 | StringPrototype.replace = function replace(searchValue, replaceValue) {
1360 | var isFn = isFunction(replaceValue);
1361 | var hasCapturingGroups = isRegex(searchValue) && (/\)[*?]/).test(searchValue.source);
1362 | if (!isFn || !hasCapturingGroups) {
1363 | return str_replace.call(this, searchValue, replaceValue);
1364 | } else {
1365 | var wrappedReplaceValue = function (match) {
1366 | var length = arguments.length;
1367 | var originalLastIndex = searchValue.lastIndex;
1368 | searchValue.lastIndex = 0;
1369 | var args = searchValue.exec(match) || [];
1370 | searchValue.lastIndex = originalLastIndex;
1371 | args.push(arguments[length - 2], arguments[length - 1]);
1372 | return replaceValue.apply(this, args);
1373 | };
1374 | return str_replace.call(this, searchValue, wrappedReplaceValue);
1375 | }
1376 | };
1377 | }
1378 |
1379 | // ECMA-262, 3rd B.2.3
1380 | // Not an ECMAScript standard, although ECMAScript 3rd Edition has a
1381 | // non-normative section suggesting uniform semantics and it should be
1382 | // normalized across all browsers
1383 | // [bugfix, IE lt 9] IE < 9 substr() with negative value not working in IE
1384 | var string_substr = StringPrototype.substr;
1385 | var hasNegativeSubstrBug = ''.substr && '0b'.substr(-1) !== 'b';
1386 | defineProperties(StringPrototype, {
1387 | substr: function substr(start, length) {
1388 | return string_substr.call(
1389 | this,
1390 | start < 0 ? ((start = this.length + start) < 0 ? 0 : start) : start,
1391 | length
1392 | );
1393 | }
1394 | }, hasNegativeSubstrBug);
1395 |
1396 | // ES5 15.5.4.20
1397 | // whitespace from: http://es5.github.io/#x15.5.4.20
1398 | var ws = '\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003' +
1399 | '\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028' +
1400 | '\u2029\uFEFF';
1401 | var zeroWidth = '\u200b';
1402 | var wsRegexChars = '[' + ws + ']';
1403 | var trimBeginRegexp = new RegExp('^' + wsRegexChars + wsRegexChars + '*');
1404 | var trimEndRegexp = new RegExp(wsRegexChars + wsRegexChars + '*$');
1405 | var hasTrimWhitespaceBug = StringPrototype.trim && (ws.trim() || !zeroWidth.trim());
1406 | defineProperties(StringPrototype, {
1407 | // http://blog.stevenlevithan.com/archives/faster-trim-javascript
1408 | // http://perfectionkills.com/whitespace-deviations/
1409 | trim: function trim() {
1410 | if (this === void 0 || this === null) {
1411 | throw new TypeError("can't convert " + this + ' to object');
1412 | }
1413 | return String(this).replace(trimBeginRegexp, '').replace(trimEndRegexp, '');
1414 | }
1415 | }, hasTrimWhitespaceBug);
1416 |
1417 | // ES-5 15.1.2.2
1418 | if (parseInt(ws + '08') !== 8 || parseInt(ws + '0x16') !== 22) {
1419 | parseInt = (function (origParseInt) {
1420 | var hexRegex = /^0[xX]/;
1421 | return function parseIntES5(str, radix) {
1422 | str = String(str).trim();
1423 | if (!Number(radix)) {
1424 | radix = hexRegex.test(str) ? 16 : 10;
1425 | }
1426 | return origParseInt(str, radix);
1427 | };
1428 | }(parseInt));
1429 | }
1430 |
1431 | }));
1432 |
--------------------------------------------------------------------------------
/binary_demo/js/html5-shiv.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @preserve HTML5 Shiv 3.7.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
3 | */
4 | ;(function(window, document) {
5 | /*jshint evil:true */
6 | /** version */
7 | var version = '3.7.2';
8 |
9 | /** Preset options */
10 | var options = window.html5 || {};
11 |
12 | /** Used to skip problem elements */
13 | var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i;
14 |
15 | /** Not all elements can be cloned in IE **/
16 | var saveClones = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i;
17 |
18 | /** Detect whether the browser supports default html5 styles */
19 | var supportsHtml5Styles;
20 |
21 | /** Name of the expando, to work with multiple documents or to re-shiv one document */
22 | var expando = '_html5shiv';
23 |
24 | /** The id for the the documents expando */
25 | var expanID = 0;
26 |
27 | /** Cached data for each document */
28 | var expandoData = {};
29 |
30 | /** Detect whether the browser supports unknown elements */
31 | var supportsUnknownElements;
32 |
33 | (function() {
34 | try {
35 | var a = document.createElement('a');
36 | a.innerHTML = ' ';
37 | //if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles
38 | supportsHtml5Styles = ('hidden' in a);
39 |
40 | supportsUnknownElements = a.childNodes.length == 1 || (function() {
41 | // assign a false positive if unable to shiv
42 | (document.createElement)('a');
43 | var frag = document.createDocumentFragment();
44 | return (
45 | typeof frag.cloneNode == 'undefined' ||
46 | typeof frag.createDocumentFragment == 'undefined' ||
47 | typeof frag.createElement == 'undefined'
48 | );
49 | }());
50 | } catch(e) {
51 | // assign a false positive if detection fails => unable to shiv
52 | supportsHtml5Styles = true;
53 | supportsUnknownElements = true;
54 | }
55 |
56 | }());
57 |
58 | /*--------------------------------------------------------------------------*/
59 |
60 | /**
61 | * Creates a style sheet with the given CSS text and adds it to the document.
62 | * @private
63 | * @param {Document} ownerDocument The document.
64 | * @param {String} cssText The CSS text.
65 | * @returns {StyleSheet} The style element.
66 | */
67 | function addStyleSheet(ownerDocument, cssText) {
68 | var p = ownerDocument.createElement('p'),
69 | parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement;
70 |
71 | p.innerHTML = 'x';
72 | return parent.insertBefore(p.lastChild, parent.firstChild);
73 | }
74 |
75 | /**
76 | * Returns the value of `html5.elements` as an array.
77 | * @private
78 | * @returns {Array} An array of shived element node names.
79 | */
80 | function getElements() {
81 | var elements = html5.elements;
82 | return typeof elements == 'string' ? elements.split(' ') : elements;
83 | }
84 |
85 | /**
86 | * Extends the built-in list of html5 elements
87 | * @memberOf html5
88 | * @param {String|Array} newElements whitespace separated list or array of new element names to shiv
89 | * @param {Document} ownerDocument The context document.
90 | */
91 | function addElements(newElements, ownerDocument) {
92 | var elements = html5.elements;
93 | if(typeof elements != 'string'){
94 | elements = elements.join(' ');
95 | }
96 | if(typeof newElements != 'string'){
97 | newElements = newElements.join(' ');
98 | }
99 | html5.elements = elements +' '+ newElements;
100 | shivDocument(ownerDocument);
101 | }
102 |
103 | /**
104 | * Returns the data associated to the given document
105 | * @private
106 | * @param {Document} ownerDocument The document.
107 | * @returns {Object} An object of data.
108 | */
109 | function getExpandoData(ownerDocument) {
110 | var data = expandoData[ownerDocument[expando]];
111 | if (!data) {
112 | data = {};
113 | expanID++;
114 | ownerDocument[expando] = expanID;
115 | expandoData[expanID] = data;
116 | }
117 | return data;
118 | }
119 |
120 | /**
121 | * returns a shived element for the given nodeName and document
122 | * @memberOf html5
123 | * @param {String} nodeName name of the element
124 | * @param {Document} ownerDocument The context document.
125 | * @returns {Object} The shived element.
126 | */
127 | function createElement(nodeName, ownerDocument, data){
128 | if (!ownerDocument) {
129 | ownerDocument = document;
130 | }
131 | if(supportsUnknownElements){
132 | return ownerDocument.createElement(nodeName);
133 | }
134 | if (!data) {
135 | data = getExpandoData(ownerDocument);
136 | }
137 | var node;
138 |
139 | if (data.cache[nodeName]) {
140 | node = data.cache[nodeName].cloneNode();
141 | } else if (saveClones.test(nodeName)) {
142 | node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode();
143 | } else {
144 | node = data.createElem(nodeName);
145 | }
146 |
147 | // Avoid adding some elements to fragments in IE < 9 because
148 | // * Attributes like `name` or `type` cannot be set/changed once an element
149 | // is inserted into a document/fragment
150 | // * Link elements with `src` attributes that are inaccessible, as with
151 | // a 403 response, will cause the tab/window to crash
152 | // * Script elements appended to fragments will execute when their `src`
153 | // or `text` property is set
154 | return node.canHaveChildren && !reSkip.test(nodeName) && !node.tagUrn ? data.frag.appendChild(node) : node;
155 | }
156 |
157 | /**
158 | * returns a shived DocumentFragment for the given document
159 | * @memberOf html5
160 | * @param {Document} ownerDocument The context document.
161 | * @returns {Object} The shived DocumentFragment.
162 | */
163 | function createDocumentFragment(ownerDocument, data){
164 | if (!ownerDocument) {
165 | ownerDocument = document;
166 | }
167 | if(supportsUnknownElements){
168 | return ownerDocument.createDocumentFragment();
169 | }
170 | data = data || getExpandoData(ownerDocument);
171 | var clone = data.frag.cloneNode(),
172 | i = 0,
173 | elems = getElements(),
174 | l = elems.length;
175 | for(;i0})}return e};e.off=function(n,r){if(n=="*")t={};else if(r){var i=t[n];for(var o=0,u;u=i&&i[o];++o){if(u==r){i.splice(o,1);o--}}}else{n.replace(/\S+/g,function(e){t[e]=[]})}return e};e.one=function(t,n){if(n)n.one=1;return e.on(t,n)};e.trigger=function(n){var r=[].slice.call(arguments,1),i=t[n]||[];for(var o=0,u;u=i[o];++o){if(!u.busy){u.busy=1;u.apply(e,u.typed?[n].concat(r):r);if(u.one){i.splice(o,1);o--}else if(i[o]!==u){o--}u.busy=0}}return e};return e};(function(e,t){if(!this.top)return;var n=location,r=e.observable(),i=u(),o=window;function u(){return n.hash.slice(1)}function f(e){return e.split("/")}function a(e){if(e.type)e=u();if(e!=i){r.trigger.apply(null,["H"].concat(f(e)));i=e}}var l=e.route=function(e){if(e[0]){n.hash=e;a(e)}else{r.on("H",e)}};l.exec=function(e){e.apply(null,f(u()))};l.parser=function(e){f=e};o.addEventListener?o.addEventListener(t,a,false):o.attachEvent("on"+t,a)})(e,"hashchange");e._tmpl=function(){var e={},t=/("|').+?[^\\]\1|\.\w*|\w*:|\b(?:this|true|false|null|undefined|new|typeof|Number|String|Object|Array|Math|Date|JSON)\b|([a-z_]\w*)/gi;return function(t,r){return t&&(e[t]=e[t]||n(t))(r)};function n(e,t){t=(e||"{}").replace(/\\{/g,"").replace(/\\}/g,"").split(/({[\s\S]*?})/);return new Function("d","return "+(!t[0]&&!t[2]?r(t[1]):"["+t.map(function(e,t){return t%2?r(e,1):'"'+e.replace(/\n/g,"\\n").replace(/"/g,'\\"')+'"'}).join(",")+'].join("")').replace(/\uFFF0/g,"{").replace(/\uFFF1/g,"}"))}function r(e,t){e=e.replace(/\n/g," ").replace(/^[{ ]+|[ }]+$|\/\*.+?\*\//g,"");return/^\s*[\w-"']+ *:/.test(e)?"["+e.replace(/\W*([\w-]+)\W*:([^,]+)/g,function(e,n,r){return r.replace(/\w[^,|& ]*/g,function(e){return i(e,t)})+'?"'+n+'":"",'})+'].join(" ")':i(e,t)}function i(e,n){return"(function(v){try{v="+(e.replace(t,function(e,t,n){return n?"d."+n:e})||"x")+"}finally{return "+(n?'!v&&v!==0?"":v':"v")+"}}).call(d)"}}();(function(e,t){if(!t)return;var n=e._tmpl,r=[],i={},o=document;function u(e,t){for(var n=0;n<(e||[]).length;n++){if(t(e[n],n)===false)n--}}function f(e,t){t&&Object.keys(t).map(function(n){e[n]=t[n]});return e}function a(e,t){return e.filter(function(e){return t.indexOf(e)<0})}function l(e,t){e=t(e)===false?e.nextSibling:e.firstChild;while(e){l(e,t);e=e.nextSibling}}function c(e){var t=e.trim().slice(1,3).toLowerCase(),n=/td|th/.test(t)?"tr":t=="tr"?"tbody":"div";el=o.createElement(n);el.innerHTML=e;return el}function s(e,t){t.trigger("update");u(e,function(e){var r=e.tag,i=e.dom;function o(e){i.removeAttribute(e)}if(e.loop){o("each");return v(e,t)}if(r)return r.update?r.update():e.tag=d({tmpl:r[0],fn:r[1],root:i,parent:t});var u=e.attr,f=n(e.expr,t);if(f==null)f="";if(e.value===f)return;e.value=f;if(!u)return i.nodeValue=f;if(!f&&e.bool||/obj|func/.test(typeof f))o(u);if(typeof f=="function"){i[u]=function(e){e=e||window.event;e.which=e.which||e.charCode||e.keyCode;e.target=e.target||e.srcElement;e.currentTarget=i;e.item=t.__item||t;if(f.call(t,e)!==true){e.preventDefault&&e.preventDefault();e.returnValue=false}t.update()}}else if(/^(show|hide|if)$/.test(u)){o(u);if(u=="hide")f=!f;i.style.display=f?"":"none"}else{if(e.bool){i[u]=f;if(!f)return;f=u}i.setAttribute(u,f)}});t.trigger("updated")}function p(e){var t={},n=[];l(e,function(e){var n=e.nodeType,o=e.nodeValue;if(n==3&&e.parentNode.tagName!="STYLE"){r(e,o)}else if(n==1){o=e.getAttribute("each");if(o){r(e,o,{loop:1});return false}var f=i[e.tagName.toLowerCase()];u(e.attributes,function(n){var i=n.name,o=n.value;if(/^(name|id)$/.test(i))t[o]=e;if(!f){var u=i.split("__")[1];r(e,o,{attr:u||i,bool:u});if(u){e.removeAttribute(i);return false}}});if(f)r(e,0,{tag:f})}});return{expr:n,elem:t};function r(e,t,r){if(t?t.indexOf("{")>=0:r){var i={dom:e,expr:t};n.push(f(i,r||{}))}}}function d(t){var i=t.opts||{},a=c(t.tmpl),l=t.root,d=t.parent,v=p(a),m={root:l,opts:i,parent:d,__item:t.item},g={};f(m,v.elem);u(l.attributes,function(e){g[e.name]=e.value});function h(){Object.keys(g).map(function(e){var t=i[e]=n(g[e],d||m);if(typeof t=="object")l.removeAttribute(e)})}h();if(!m.on){e.observable(m);delete m.off}if(t.fn)t.fn.call(m,i);m.update=function(e,t){if(d&&a&&!a.firstChild){l=d.root;a=null}if(t||o.body.contains(l)){f(m,e);f(m,m.__item);h();s(v.expr,m);!t&&m.__item&&d.update();return true}else{m.trigger("unmount")}};m.update(0,true);while(a.firstChild){if(t.before)l.insertBefore(a.firstChild,t.before);else l.appendChild(a.firstChild)}m.trigger("mount");r.push(m);return m}function v(e,t){if(e.done)return;e.done=true;var r=e.dom,i=r.previousSibling,o=r.parentNode,u=r.outerHTML,f=e.expr,l=f.split(/\s+in\s+/),c=[],s,p;if(l[1]){f="{ "+l[1];p=l[0].slice(1).trim().split(/,\s*/)}t.one("mount",function(){var e=r.parentNode;if(e){o=e;o.removeChild(r)}});function v(){return Array.prototype.indexOf.call(o.childNodes,i)+1}t.on("updated",function(){var e=n(f,t);is_array=Array.isArray(e);if(is_array)e=e.slice(0);else{if(!e)return;var r=JSON.stringify(e);if(r==s)return;s=r;e=Object.keys(e).map(function(t,n){var r={};r[p[0]]=t;r[p[1]]=e[t];return r})}a(c,e).map(function(e){var t=c.indexOf(e);o.removeChild(o.childNodes[v()+t]);c.splice(t,1)});a(e,c).map(function(n,r){var i=e.indexOf(n);if(p&&!s){var f={};f[p[0]]=n;f[p[1]]=i;n=f}var a=d({before:o.childNodes[v()+i],parent:t,tmpl:u,item:n,root:o});t.on("update",function(){a.update(0,true)})});c=e})}e.tag=function(e,t,n){n=n||noop,i[e]=[t,n]};e.mountTo=function(e,t,n){var r=i[t];return r&&d({tmpl:r[0],fn:r[1],root:e,opts:n})};e.mount=function(t,n){if(t=="*")t=Object.keys(i).join(", ");var r=[];u(o.querySelectorAll(t),function(t){if(t.riot)return;var i=t.tagName.toLowerCase(),o=e.mountTo(t,i,n);if(o){r.push(o);t.riot=1}});return r};e.update=function(){return r=r.filter(function(e){return!!e.update()})}})(e,this.top);if(typeof exports==="object")module.exports=e;else if(typeof define==="function"&&define.amd)define(function(){return e});else this.riot=e})();
--------------------------------------------------------------------------------
/binary_demo/riotcontrol.js:
--------------------------------------------------------------------------------
1 | var _RiotControlApi = ['on','one','off','trigger']
2 | var RiotControl = {
3 | _stores: [],
4 | addStore: function(store) {
5 | this._stores.push(store)
6 | }
7 | }
8 | _RiotControlApi.forEach(function(api){
9 | RiotControl[api] = function() {
10 | var args = [].slice.call(arguments)
11 | this._stores.forEach(function(el){
12 | el[api].apply(null, args)
13 | })
14 | }
15 | })
16 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "RiotControl",
3 | "version": "0.0.3",
4 | "homepage": "https://github.com/jimsparkman/RiotControl",
5 | "authors": [
6 | "Jim Sparkman"
7 | ],
8 | "description": "Event Controller / Dispatcher For RiotJS, Inspired By Flux",
9 | "main": "riotcontrol.js",
10 | "moduleType": [
11 | "node"
12 | ],
13 | "keywords": [
14 | "riotjs",
15 | "flux"
16 | ],
17 | "license": "MIT",
18 | "ignore": [
19 | "**/.*",
20 | "node_modules",
21 | "bower_components",
22 | "test",
23 | "tests"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | RiotControl Demo
7 |
8 |
9 |
10 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | View on GitHub
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/demo/js/es5-shim.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * https://github.com/es-shims/es5-shim
3 | * @license es5-shim Copyright 2009-2014 by contributors, MIT License
4 | * see https://github.com/es-shims/es5-shim/blob/master/LICENSE
5 | */
6 |
7 | // vim: ts=4 sts=4 sw=4 expandtab
8 |
9 | //Add semicolon to prevent IIFE from being passed as argument to concatenated code.
10 | ;
11 |
12 | // UMD (Universal Module Definition)
13 | // see https://github.com/umdjs/umd/blob/master/returnExports.js
14 | (function (root, factory) {
15 | if (typeof define === 'function' && define.amd) {
16 | // AMD. Register as an anonymous module.
17 | define(factory);
18 | } else if (typeof exports === 'object') {
19 | // Node. Does not work with strict CommonJS, but
20 | // only CommonJS-like enviroments that support module.exports,
21 | // like Node.
22 | module.exports = factory();
23 | } else {
24 | // Browser globals (root is window)
25 | root.returnExports = factory();
26 | }
27 | }(this, function () {
28 |
29 | /**
30 | * Brings an environment as close to ECMAScript 5 compliance
31 | * as is possible with the facilities of erstwhile engines.
32 | *
33 | * Annotated ES5: http://es5.github.com/ (specific links below)
34 | * ES5 Spec: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf
35 | * Required reading: http://javascriptweblog.wordpress.com/2011/12/05/extending-javascript-natives/
36 | */
37 |
38 | // Shortcut to an often accessed properties, in order to avoid multiple
39 | // dereference that costs universally.
40 | var ArrayPrototype = Array.prototype;
41 | var ObjectPrototype = Object.prototype;
42 | var FunctionPrototype = Function.prototype;
43 | var StringPrototype = String.prototype;
44 | var NumberPrototype = Number.prototype;
45 | var array_slice = ArrayPrototype.slice;
46 | var array_splice = ArrayPrototype.splice;
47 | var array_push = ArrayPrototype.push;
48 | var array_unshift = ArrayPrototype.unshift;
49 | var call = FunctionPrototype.call;
50 |
51 | // Having a toString local variable name breaks in Opera so use _toString.
52 | var _toString = ObjectPrototype.toString;
53 |
54 | var isFunction = function (val) {
55 | return ObjectPrototype.toString.call(val) === '[object Function]';
56 | };
57 | var isRegex = function (val) {
58 | return ObjectPrototype.toString.call(val) === '[object RegExp]';
59 | };
60 | var isArray = function isArray(obj) {
61 | return _toString.call(obj) === '[object Array]';
62 | };
63 | var isString = function isString(obj) {
64 | return _toString.call(obj) === '[object String]';
65 | };
66 | var isArguments = function isArguments(value) {
67 | var str = _toString.call(value);
68 | var isArgs = str === '[object Arguments]';
69 | if (!isArgs) {
70 | isArgs = !isArray(value)
71 | && value !== null
72 | && typeof value === 'object'
73 | && typeof value.length === 'number'
74 | && value.length >= 0
75 | && isFunction(value.callee);
76 | }
77 | return isArgs;
78 | };
79 |
80 | var supportsDescriptors = Object.defineProperty && (function () {
81 | try {
82 | Object.defineProperty({}, 'x', {});
83 | return true;
84 | } catch (e) { /* this is ES3 */
85 | return false;
86 | }
87 | }());
88 |
89 | // Define configurable, writable and non-enumerable props
90 | // if they don't exist.
91 | var defineProperty;
92 | if (supportsDescriptors) {
93 | defineProperty = function (object, name, method, forceAssign) {
94 | if (!forceAssign && (name in object)) { return; }
95 | Object.defineProperty(object, name, {
96 | configurable: true,
97 | enumerable: false,
98 | writable: true,
99 | value: method
100 | });
101 | };
102 | } else {
103 | defineProperty = function (object, name, method, forceAssign) {
104 | if (!forceAssign && (name in object)) { return; }
105 | object[name] = method;
106 | };
107 | }
108 | var defineProperties = function (object, map, forceAssign) {
109 | for (var name in map) {
110 | if (ObjectPrototype.hasOwnProperty.call(map, name)) {
111 | defineProperty(object, name, map[name], forceAssign);
112 | }
113 | }
114 | };
115 |
116 | //
117 | // Util
118 | // ======
119 | //
120 |
121 | // ES5 9.4
122 | // http://es5.github.com/#x9.4
123 | // http://jsperf.com/to-integer
124 |
125 | function toInteger(num) {
126 | var n = +num;
127 | if (n !== n) { // isNaN
128 | n = 0;
129 | } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
130 | n = (n > 0 || -1) * Math.floor(Math.abs(n));
131 | }
132 | return n;
133 | }
134 |
135 | function isPrimitive(input) {
136 | var type = typeof input;
137 | return (
138 | input === null ||
139 | type === 'undefined' ||
140 | type === 'boolean' ||
141 | type === 'number' ||
142 | type === 'string'
143 | );
144 | }
145 |
146 | function toPrimitive(input) {
147 | var val, valueOf, toStr;
148 | if (isPrimitive(input)) {
149 | return input;
150 | }
151 | valueOf = input.valueOf;
152 | if (isFunction(valueOf)) {
153 | val = valueOf.call(input);
154 | if (isPrimitive(val)) {
155 | return val;
156 | }
157 | }
158 | toStr = input.toString;
159 | if (isFunction(toStr)) {
160 | val = toStr.call(input);
161 | if (isPrimitive(val)) {
162 | return val;
163 | }
164 | }
165 | throw new TypeError();
166 | }
167 |
168 | // ES5 9.9
169 | // http://es5.github.com/#x9.9
170 | var toObject = function (o) {
171 | if (o == null) { // this matches both null and undefined
172 | throw new TypeError("can't convert " + o + ' to object');
173 | }
174 | return Object(o);
175 | };
176 |
177 | var ToUint32 = function ToUint32(x) {
178 | return x >>> 0;
179 | };
180 |
181 | //
182 | // Function
183 | // ========
184 | //
185 |
186 | // ES-5 15.3.4.5
187 | // http://es5.github.com/#x15.3.4.5
188 |
189 | function Empty() {}
190 |
191 | defineProperties(FunctionPrototype, {
192 | bind: function bind(that) { // .length is 1
193 | // 1. Let Target be the this value.
194 | var target = this;
195 | // 2. If IsCallable(Target) is false, throw a TypeError exception.
196 | if (!isFunction(target)) {
197 | throw new TypeError('Function.prototype.bind called on incompatible ' + target);
198 | }
199 | // 3. Let A be a new (possibly empty) internal list of all of the
200 | // argument values provided after thisArg (arg1, arg2 etc), in order.
201 | // XXX slicedArgs will stand in for "A" if used
202 | var args = array_slice.call(arguments, 1); // for normal call
203 | // 4. Let F be a new native ECMAScript object.
204 | // 11. Set the [[Prototype]] internal property of F to the standard
205 | // built-in Function prototype object as specified in 15.3.3.1.
206 | // 12. Set the [[Call]] internal property of F as described in
207 | // 15.3.4.5.1.
208 | // 13. Set the [[Construct]] internal property of F as described in
209 | // 15.3.4.5.2.
210 | // 14. Set the [[HasInstance]] internal property of F as described in
211 | // 15.3.4.5.3.
212 | var binder = function () {
213 |
214 | if (this instanceof bound) {
215 | // 15.3.4.5.2 [[Construct]]
216 | // When the [[Construct]] internal method of a function object,
217 | // F that was created using the bind function is called with a
218 | // list of arguments ExtraArgs, the following steps are taken:
219 | // 1. Let target be the value of F's [[TargetFunction]]
220 | // internal property.
221 | // 2. If target has no [[Construct]] internal method, a
222 | // TypeError exception is thrown.
223 | // 3. Let boundArgs be the value of F's [[BoundArgs]] internal
224 | // property.
225 | // 4. Let args be a new list containing the same values as the
226 | // list boundArgs in the same order followed by the same
227 | // values as the list ExtraArgs in the same order.
228 | // 5. Return the result of calling the [[Construct]] internal
229 | // method of target providing args as the arguments.
230 |
231 | var result = target.apply(
232 | this,
233 | args.concat(array_slice.call(arguments))
234 | );
235 | if (Object(result) === result) {
236 | return result;
237 | }
238 | return this;
239 |
240 | } else {
241 | // 15.3.4.5.1 [[Call]]
242 | // When the [[Call]] internal method of a function object, F,
243 | // which was created using the bind function is called with a
244 | // this value and a list of arguments ExtraArgs, the following
245 | // steps are taken:
246 | // 1. Let boundArgs be the value of F's [[BoundArgs]] internal
247 | // property.
248 | // 2. Let boundThis be the value of F's [[BoundThis]] internal
249 | // property.
250 | // 3. Let target be the value of F's [[TargetFunction]] internal
251 | // property.
252 | // 4. Let args be a new list containing the same values as the
253 | // list boundArgs in the same order followed by the same
254 | // values as the list ExtraArgs in the same order.
255 | // 5. Return the result of calling the [[Call]] internal method
256 | // of target providing boundThis as the this value and
257 | // providing args as the arguments.
258 |
259 | // equiv: target.call(this, ...boundArgs, ...args)
260 | return target.apply(
261 | that,
262 | args.concat(array_slice.call(arguments))
263 | );
264 |
265 | }
266 |
267 | };
268 |
269 | // 15. If the [[Class]] internal property of Target is "Function", then
270 | // a. Let L be the length property of Target minus the length of A.
271 | // b. Set the length own property of F to either 0 or L, whichever is
272 | // larger.
273 | // 16. Else set the length own property of F to 0.
274 |
275 | var boundLength = Math.max(0, target.length - args.length);
276 |
277 | // 17. Set the attributes of the length own property of F to the values
278 | // specified in 15.3.5.1.
279 | var boundArgs = [];
280 | for (var i = 0; i < boundLength; i++) {
281 | boundArgs.push('$' + i);
282 | }
283 |
284 | // XXX Build a dynamic function with desired amount of arguments is the only
285 | // way to set the length property of a function.
286 | // In environments where Content Security Policies enabled (Chrome extensions,
287 | // for ex.) all use of eval or Function costructor throws an exception.
288 | // However in all of these environments Function.prototype.bind exists
289 | // and so this code will never be executed.
290 | var bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this, arguments); }')(binder);
291 |
292 | if (target.prototype) {
293 | Empty.prototype = target.prototype;
294 | bound.prototype = new Empty();
295 | // Clean up dangling references.
296 | Empty.prototype = null;
297 | }
298 |
299 | // TODO
300 | // 18. Set the [[Extensible]] internal property of F to true.
301 |
302 | // TODO
303 | // 19. Let thrower be the [[ThrowTypeError]] function Object (13.2.3).
304 | // 20. Call the [[DefineOwnProperty]] internal method of F with
305 | // arguments "caller", PropertyDescriptor {[[Get]]: thrower, [[Set]]:
306 | // thrower, [[Enumerable]]: false, [[Configurable]]: false}, and
307 | // false.
308 | // 21. Call the [[DefineOwnProperty]] internal method of F with
309 | // arguments "arguments", PropertyDescriptor {[[Get]]: thrower,
310 | // [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: false},
311 | // and false.
312 |
313 | // TODO
314 | // NOTE Function objects created using Function.prototype.bind do not
315 | // have a prototype property or the [[Code]], [[FormalParameters]], and
316 | // [[Scope]] internal properties.
317 | // XXX can't delete prototype in pure-js.
318 |
319 | // 22. Return F.
320 | return bound;
321 | }
322 | });
323 |
324 | // _Please note: Shortcuts are defined after `Function.prototype.bind` as we
325 | // us it in defining shortcuts.
326 | var owns = call.bind(ObjectPrototype.hasOwnProperty);
327 |
328 | // If JS engine supports accessors creating shortcuts.
329 | var defineGetter;
330 | var defineSetter;
331 | var lookupGetter;
332 | var lookupSetter;
333 | var supportsAccessors;
334 | if ((supportsAccessors = owns(ObjectPrototype, '__defineGetter__'))) {
335 | defineGetter = call.bind(ObjectPrototype.__defineGetter__);
336 | defineSetter = call.bind(ObjectPrototype.__defineSetter__);
337 | lookupGetter = call.bind(ObjectPrototype.__lookupGetter__);
338 | lookupSetter = call.bind(ObjectPrototype.__lookupSetter__);
339 | }
340 |
341 | //
342 | // Array
343 | // =====
344 | //
345 |
346 | // ES5 15.4.4.12
347 | // http://es5.github.com/#x15.4.4.12
348 | var spliceNoopReturnsEmptyArray = (function () {
349 | var a = [1, 2];
350 | var result = a.splice();
351 | return a.length === 2 && isArray(result) && result.length === 0;
352 | }());
353 | defineProperties(ArrayPrototype, {
354 | // Safari 5.0 bug where .splice() returns undefined
355 | splice: function splice(start, deleteCount) {
356 | if (arguments.length === 0) {
357 | return [];
358 | } else {
359 | return array_splice.apply(this, arguments);
360 | }
361 | }
362 | }, spliceNoopReturnsEmptyArray);
363 |
364 | var spliceWorksWithEmptyObject = (function () {
365 | var obj = {};
366 | ArrayPrototype.splice.call(obj, 0, 0, 1);
367 | return obj.length === 1;
368 | }());
369 | defineProperties(ArrayPrototype, {
370 | splice: function splice(start, deleteCount) {
371 | if (arguments.length === 0) { return []; }
372 | var args = arguments;
373 | this.length = Math.max(toInteger(this.length), 0);
374 | if (arguments.length > 0 && typeof deleteCount !== 'number') {
375 | args = array_slice.call(arguments);
376 | if (args.length < 2) {
377 | args.push(this.length - start);
378 | } else {
379 | args[1] = toInteger(deleteCount);
380 | }
381 | }
382 | return array_splice.apply(this, args);
383 | }
384 | }, !spliceWorksWithEmptyObject);
385 |
386 | // ES5 15.4.4.12
387 | // http://es5.github.com/#x15.4.4.13
388 | // Return len+argCount.
389 | // [bugfix, ielt8]
390 | // IE < 8 bug: [].unshift(0) === undefined but should be "1"
391 | var hasUnshiftReturnValueBug = [].unshift(0) !== 1;
392 | defineProperties(ArrayPrototype, {
393 | unshift: function () {
394 | array_unshift.apply(this, arguments);
395 | return this.length;
396 | }
397 | }, hasUnshiftReturnValueBug);
398 |
399 | // ES5 15.4.3.2
400 | // http://es5.github.com/#x15.4.3.2
401 | // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/isArray
402 | defineProperties(Array, { isArray: isArray });
403 |
404 | // The IsCallable() check in the Array functions
405 | // has been replaced with a strict check on the
406 | // internal class of the object to trap cases where
407 | // the provided function was actually a regular
408 | // expression literal, which in V8 and
409 | // JavaScriptCore is a typeof "function". Only in
410 | // V8 are regular expression literals permitted as
411 | // reduce parameters, so it is desirable in the
412 | // general case for the shim to match the more
413 | // strict and common behavior of rejecting regular
414 | // expressions.
415 |
416 | // ES5 15.4.4.18
417 | // http://es5.github.com/#x15.4.4.18
418 | // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/forEach
419 |
420 | // Check failure of by-index access of string characters (IE < 9)
421 | // and failure of `0 in boxedString` (Rhino)
422 | var boxedString = Object('a');
423 | var splitString = boxedString[0] !== 'a' || !(0 in boxedString);
424 |
425 | var properlyBoxesContext = function properlyBoxed(method) {
426 | // Check node 0.6.21 bug where third parameter is not boxed
427 | var properlyBoxesNonStrict = true;
428 | var properlyBoxesStrict = true;
429 | if (method) {
430 | method.call('foo', function (_, __, context) {
431 | if (typeof context !== 'object') { properlyBoxesNonStrict = false; }
432 | });
433 |
434 | method.call([1], function () {
435 | 'use strict';
436 | properlyBoxesStrict = typeof this === 'string';
437 | }, 'x');
438 | }
439 | return !!method && properlyBoxesNonStrict && properlyBoxesStrict;
440 | };
441 |
442 | defineProperties(ArrayPrototype, {
443 | forEach: function forEach(fun /*, thisp*/) {
444 | var object = toObject(this),
445 | self = splitString && isString(this) ? this.split('') : object,
446 | thisp = arguments[1],
447 | i = -1,
448 | length = self.length >>> 0;
449 |
450 | // If no callback function or if callback is not a callable function
451 | if (!isFunction(fun)) {
452 | throw new TypeError(); // TODO message
453 | }
454 |
455 | while (++i < length) {
456 | if (i in self) {
457 | // Invoke the callback function with call, passing arguments:
458 | // context, property value, property key, thisArg object
459 | // context
460 | fun.call(thisp, self[i], i, object);
461 | }
462 | }
463 | }
464 | }, !properlyBoxesContext(ArrayPrototype.forEach));
465 |
466 | // ES5 15.4.4.19
467 | // http://es5.github.com/#x15.4.4.19
468 | // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map
469 | defineProperties(ArrayPrototype, {
470 | map: function map(fun /*, thisp*/) {
471 | var object = toObject(this),
472 | self = splitString && isString(this) ? this.split('') : object,
473 | length = self.length >>> 0,
474 | result = Array(length),
475 | thisp = arguments[1];
476 |
477 | // If no callback function or if callback is not a callable function
478 | if (!isFunction(fun)) {
479 | throw new TypeError(fun + ' is not a function');
480 | }
481 |
482 | for (var i = 0; i < length; i++) {
483 | if (i in self) {
484 | result[i] = fun.call(thisp, self[i], i, object);
485 | }
486 | }
487 | return result;
488 | }
489 | }, !properlyBoxesContext(ArrayPrototype.map));
490 |
491 | // ES5 15.4.4.20
492 | // http://es5.github.com/#x15.4.4.20
493 | // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/filter
494 | defineProperties(ArrayPrototype, {
495 | filter: function filter(fun /*, thisp */) {
496 | var object = toObject(this),
497 | self = splitString && isString(this) ? this.split('') : object,
498 | length = self.length >>> 0,
499 | result = [],
500 | value,
501 | thisp = arguments[1];
502 |
503 | // If no callback function or if callback is not a callable function
504 | if (!isFunction(fun)) {
505 | throw new TypeError(fun + ' is not a function');
506 | }
507 |
508 | for (var i = 0; i < length; i++) {
509 | if (i in self) {
510 | value = self[i];
511 | if (fun.call(thisp, value, i, object)) {
512 | result.push(value);
513 | }
514 | }
515 | }
516 | return result;
517 | }
518 | }, !properlyBoxesContext(ArrayPrototype.filter));
519 |
520 | // ES5 15.4.4.16
521 | // http://es5.github.com/#x15.4.4.16
522 | // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/every
523 | defineProperties(ArrayPrototype, {
524 | every: function every(fun /*, thisp */) {
525 | var object = toObject(this),
526 | self = splitString && isString(this) ? this.split('') : object,
527 | length = self.length >>> 0,
528 | thisp = arguments[1];
529 |
530 | // If no callback function or if callback is not a callable function
531 | if (!isFunction(fun)) {
532 | throw new TypeError(fun + ' is not a function');
533 | }
534 |
535 | for (var i = 0; i < length; i++) {
536 | if (i in self && !fun.call(thisp, self[i], i, object)) {
537 | return false;
538 | }
539 | }
540 | return true;
541 | }
542 | }, !properlyBoxesContext(ArrayPrototype.every));
543 |
544 | // ES5 15.4.4.17
545 | // http://es5.github.com/#x15.4.4.17
546 | // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/some
547 | defineProperties(ArrayPrototype, {
548 | some: function some(fun /*, thisp */) {
549 | var object = toObject(this),
550 | self = splitString && isString(this) ? this.split('') : object,
551 | length = self.length >>> 0,
552 | thisp = arguments[1];
553 |
554 | // If no callback function or if callback is not a callable function
555 | if (!isFunction(fun)) {
556 | throw new TypeError(fun + ' is not a function');
557 | }
558 |
559 | for (var i = 0; i < length; i++) {
560 | if (i in self && fun.call(thisp, self[i], i, object)) {
561 | return true;
562 | }
563 | }
564 | return false;
565 | }
566 | }, !properlyBoxesContext(ArrayPrototype.some));
567 |
568 | // ES5 15.4.4.21
569 | // http://es5.github.com/#x15.4.4.21
570 | // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduce
571 | var reduceCoercesToObject = false;
572 | if (ArrayPrototype.reduce) {
573 | reduceCoercesToObject = typeof ArrayPrototype.reduce.call('es5', function (_, __, ___, list) { return list; }) === 'object';
574 | }
575 | defineProperties(ArrayPrototype, {
576 | reduce: function reduce(fun /*, initial*/) {
577 | var object = toObject(this),
578 | self = splitString && isString(this) ? this.split('') : object,
579 | length = self.length >>> 0;
580 |
581 | // If no callback function or if callback is not a callable function
582 | if (!isFunction(fun)) {
583 | throw new TypeError(fun + ' is not a function');
584 | }
585 |
586 | // no value to return if no initial value and an empty array
587 | if (!length && arguments.length === 1) {
588 | throw new TypeError('reduce of empty array with no initial value');
589 | }
590 |
591 | var i = 0;
592 | var result;
593 | if (arguments.length >= 2) {
594 | result = arguments[1];
595 | } else {
596 | do {
597 | if (i in self) {
598 | result = self[i++];
599 | break;
600 | }
601 |
602 | // if array contains no values, no initial value to return
603 | if (++i >= length) {
604 | throw new TypeError('reduce of empty array with no initial value');
605 | }
606 | } while (true);
607 | }
608 |
609 | for (; i < length; i++) {
610 | if (i in self) {
611 | result = fun.call(void 0, result, self[i], i, object);
612 | }
613 | }
614 |
615 | return result;
616 | }
617 | }, !reduceCoercesToObject);
618 |
619 | // ES5 15.4.4.22
620 | // http://es5.github.com/#x15.4.4.22
621 | // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduceRight
622 | var reduceRightCoercesToObject = false;
623 | if (ArrayPrototype.reduceRight) {
624 | reduceRightCoercesToObject = typeof ArrayPrototype.reduceRight.call('es5', function (_, __, ___, list) { return list; }) === 'object';
625 | }
626 | defineProperties(ArrayPrototype, {
627 | reduceRight: function reduceRight(fun /*, initial*/) {
628 | var object = toObject(this),
629 | self = splitString && isString(this) ? this.split('') : object,
630 | length = self.length >>> 0;
631 |
632 | // If no callback function or if callback is not a callable function
633 | if (!isFunction(fun)) {
634 | throw new TypeError(fun + ' is not a function');
635 | }
636 |
637 | // no value to return if no initial value, empty array
638 | if (!length && arguments.length === 1) {
639 | throw new TypeError('reduceRight of empty array with no initial value');
640 | }
641 |
642 | var result, i = length - 1;
643 | if (arguments.length >= 2) {
644 | result = arguments[1];
645 | } else {
646 | do {
647 | if (i in self) {
648 | result = self[i--];
649 | break;
650 | }
651 |
652 | // if array contains no values, no initial value to return
653 | if (--i < 0) {
654 | throw new TypeError('reduceRight of empty array with no initial value');
655 | }
656 | } while (true);
657 | }
658 |
659 | if (i < 0) {
660 | return result;
661 | }
662 |
663 | do {
664 | if (i in self) {
665 | result = fun.call(void 0, result, self[i], i, object);
666 | }
667 | } while (i--);
668 |
669 | return result;
670 | }
671 | }, !reduceRightCoercesToObject);
672 |
673 | // ES5 15.4.4.14
674 | // http://es5.github.com/#x15.4.4.14
675 | // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf
676 | var hasFirefox2IndexOfBug = Array.prototype.indexOf && [0, 1].indexOf(1, 2) !== -1;
677 | defineProperties(ArrayPrototype, {
678 | indexOf: function indexOf(sought /*, fromIndex */ ) {
679 | var self = splitString && isString(this) ? this.split('') : toObject(this),
680 | length = self.length >>> 0;
681 |
682 | if (!length) {
683 | return -1;
684 | }
685 |
686 | var i = 0;
687 | if (arguments.length > 1) {
688 | i = toInteger(arguments[1]);
689 | }
690 |
691 | // handle negative indices
692 | i = i >= 0 ? i : Math.max(0, length + i);
693 | for (; i < length; i++) {
694 | if (i in self && self[i] === sought) {
695 | return i;
696 | }
697 | }
698 | return -1;
699 | }
700 | }, hasFirefox2IndexOfBug);
701 |
702 | // ES5 15.4.4.15
703 | // http://es5.github.com/#x15.4.4.15
704 | // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/lastIndexOf
705 | var hasFirefox2LastIndexOfBug = Array.prototype.lastIndexOf && [0, 1].lastIndexOf(0, -3) !== -1;
706 | defineProperties(ArrayPrototype, {
707 | lastIndexOf: function lastIndexOf(sought /*, fromIndex */) {
708 | var self = splitString && isString(this) ? this.split('') : toObject(this),
709 | length = self.length >>> 0;
710 |
711 | if (!length) {
712 | return -1;
713 | }
714 | var i = length - 1;
715 | if (arguments.length > 1) {
716 | i = Math.min(i, toInteger(arguments[1]));
717 | }
718 | // handle negative indices
719 | i = i >= 0 ? i : length - Math.abs(i);
720 | for (; i >= 0; i--) {
721 | if (i in self && sought === self[i]) {
722 | return i;
723 | }
724 | }
725 | return -1;
726 | }
727 | }, hasFirefox2LastIndexOfBug);
728 |
729 | //
730 | // Object
731 | // ======
732 | //
733 |
734 | // ES5 15.2.3.14
735 | // http://es5.github.com/#x15.2.3.14
736 |
737 | // http://whattheheadsaid.com/2010/10/a-safer-object-keys-compatibility-implementation
738 | var hasDontEnumBug = !({'toString': null}).propertyIsEnumerable('toString'),
739 | hasProtoEnumBug = (function () {}).propertyIsEnumerable('prototype'),
740 | dontEnums = [
741 | 'toString',
742 | 'toLocaleString',
743 | 'valueOf',
744 | 'hasOwnProperty',
745 | 'isPrototypeOf',
746 | 'propertyIsEnumerable',
747 | 'constructor'
748 | ],
749 | dontEnumsLength = dontEnums.length;
750 |
751 | defineProperties(Object, {
752 | keys: function keys(object) {
753 | var isFn = isFunction(object),
754 | isArgs = isArguments(object),
755 | isObject = object !== null && typeof object === 'object',
756 | isStr = isObject && isString(object);
757 |
758 | if (!isObject && !isFn && !isArgs) {
759 | throw new TypeError('Object.keys called on a non-object');
760 | }
761 |
762 | var theKeys = [];
763 | var skipProto = hasProtoEnumBug && isFn;
764 | if (isStr || isArgs) {
765 | for (var i = 0; i < object.length; ++i) {
766 | theKeys.push(String(i));
767 | }
768 | } else {
769 | for (var name in object) {
770 | if (!(skipProto && name === 'prototype') && owns(object, name)) {
771 | theKeys.push(String(name));
772 | }
773 | }
774 | }
775 |
776 | if (hasDontEnumBug) {
777 | var ctor = object.constructor,
778 | skipConstructor = ctor && ctor.prototype === object;
779 | for (var j = 0; j < dontEnumsLength; j++) {
780 | var dontEnum = dontEnums[j];
781 | if (!(skipConstructor && dontEnum === 'constructor') && owns(object, dontEnum)) {
782 | theKeys.push(dontEnum);
783 | }
784 | }
785 | }
786 | return theKeys;
787 | }
788 | });
789 |
790 | var keysWorksWithArguments = Object.keys && (function () {
791 | // Safari 5.0 bug
792 | return Object.keys(arguments).length === 2;
793 | }(1, 2));
794 | var originalKeys = Object.keys;
795 | defineProperties(Object, {
796 | keys: function keys(object) {
797 | if (isArguments(object)) {
798 | return originalKeys(ArrayPrototype.slice.call(object));
799 | } else {
800 | return originalKeys(object);
801 | }
802 | }
803 | }, !keysWorksWithArguments);
804 |
805 | //
806 | // Date
807 | // ====
808 | //
809 |
810 | // ES5 15.9.5.43
811 | // http://es5.github.com/#x15.9.5.43
812 | // This function returns a String value represent the instance in time
813 | // represented by this Date object. The format of the String is the Date Time
814 | // string format defined in 15.9.1.15. All fields are present in the String.
815 | // The time zone is always UTC, denoted by the suffix Z. If the time value of
816 | // this object is not a finite Number a RangeError exception is thrown.
817 | var negativeDate = -62198755200000;
818 | var negativeYearString = '-000001';
819 | var hasNegativeDateBug = Date.prototype.toISOString && new Date(negativeDate).toISOString().indexOf(negativeYearString) === -1;
820 |
821 | defineProperties(Date.prototype, {
822 | toISOString: function toISOString() {
823 | var result, length, value, year, month;
824 | if (!isFinite(this)) {
825 | throw new RangeError('Date.prototype.toISOString called on non-finite value.');
826 | }
827 |
828 | year = this.getUTCFullYear();
829 |
830 | month = this.getUTCMonth();
831 | // see https://github.com/es-shims/es5-shim/issues/111
832 | year += Math.floor(month / 12);
833 | month = (month % 12 + 12) % 12;
834 |
835 | // the date time string format is specified in 15.9.1.15.
836 | result = [month + 1, this.getUTCDate(), this.getUTCHours(), this.getUTCMinutes(), this.getUTCSeconds()];
837 | year = (
838 | (year < 0 ? '-' : (year > 9999 ? '+' : '')) +
839 | ('00000' + Math.abs(year)).slice(0 <= year && year <= 9999 ? -4 : -6)
840 | );
841 |
842 | length = result.length;
843 | while (length--) {
844 | value = result[length];
845 | // pad months, days, hours, minutes, and seconds to have two
846 | // digits.
847 | if (value < 10) {
848 | result[length] = '0' + value;
849 | }
850 | }
851 | // pad milliseconds to have three digits.
852 | return (
853 | year + '-' + result.slice(0, 2).join('-') +
854 | 'T' + result.slice(2).join(':') + '.' +
855 | ('000' + this.getUTCMilliseconds()).slice(-3) + 'Z'
856 | );
857 | }
858 | }, hasNegativeDateBug);
859 |
860 |
861 | // ES5 15.9.5.44
862 | // http://es5.github.com/#x15.9.5.44
863 | // This function provides a String representation of a Date object for use by
864 | // JSON.stringify (15.12.3).
865 | var dateToJSONIsSupported = false;
866 | try {
867 | dateToJSONIsSupported = (
868 | Date.prototype.toJSON &&
869 | new Date(NaN).toJSON() === null &&
870 | new Date(negativeDate).toJSON().indexOf(negativeYearString) !== -1 &&
871 | Date.prototype.toJSON.call({ // generic
872 | toISOString: function () {
873 | return true;
874 | }
875 | })
876 | );
877 | } catch (e) {
878 | }
879 | if (!dateToJSONIsSupported) {
880 | Date.prototype.toJSON = function toJSON(key) {
881 | // When the toJSON method is called with argument key, the following
882 | // steps are taken:
883 |
884 | // 1. Let O be the result of calling ToObject, giving it the this
885 | // value as its argument.
886 | // 2. Let tv be toPrimitive(O, hint Number).
887 | var o = Object(this),
888 | tv = toPrimitive(o),
889 | toISO;
890 | // 3. If tv is a Number and is not finite, return null.
891 | if (typeof tv === 'number' && !isFinite(tv)) {
892 | return null;
893 | }
894 | // 4. Let toISO be the result of calling the [[Get]] internal method of
895 | // O with argument "toISOString".
896 | toISO = o.toISOString;
897 | // 5. If IsCallable(toISO) is false, throw a TypeError exception.
898 | if (typeof toISO !== 'function') {
899 | throw new TypeError('toISOString property is not callable');
900 | }
901 | // 6. Return the result of calling the [[Call]] internal method of
902 | // toISO with O as the this value and an empty argument list.
903 | return toISO.call(o);
904 |
905 | // NOTE 1 The argument is ignored.
906 |
907 | // NOTE 2 The toJSON function is intentionally generic; it does not
908 | // require that its this value be a Date object. Therefore, it can be
909 | // transferred to other kinds of objects for use as a method. However,
910 | // it does require that any such object have a toISOString method. An
911 | // object is free to use the argument key to filter its
912 | // stringification.
913 | };
914 | }
915 |
916 | // ES5 15.9.4.2
917 | // http://es5.github.com/#x15.9.4.2
918 | // based on work shared by Daniel Friesen (dantman)
919 | // http://gist.github.com/303249
920 | var supportsExtendedYears = Date.parse('+033658-09-27T01:46:40.000Z') === 1e15;
921 | var acceptsInvalidDates = !isNaN(Date.parse('2012-04-04T24:00:00.500Z')) || !isNaN(Date.parse('2012-11-31T23:59:59.000Z'));
922 | var doesNotParseY2KNewYear = isNaN(Date.parse('2000-01-01T00:00:00.000Z'));
923 | if (!Date.parse || doesNotParseY2KNewYear || acceptsInvalidDates || !supportsExtendedYears) {
924 | // XXX global assignment won't work in embeddings that use
925 | // an alternate object for the context.
926 | Date = (function (NativeDate) {
927 |
928 | // Date.length === 7
929 | function Date(Y, M, D, h, m, s, ms) {
930 | var length = arguments.length;
931 | if (this instanceof NativeDate) {
932 | var date = length === 1 && String(Y) === Y ? // isString(Y)
933 | // We explicitly pass it through parse:
934 | new NativeDate(Date.parse(Y)) :
935 | // We have to manually make calls depending on argument
936 | // length here
937 | length >= 7 ? new NativeDate(Y, M, D, h, m, s, ms) :
938 | length >= 6 ? new NativeDate(Y, M, D, h, m, s) :
939 | length >= 5 ? new NativeDate(Y, M, D, h, m) :
940 | length >= 4 ? new NativeDate(Y, M, D, h) :
941 | length >= 3 ? new NativeDate(Y, M, D) :
942 | length >= 2 ? new NativeDate(Y, M) :
943 | length >= 1 ? new NativeDate(Y) :
944 | new NativeDate();
945 | // Prevent mixups with unfixed Date object
946 | date.constructor = Date;
947 | return date;
948 | }
949 | return NativeDate.apply(this, arguments);
950 | }
951 |
952 | // 15.9.1.15 Date Time String Format.
953 | var isoDateExpression = new RegExp('^' +
954 | '(\\d{4}|[\+\-]\\d{6})' + // four-digit year capture or sign +
955 | // 6-digit extended year
956 | '(?:-(\\d{2})' + // optional month capture
957 | '(?:-(\\d{2})' + // optional day capture
958 | '(?:' + // capture hours:minutes:seconds.milliseconds
959 | 'T(\\d{2})' + // hours capture
960 | ':(\\d{2})' + // minutes capture
961 | '(?:' + // optional :seconds.milliseconds
962 | ':(\\d{2})' + // seconds capture
963 | '(?:(\\.\\d{1,}))?' + // milliseconds capture
964 | ')?' +
965 | '(' + // capture UTC offset component
966 | 'Z|' + // UTC capture
967 | '(?:' + // offset specifier +/-hours:minutes
968 | '([-+])' + // sign capture
969 | '(\\d{2})' + // hours offset capture
970 | ':(\\d{2})' + // minutes offset capture
971 | ')' +
972 | ')?)?)?)?' +
973 | '$');
974 |
975 | var months = [
976 | 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
977 | ];
978 |
979 | function dayFromMonth(year, month) {
980 | var t = month > 1 ? 1 : 0;
981 | return (
982 | months[month] +
983 | Math.floor((year - 1969 + t) / 4) -
984 | Math.floor((year - 1901 + t) / 100) +
985 | Math.floor((year - 1601 + t) / 400) +
986 | 365 * (year - 1970)
987 | );
988 | }
989 |
990 | function toUTC(t) {
991 | return Number(new NativeDate(1970, 0, 1, 0, 0, 0, t));
992 | }
993 |
994 | // Copy any custom methods a 3rd party library may have added
995 | for (var key in NativeDate) {
996 | Date[key] = NativeDate[key];
997 | }
998 |
999 | // Copy "native" methods explicitly; they may be non-enumerable
1000 | Date.now = NativeDate.now;
1001 | Date.UTC = NativeDate.UTC;
1002 | Date.prototype = NativeDate.prototype;
1003 | Date.prototype.constructor = Date;
1004 |
1005 | // Upgrade Date.parse to handle simplified ISO 8601 strings
1006 | Date.parse = function parse(string) {
1007 | var match = isoDateExpression.exec(string);
1008 | if (match) {
1009 | // parse months, days, hours, minutes, seconds, and milliseconds
1010 | // provide default values if necessary
1011 | // parse the UTC offset component
1012 | var year = Number(match[1]),
1013 | month = Number(match[2] || 1) - 1,
1014 | day = Number(match[3] || 1) - 1,
1015 | hour = Number(match[4] || 0),
1016 | minute = Number(match[5] || 0),
1017 | second = Number(match[6] || 0),
1018 | millisecond = Math.floor(Number(match[7] || 0) * 1000),
1019 | // When time zone is missed, local offset should be used
1020 | // (ES 5.1 bug)
1021 | // see https://bugs.ecmascript.org/show_bug.cgi?id=112
1022 | isLocalTime = Boolean(match[4] && !match[8]),
1023 | signOffset = match[9] === '-' ? 1 : -1,
1024 | hourOffset = Number(match[10] || 0),
1025 | minuteOffset = Number(match[11] || 0),
1026 | result;
1027 | if (
1028 | hour < (
1029 | minute > 0 || second > 0 || millisecond > 0 ?
1030 | 24 : 25
1031 | ) &&
1032 | minute < 60 && second < 60 && millisecond < 1000 &&
1033 | month > -1 && month < 12 && hourOffset < 24 &&
1034 | minuteOffset < 60 && // detect invalid offsets
1035 | day > -1 &&
1036 | day < (
1037 | dayFromMonth(year, month + 1) -
1038 | dayFromMonth(year, month)
1039 | )
1040 | ) {
1041 | result = (
1042 | (dayFromMonth(year, month) + day) * 24 +
1043 | hour +
1044 | hourOffset * signOffset
1045 | ) * 60;
1046 | result = (
1047 | (result + minute + minuteOffset * signOffset) * 60 +
1048 | second
1049 | ) * 1000 + millisecond;
1050 | if (isLocalTime) {
1051 | result = toUTC(result);
1052 | }
1053 | if (-8.64e15 <= result && result <= 8.64e15) {
1054 | return result;
1055 | }
1056 | }
1057 | return NaN;
1058 | }
1059 | return NativeDate.parse.apply(this, arguments);
1060 | };
1061 |
1062 | return Date;
1063 | })(Date);
1064 | }
1065 |
1066 | // ES5 15.9.4.4
1067 | // http://es5.github.com/#x15.9.4.4
1068 | if (!Date.now) {
1069 | Date.now = function now() {
1070 | return new Date().getTime();
1071 | };
1072 | }
1073 |
1074 |
1075 | //
1076 | // Number
1077 | // ======
1078 | //
1079 |
1080 | // ES5.1 15.7.4.5
1081 | // http://es5.github.com/#x15.7.4.5
1082 | var hasToFixedBugs = NumberPrototype.toFixed && (
1083 | (0.00008).toFixed(3) !== '0.000'
1084 | || (0.9).toFixed(0) !== '1'
1085 | || (1.255).toFixed(2) !== '1.25'
1086 | || (1000000000000000128).toFixed(0) !== '1000000000000000128'
1087 | );
1088 |
1089 | var toFixedHelpers = {
1090 | base: 1e7,
1091 | size: 6,
1092 | data: [0, 0, 0, 0, 0, 0],
1093 | multiply: function multiply(n, c) {
1094 | var i = -1;
1095 | while (++i < toFixedHelpers.size) {
1096 | c += n * toFixedHelpers.data[i];
1097 | toFixedHelpers.data[i] = c % toFixedHelpers.base;
1098 | c = Math.floor(c / toFixedHelpers.base);
1099 | }
1100 | },
1101 | divide: function divide(n) {
1102 | var i = toFixedHelpers.size, c = 0;
1103 | while (--i >= 0) {
1104 | c += toFixedHelpers.data[i];
1105 | toFixedHelpers.data[i] = Math.floor(c / n);
1106 | c = (c % n) * toFixedHelpers.base;
1107 | }
1108 | },
1109 | numToString: function numToString() {
1110 | var i = toFixedHelpers.size;
1111 | var s = '';
1112 | while (--i >= 0) {
1113 | if (s !== '' || i === 0 || toFixedHelpers.data[i] !== 0) {
1114 | var t = String(toFixedHelpers.data[i]);
1115 | if (s === '') {
1116 | s = t;
1117 | } else {
1118 | s += '0000000'.slice(0, 7 - t.length) + t;
1119 | }
1120 | }
1121 | }
1122 | return s;
1123 | },
1124 | pow: function pow(x, n, acc) {
1125 | return (n === 0 ? acc : (n % 2 === 1 ? pow(x, n - 1, acc * x) : pow(x * x, n / 2, acc)));
1126 | },
1127 | log: function log(x) {
1128 | var n = 0;
1129 | while (x >= 4096) {
1130 | n += 12;
1131 | x /= 4096;
1132 | }
1133 | while (x >= 2) {
1134 | n += 1;
1135 | x /= 2;
1136 | }
1137 | return n;
1138 | }
1139 | };
1140 |
1141 | defineProperties(NumberPrototype, {
1142 | toFixed: function toFixed(fractionDigits) {
1143 | var f, x, s, m, e, z, j, k;
1144 |
1145 | // Test for NaN and round fractionDigits down
1146 | f = Number(fractionDigits);
1147 | f = f !== f ? 0 : Math.floor(f);
1148 |
1149 | if (f < 0 || f > 20) {
1150 | throw new RangeError('Number.toFixed called with invalid number of decimals');
1151 | }
1152 |
1153 | x = Number(this);
1154 |
1155 | // Test for NaN
1156 | if (x !== x) {
1157 | return 'NaN';
1158 | }
1159 |
1160 | // If it is too big or small, return the string value of the number
1161 | if (x <= -1e21 || x >= 1e21) {
1162 | return String(x);
1163 | }
1164 |
1165 | s = '';
1166 |
1167 | if (x < 0) {
1168 | s = '-';
1169 | x = -x;
1170 | }
1171 |
1172 | m = '0';
1173 |
1174 | if (x > 1e-21) {
1175 | // 1e-21 < x < 1e21
1176 | // -70 < log2(x) < 70
1177 | e = toFixedHelpers.log(x * toFixedHelpers.pow(2, 69, 1)) - 69;
1178 | z = (e < 0 ? x * toFixedHelpers.pow(2, -e, 1) : x / toFixedHelpers.pow(2, e, 1));
1179 | z *= 0x10000000000000; // Math.pow(2, 52);
1180 | e = 52 - e;
1181 |
1182 | // -18 < e < 122
1183 | // x = z / 2 ^ e
1184 | if (e > 0) {
1185 | toFixedHelpers.multiply(0, z);
1186 | j = f;
1187 |
1188 | while (j >= 7) {
1189 | toFixedHelpers.multiply(1e7, 0);
1190 | j -= 7;
1191 | }
1192 |
1193 | toFixedHelpers.multiply(toFixedHelpers.pow(10, j, 1), 0);
1194 | j = e - 1;
1195 |
1196 | while (j >= 23) {
1197 | toFixedHelpers.divide(1 << 23);
1198 | j -= 23;
1199 | }
1200 |
1201 | toFixedHelpers.divide(1 << j);
1202 | toFixedHelpers.multiply(1, 1);
1203 | toFixedHelpers.divide(2);
1204 | m = toFixedHelpers.numToString();
1205 | } else {
1206 | toFixedHelpers.multiply(0, z);
1207 | toFixedHelpers.multiply(1 << (-e), 0);
1208 | m = toFixedHelpers.numToString() + '0.00000000000000000000'.slice(2, 2 + f);
1209 | }
1210 | }
1211 |
1212 | if (f > 0) {
1213 | k = m.length;
1214 |
1215 | if (k <= f) {
1216 | m = s + '0.0000000000000000000'.slice(0, f - k + 2) + m;
1217 | } else {
1218 | m = s + m.slice(0, k - f) + '.' + m.slice(k - f);
1219 | }
1220 | } else {
1221 | m = s + m;
1222 | }
1223 |
1224 | return m;
1225 | }
1226 | }, hasToFixedBugs);
1227 |
1228 |
1229 | //
1230 | // String
1231 | // ======
1232 | //
1233 |
1234 | // ES5 15.5.4.14
1235 | // http://es5.github.com/#x15.5.4.14
1236 |
1237 | // [bugfix, IE lt 9, firefox 4, Konqueror, Opera, obscure browsers]
1238 | // Many browsers do not split properly with regular expressions or they
1239 | // do not perform the split correctly under obscure conditions.
1240 | // See http://blog.stevenlevithan.com/archives/cross-browser-split
1241 | // I've tested in many browsers and this seems to cover the deviant ones:
1242 | // 'ab'.split(/(?:ab)*/) should be ["", ""], not [""]
1243 | // '.'.split(/(.?)(.?)/) should be ["", ".", "", ""], not ["", ""]
1244 | // 'tesst'.split(/(s)*/) should be ["t", undefined, "e", "s", "t"], not
1245 | // [undefined, "t", undefined, "e", ...]
1246 | // ''.split(/.?/) should be [], not [""]
1247 | // '.'.split(/()()/) should be ["."], not ["", "", "."]
1248 |
1249 | var string_split = StringPrototype.split;
1250 | if (
1251 | 'ab'.split(/(?:ab)*/).length !== 2 ||
1252 | '.'.split(/(.?)(.?)/).length !== 4 ||
1253 | 'tesst'.split(/(s)*/)[1] === 't' ||
1254 | 'test'.split(/(?:)/, -1).length !== 4 ||
1255 | ''.split(/.?/).length ||
1256 | '.'.split(/()()/).length > 1
1257 | ) {
1258 | (function () {
1259 | var compliantExecNpcg = /()??/.exec('')[1] === void 0; // NPCG: nonparticipating capturing group
1260 |
1261 | StringPrototype.split = function (separator, limit) {
1262 | var string = this;
1263 | if (separator === void 0 && limit === 0) {
1264 | return [];
1265 | }
1266 |
1267 | // If `separator` is not a regex, use native split
1268 | if (_toString.call(separator) !== '[object RegExp]') {
1269 | return string_split.call(this, separator, limit);
1270 | }
1271 |
1272 | var output = [],
1273 | flags = (separator.ignoreCase ? 'i' : '') +
1274 | (separator.multiline ? 'm' : '') +
1275 | (separator.extended ? 'x' : '') + // Proposed for ES6
1276 | (separator.sticky ? 'y' : ''), // Firefox 3+
1277 | lastLastIndex = 0,
1278 | // Make `global` and avoid `lastIndex` issues by working with a copy
1279 | separator2, match, lastIndex, lastLength;
1280 | separator = new RegExp(separator.source, flags + 'g');
1281 | string += ''; // Type-convert
1282 | if (!compliantExecNpcg) {
1283 | // Doesn't need flags gy, but they don't hurt
1284 | separator2 = new RegExp('^' + separator.source + '$(?!\\s)', flags);
1285 | }
1286 | /* Values for `limit`, per the spec:
1287 | * If undefined: 4294967295 // Math.pow(2, 32) - 1
1288 | * If 0, Infinity, or NaN: 0
1289 | * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
1290 | * If negative number: 4294967296 - Math.floor(Math.abs(limit))
1291 | * If other: Type-convert, then use the above rules
1292 | */
1293 | limit = limit === void 0 ?
1294 | -1 >>> 0 : // Math.pow(2, 32) - 1
1295 | ToUint32(limit);
1296 | while (match = separator.exec(string)) {
1297 | // `separator.lastIndex` is not reliable cross-browser
1298 | lastIndex = match.index + match[0].length;
1299 | if (lastIndex > lastLastIndex) {
1300 | output.push(string.slice(lastLastIndex, match.index));
1301 | // Fix browsers whose `exec` methods don't consistently return `undefined` for
1302 | // nonparticipating capturing groups
1303 | if (!compliantExecNpcg && match.length > 1) {
1304 | match[0].replace(separator2, function () {
1305 | for (var i = 1; i < arguments.length - 2; i++) {
1306 | if (arguments[i] === void 0) {
1307 | match[i] = void 0;
1308 | }
1309 | }
1310 | });
1311 | }
1312 | if (match.length > 1 && match.index < string.length) {
1313 | ArrayPrototype.push.apply(output, match.slice(1));
1314 | }
1315 | lastLength = match[0].length;
1316 | lastLastIndex = lastIndex;
1317 | if (output.length >= limit) {
1318 | break;
1319 | }
1320 | }
1321 | if (separator.lastIndex === match.index) {
1322 | separator.lastIndex++; // Avoid an infinite loop
1323 | }
1324 | }
1325 | if (lastLastIndex === string.length) {
1326 | if (lastLength || !separator.test('')) {
1327 | output.push('');
1328 | }
1329 | } else {
1330 | output.push(string.slice(lastLastIndex));
1331 | }
1332 | return output.length > limit ? output.slice(0, limit) : output;
1333 | };
1334 | }());
1335 |
1336 | // [bugfix, chrome]
1337 | // If separator is undefined, then the result array contains just one String,
1338 | // which is the this value (converted to a String). If limit is not undefined,
1339 | // then the output array is truncated so that it contains no more than limit
1340 | // elements.
1341 | // "0".split(undefined, 0) -> []
1342 | } else if ('0'.split(void 0, 0).length) {
1343 | StringPrototype.split = function split(separator, limit) {
1344 | if (separator === void 0 && limit === 0) { return []; }
1345 | return string_split.call(this, separator, limit);
1346 | };
1347 | }
1348 |
1349 | var str_replace = StringPrototype.replace;
1350 | var replaceReportsGroupsCorrectly = (function () {
1351 | var groups = [];
1352 | 'x'.replace(/x(.)?/g, function (match, group) {
1353 | groups.push(group);
1354 | });
1355 | return groups.length === 1 && typeof groups[0] === 'undefined';
1356 | }());
1357 |
1358 | if (!replaceReportsGroupsCorrectly) {
1359 | StringPrototype.replace = function replace(searchValue, replaceValue) {
1360 | var isFn = isFunction(replaceValue);
1361 | var hasCapturingGroups = isRegex(searchValue) && (/\)[*?]/).test(searchValue.source);
1362 | if (!isFn || !hasCapturingGroups) {
1363 | return str_replace.call(this, searchValue, replaceValue);
1364 | } else {
1365 | var wrappedReplaceValue = function (match) {
1366 | var length = arguments.length;
1367 | var originalLastIndex = searchValue.lastIndex;
1368 | searchValue.lastIndex = 0;
1369 | var args = searchValue.exec(match) || [];
1370 | searchValue.lastIndex = originalLastIndex;
1371 | args.push(arguments[length - 2], arguments[length - 1]);
1372 | return replaceValue.apply(this, args);
1373 | };
1374 | return str_replace.call(this, searchValue, wrappedReplaceValue);
1375 | }
1376 | };
1377 | }
1378 |
1379 | // ECMA-262, 3rd B.2.3
1380 | // Not an ECMAScript standard, although ECMAScript 3rd Edition has a
1381 | // non-normative section suggesting uniform semantics and it should be
1382 | // normalized across all browsers
1383 | // [bugfix, IE lt 9] IE < 9 substr() with negative value not working in IE
1384 | var string_substr = StringPrototype.substr;
1385 | var hasNegativeSubstrBug = ''.substr && '0b'.substr(-1) !== 'b';
1386 | defineProperties(StringPrototype, {
1387 | substr: function substr(start, length) {
1388 | return string_substr.call(
1389 | this,
1390 | start < 0 ? ((start = this.length + start) < 0 ? 0 : start) : start,
1391 | length
1392 | );
1393 | }
1394 | }, hasNegativeSubstrBug);
1395 |
1396 | // ES5 15.5.4.20
1397 | // whitespace from: http://es5.github.io/#x15.5.4.20
1398 | var ws = '\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003' +
1399 | '\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028' +
1400 | '\u2029\uFEFF';
1401 | var zeroWidth = '\u200b';
1402 | var wsRegexChars = '[' + ws + ']';
1403 | var trimBeginRegexp = new RegExp('^' + wsRegexChars + wsRegexChars + '*');
1404 | var trimEndRegexp = new RegExp(wsRegexChars + wsRegexChars + '*$');
1405 | var hasTrimWhitespaceBug = StringPrototype.trim && (ws.trim() || !zeroWidth.trim());
1406 | defineProperties(StringPrototype, {
1407 | // http://blog.stevenlevithan.com/archives/faster-trim-javascript
1408 | // http://perfectionkills.com/whitespace-deviations/
1409 | trim: function trim() {
1410 | if (this === void 0 || this === null) {
1411 | throw new TypeError("can't convert " + this + ' to object');
1412 | }
1413 | return String(this).replace(trimBeginRegexp, '').replace(trimEndRegexp, '');
1414 | }
1415 | }, hasTrimWhitespaceBug);
1416 |
1417 | // ES-5 15.1.2.2
1418 | if (parseInt(ws + '08') !== 8 || parseInt(ws + '0x16') !== 22) {
1419 | parseInt = (function (origParseInt) {
1420 | var hexRegex = /^0[xX]/;
1421 | return function parseIntES5(str, radix) {
1422 | str = String(str).trim();
1423 | if (!Number(radix)) {
1424 | radix = hexRegex.test(str) ? 16 : 10;
1425 | }
1426 | return origParseInt(str, radix);
1427 | };
1428 | }(parseInt));
1429 | }
1430 |
1431 | }));
1432 |
--------------------------------------------------------------------------------
/demo/js/html5-shiv.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @preserve HTML5 Shiv 3.7.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
3 | */
4 | ;(function(window, document) {
5 | /*jshint evil:true */
6 | /** version */
7 | var version = '3.7.2';
8 |
9 | /** Preset options */
10 | var options = window.html5 || {};
11 |
12 | /** Used to skip problem elements */
13 | var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i;
14 |
15 | /** Not all elements can be cloned in IE **/
16 | var saveClones = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i;
17 |
18 | /** Detect whether the browser supports default html5 styles */
19 | var supportsHtml5Styles;
20 |
21 | /** Name of the expando, to work with multiple documents or to re-shiv one document */
22 | var expando = '_html5shiv';
23 |
24 | /** The id for the the documents expando */
25 | var expanID = 0;
26 |
27 | /** Cached data for each document */
28 | var expandoData = {};
29 |
30 | /** Detect whether the browser supports unknown elements */
31 | var supportsUnknownElements;
32 |
33 | (function() {
34 | try {
35 | var a = document.createElement('a');
36 | a.innerHTML = ' ';
37 | //if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles
38 | supportsHtml5Styles = ('hidden' in a);
39 |
40 | supportsUnknownElements = a.childNodes.length == 1 || (function() {
41 | // assign a false positive if unable to shiv
42 | (document.createElement)('a');
43 | var frag = document.createDocumentFragment();
44 | return (
45 | typeof frag.cloneNode == 'undefined' ||
46 | typeof frag.createDocumentFragment == 'undefined' ||
47 | typeof frag.createElement == 'undefined'
48 | );
49 | }());
50 | } catch(e) {
51 | // assign a false positive if detection fails => unable to shiv
52 | supportsHtml5Styles = true;
53 | supportsUnknownElements = true;
54 | }
55 |
56 | }());
57 |
58 | /*--------------------------------------------------------------------------*/
59 |
60 | /**
61 | * Creates a style sheet with the given CSS text and adds it to the document.
62 | * @private
63 | * @param {Document} ownerDocument The document.
64 | * @param {String} cssText The CSS text.
65 | * @returns {StyleSheet} The style element.
66 | */
67 | function addStyleSheet(ownerDocument, cssText) {
68 | var p = ownerDocument.createElement('p'),
69 | parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement;
70 |
71 | p.innerHTML = 'x';
72 | return parent.insertBefore(p.lastChild, parent.firstChild);
73 | }
74 |
75 | /**
76 | * Returns the value of `html5.elements` as an array.
77 | * @private
78 | * @returns {Array} An array of shived element node names.
79 | */
80 | function getElements() {
81 | var elements = html5.elements;
82 | return typeof elements == 'string' ? elements.split(' ') : elements;
83 | }
84 |
85 | /**
86 | * Extends the built-in list of html5 elements
87 | * @memberOf html5
88 | * @param {String|Array} newElements whitespace separated list or array of new element names to shiv
89 | * @param {Document} ownerDocument The context document.
90 | */
91 | function addElements(newElements, ownerDocument) {
92 | var elements = html5.elements;
93 | if(typeof elements != 'string'){
94 | elements = elements.join(' ');
95 | }
96 | if(typeof newElements != 'string'){
97 | newElements = newElements.join(' ');
98 | }
99 | html5.elements = elements +' '+ newElements;
100 | shivDocument(ownerDocument);
101 | }
102 |
103 | /**
104 | * Returns the data associated to the given document
105 | * @private
106 | * @param {Document} ownerDocument The document.
107 | * @returns {Object} An object of data.
108 | */
109 | function getExpandoData(ownerDocument) {
110 | var data = expandoData[ownerDocument[expando]];
111 | if (!data) {
112 | data = {};
113 | expanID++;
114 | ownerDocument[expando] = expanID;
115 | expandoData[expanID] = data;
116 | }
117 | return data;
118 | }
119 |
120 | /**
121 | * returns a shived element for the given nodeName and document
122 | * @memberOf html5
123 | * @param {String} nodeName name of the element
124 | * @param {Document} ownerDocument The context document.
125 | * @returns {Object} The shived element.
126 | */
127 | function createElement(nodeName, ownerDocument, data){
128 | if (!ownerDocument) {
129 | ownerDocument = document;
130 | }
131 | if(supportsUnknownElements){
132 | return ownerDocument.createElement(nodeName);
133 | }
134 | if (!data) {
135 | data = getExpandoData(ownerDocument);
136 | }
137 | var node;
138 |
139 | if (data.cache[nodeName]) {
140 | node = data.cache[nodeName].cloneNode();
141 | } else if (saveClones.test(nodeName)) {
142 | node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode();
143 | } else {
144 | node = data.createElem(nodeName);
145 | }
146 |
147 | // Avoid adding some elements to fragments in IE < 9 because
148 | // * Attributes like `name` or `type` cannot be set/changed once an element
149 | // is inserted into a document/fragment
150 | // * Link elements with `src` attributes that are inaccessible, as with
151 | // a 403 response, will cause the tab/window to crash
152 | // * Script elements appended to fragments will execute when their `src`
153 | // or `text` property is set
154 | return node.canHaveChildren && !reSkip.test(nodeName) && !node.tagUrn ? data.frag.appendChild(node) : node;
155 | }
156 |
157 | /**
158 | * returns a shived DocumentFragment for the given document
159 | * @memberOf html5
160 | * @param {Document} ownerDocument The context document.
161 | * @returns {Object} The shived DocumentFragment.
162 | */
163 | function createDocumentFragment(ownerDocument, data){
164 | if (!ownerDocument) {
165 | ownerDocument = document;
166 | }
167 | if(supportsUnknownElements){
168 | return ownerDocument.createDocumentFragment();
169 | }
170 | data = data || getExpandoData(ownerDocument);
171 | var clone = data.frag.cloneNode(),
172 | i = 0,
173 | elems = getElements(),
174 | l = elems.length;
175 | for(;i 0
12 | })
13 | }
14 | return el
15 | }
16 |
17 | el.off = function(events, fn) {
18 | if (events == '*') callbacks = {}
19 | else if (fn) {
20 | var arr = callbacks[events]
21 | for (var i = 0, cb; (cb = arr && arr[i]); ++i) {
22 | if (cb == fn) { arr.splice(i, 1); i-- }
23 | }
24 | } else {
25 | events.replace(/\S+/g, function(name) {
26 | callbacks[name] = []
27 | })
28 | }
29 | return el
30 | }
31 |
32 | // only single event supported
33 | el.one = function(name, fn) {
34 | if (fn) fn.one = 1
35 | return el.on(name, fn)
36 | }
37 |
38 | el.trigger = function(name) {
39 | var args = [].slice.call(arguments, 1),
40 | fns = callbacks[name] || []
41 |
42 | for (var i = 0, fn; (fn = fns[i]); ++i) {
43 | if (!fn.busy) {
44 | fn.busy = 1
45 | fn.apply(el, fn.typed ? [name].concat(args) : args)
46 | if (fn.one) { fns.splice(i, 1); i-- }
47 | fn.busy = 0
48 | }
49 | }
50 |
51 | return el
52 | }
53 |
54 | return el
55 |
56 | }
57 | ;(function(riot, evt) {
58 |
59 | // browsers only
60 | if (!this.top) return
61 |
62 | var loc = location,
63 | fns = riot.observable({}),
64 | current = hash(),
65 | win = window
66 |
67 | function hash() {
68 | return loc.hash.slice(1)
69 | }
70 |
71 | function emit(path) {
72 | if (path.type) path = hash()
73 |
74 | if (path != current) {
75 | fns.trigger.apply(null, ['H'].concat(path.split('/')))
76 | current = path
77 | }
78 | }
79 |
80 | var r = riot.route = function(arg) {
81 | // string
82 | if (arg[0]) {
83 | loc.hash = arg
84 | emit(arg)
85 |
86 | // function
87 | } else {
88 | fns.on('H', arg)
89 | }
90 | }
91 |
92 | r.exec = function(fn) {
93 | fn.apply(null, hash().split('/'))
94 | }
95 |
96 | win.addEventListener ? win.addEventListener(evt, emit, false) : win.attachEvent('on' + evt, emit)
97 |
98 | })(riot, 'hashchange')
99 | /*
100 |
101 | //// How it works?
102 |
103 |
104 | Three ways:
105 |
106 | 1. Expressions: tmpl('{ value }', data).
107 | Returns the result of evaluated expression as a raw object.
108 |
109 | 2. Templates: tmpl('Hi { name } { surname }', data).
110 | Returns a string with evaluated expressions.
111 |
112 | 3. Filters: tmpl('{ show: !done, highlight: active }', data).
113 | Returns a space separated list of trueish keys (mainly
114 | used for setting html classes), e.g. "show highlight".
115 |
116 |
117 | // Template examples
118 |
119 | tmpl('{ title || "Untitled" }', data)
120 | tmpl('Results are { results ? "ready" : "loading" }', data)
121 | tmpl('Today is { new Date() }', data)
122 | tmpl('{ message.length > 140 && "Message is too long" }', data)
123 | tmpl('This item got { Math.round(rating) } stars', data)
124 | tmpl('{ title } { body }', data)
125 |
126 |
127 | // Falsy expressions in templates
128 |
129 | In templates (as opposed to single expressions) all falsy values
130 | except zero (undefined/null/false) will default to empty string:
131 |
132 | tmpl('{ undefined } - { false } - { null } - { 0 }', {})
133 | // will return: " - - - 0"
134 |
135 | */
136 |
137 | riot._tmpl = (function() {
138 |
139 | var cache = {},
140 |
141 | // find variable names
142 | re_vars = /("|').+?[^\\]\1|\.\w*|\w*:|\b(?:this|true|false|null|new|typeof|Number|String|Object|Array|Math|Date)\b|([a-z_]\w*)/gi
143 | // [ 1 ][ 2 ][ 3 ][ 4 ][ 5 ]
144 | // 1. skip quoted strings: "a b", 'a b', 'a \'b\''
145 | // 2. skip object properties: .name
146 | // 3. skip object literals: name:
147 | // 4. skip reserved words
148 | // 5. match var name
149 |
150 | // build a template (or get it from cache), render with data
151 |
152 | return function(str, data) {
153 | return str && (cache[str] = cache[str] || tmpl(str))(data)
154 | }
155 |
156 |
157 | // create a template instance
158 |
159 | function tmpl(s, p) {
160 | p = (s || '{}')
161 |
162 | // temporarily convert \{ and \} to a non-character
163 | .replace(/\\{/g, '\uFFF0')
164 | .replace(/\\}/g, '\uFFF1')
165 |
166 | // split string to expression and non-expresion parts
167 | .split(/({[\s\S]*?})/)
168 |
169 | return new Function('d', 'return ' + (
170 |
171 | // is it a single expression or a template? i.e. {x} or {x}
172 | !p[0] && !p[2]
173 |
174 | // if expression, evaluate it
175 | ? expr(p[1])
176 |
177 | // if template, evaluate all expressions in it
178 | : '[' + p.map(function(s, i) {
179 |
180 | // is it an expression or a string (every second part is an expression)
181 | return i % 2
182 |
183 | // evaluate the expressions
184 | ? expr(s, 1)
185 |
186 | // process string parts of the template:
187 | : '"' + s
188 |
189 | // preserve new lines
190 | .replace(/\n/g, '\\n')
191 |
192 | // escape quotes
193 | .replace(/"/g, '\\"')
194 |
195 | + '"'
196 |
197 | }).join(',') + '].join("")'
198 | )
199 |
200 | // bring escaped { and } back
201 | .replace(/\uFFF0/g, '{')
202 | .replace(/\uFFF1/g, '}')
203 |
204 | )
205 |
206 | }
207 |
208 |
209 | // parse { ... } expression
210 |
211 | function expr(s, n) {
212 | s = s
213 |
214 | // convert new lines to spaces
215 | .replace(/\n/g, ' ')
216 |
217 | // trim whitespace, curly brackets, strip comments
218 | .replace(/^[{ ]+|[ }]+$|\/\*.+?\*\//g, '')
219 |
220 | // is it an object literal? i.e. { key : value }
221 | return /^\s*[\w-"']+ *:/.test(s)
222 |
223 | // if object literal, return trueish keys
224 | // e.g.: { show: isOpen(), done: item.done } -> "show done"
225 | ? '[' + s.replace(/\W*([\w-]+)\W*:([^,]+)/g, function(_, k, v) {
226 |
227 | // safely execute vars to prevent undefined value errors
228 | return v.replace(/\w[^,|& ]*/g, function(v) { return wrap(v, n) }) + '?"' + k + '":"",'
229 |
230 | }) + '].join(" ")'
231 |
232 | // if js expression, evaluate as javascript
233 | : wrap(s, n)
234 |
235 | }
236 |
237 |
238 | // execute js w/o breaking on errors or undefined vars
239 |
240 | function wrap(s, nonull) {
241 | return '(function(v){try{v='
242 |
243 | // prefix vars (name => data.name)
244 | + (s.replace(re_vars, function(s, _, v) { return v ? 'd.' + v : s })
245 |
246 | // break the expression if its empty (resulting in undefined value)
247 | || 'x')
248 |
249 | + '}finally{return '
250 |
251 | // default to empty string for falsy values except zero
252 | + (nonull ? '!v&&v!==0?"":v' : 'v')
253 |
254 | + '}}).call(d)'
255 | }
256 |
257 | })()
258 | ;(function(riot, doc) {
259 |
260 | var tmpl = riot._tmpl,
261 | all_tags = [],
262 | tag_impl = {}
263 |
264 | function each(nodes, fn) {
265 | for (var i = 0; i < (nodes || []).length; i++) {
266 | if (fn(nodes[i], i) === false) i--
267 | }
268 | }
269 |
270 | function extend(obj, from) {
271 | from && Object.keys(from).map(function(key) {
272 | obj[key] = from[key]
273 | })
274 | return obj
275 | }
276 |
277 | function diff(arr1, arr2) {
278 | return arr1.filter(function(el) {
279 | return arr2.indexOf(el) < 0
280 | })
281 | }
282 |
283 | function walk(dom, fn) {
284 | dom = fn(dom) === false ? dom.nextSibling : dom.firstChild
285 |
286 | while (dom) {
287 | walk(dom, fn)
288 | dom = dom.nextSibling
289 | }
290 | }
291 |
292 |
293 | function mkdom(tmpl) {
294 | var el = doc.createElement('div')
295 | el.innerHTML = tmpl
296 | return el
297 | }
298 |
299 |
300 | function update(expressions, instance) {
301 |
302 | // allow recalculation of context data
303 | instance.trigger('update')
304 |
305 | each(expressions, function(expr) {
306 | var tag = expr.tag,
307 | dom = expr.dom
308 |
309 | function remAttr(name) {
310 | dom.removeAttribute(name)
311 | }
312 |
313 | // loops first: TODO remove from expressions arr
314 | if (expr.loop) {
315 | remAttr('each')
316 | return loop(expr, instance)
317 | }
318 |
319 | // custom tag
320 | if (tag) return tag.update ? tag.update() :
321 | expr.tag = createTag({ tmpl: tag[0], fn: tag[1], root: dom, parent: instance })
322 |
323 |
324 | var attr_name = expr.attr,
325 | value = tmpl(expr.expr, instance)
326 |
327 | if (value == null) value = ''
328 |
329 | // no change
330 | if (expr.value === value) return
331 | expr.value = value
332 |
333 |
334 | // text node
335 | if (!attr_name) return dom.nodeValue = value
336 |
337 | // attribute
338 | if (!value && expr.bool || /obj|func/.test(typeof value)) remAttr(attr_name)
339 |
340 | // event handler
341 | if (typeof value == 'function') {
342 | dom[attr_name] = function(e) {
343 |
344 | // cross browser event fix
345 | e = e || window.event
346 | e.which = e.which || e.charCode || e.keyCode
347 | e.target = e.target || e.srcElement
348 | e.currentTarget = dom
349 |
350 | // currently looped item
351 | e.item = instance.__item || instance
352 |
353 | // prevent default behaviour (by default)
354 | if (value.call(instance, e) !== true) {
355 | e.preventDefault && e.preventDefault()
356 | e.returnValue = false
357 | }
358 |
359 | instance.update()
360 | }
361 |
362 | // show / hide / if
363 | } else if (/^(show|hide|if)$/.test(attr_name)) {
364 | remAttr(attr_name)
365 | if (attr_name == 'hide') value = !value
366 | dom.style.display = value ? '' : 'none'
367 |
368 | // normal attribute
369 | } else {
370 | if (expr.bool) {
371 | if (!value) return
372 | value = attr_name
373 | }
374 |
375 | dom.setAttribute(attr_name, value)
376 | }
377 |
378 | })
379 |
380 | instance.trigger('updated')
381 |
382 | }
383 |
384 | function parse(root) {
385 |
386 | var named_elements = {},
387 | expressions = []
388 |
389 | walk(root, function(dom) {
390 |
391 | var type = dom.nodeType,
392 | value = dom.nodeValue
393 |
394 | function addExpr(value, data) {
395 | if (value ? value.indexOf('{') >= 0 : data) {
396 | var expr = { dom: dom, expr: value }
397 | expressions.push(extend(expr, data || {}))
398 | }
399 | }
400 |
401 | // text node
402 | if (type == 3 && dom.parentNode.tagName != 'STYLE') {
403 | addExpr(value)
404 |
405 | // element
406 | } else if (type == 1) {
407 |
408 | // loop?
409 | value = dom.getAttribute('each')
410 |
411 | if (value) {
412 | addExpr(value, { loop: 1 })
413 | return false
414 | }
415 |
416 | // custom tag?
417 | var tag = tag_impl[dom.tagName.toLowerCase()]
418 |
419 | // attributes
420 | each(dom.attributes, function(attr) {
421 | var name = attr.name,
422 | value = attr.value
423 |
424 | // named elements
425 | if (/^(name|id)$/.test(name)) named_elements[value] = dom
426 |
427 | // expressions
428 | if (!tag) {
429 | var bool = name.split('__')[1]
430 | addExpr(value, { attr: bool || name, bool: bool })
431 | if (bool) {
432 | dom.removeAttribute(name)
433 | return false
434 | }
435 | }
436 |
437 | })
438 |
439 | if (tag) addExpr(0, { tag: tag })
440 |
441 | }
442 |
443 | })
444 |
445 | return { expr: expressions, elem: named_elements }
446 |
447 | }
448 |
449 |
450 |
451 | // create new custom tag (component)
452 | function createTag(conf) {
453 |
454 | var opts = conf.opts || {},
455 | dom = mkdom(conf.tmpl),
456 | mountNode = conf.root,
457 | parent = conf.parent,
458 | ast = parse(dom),
459 | tag = { root: mountNode, opts: opts, parent: parent, __item: conf.item },
460 | attributes = {}
461 |
462 | // named elements
463 | extend(tag, ast.elem)
464 |
465 | // attributes
466 | each(mountNode.attributes, function(attr) {
467 | attributes[attr.name] = attr.value
468 | })
469 |
470 | function updateOpts() {
471 | Object.keys(attributes).map(function(name) {
472 | var val = opts[name] = tmpl(attributes[name], parent || tag)
473 | if (typeof val == 'object') mountNode.removeAttribute(name)
474 | })
475 | }
476 |
477 | updateOpts()
478 |
479 | if (!tag.on) {
480 | riot.observable(tag)
481 | delete tag.off // off method not needed
482 | }
483 |
484 | if (conf.fn) conf.fn.call(tag, opts)
485 |
486 |
487 | tag.update = function(data, _system) {
488 |
489 | /*
490 | If loop is defined on the root of the HTML template
491 | the original parent is a temporary
by mkdom()
492 | */
493 | if (parent && dom && !dom.firstChild) {
494 | mountNode = parent.root
495 | dom = null
496 | }
497 |
498 | if (_system || doc.body.contains(mountNode)) {
499 | extend(tag, data)
500 | extend(tag, tag.__item)
501 | updateOpts()
502 | update(ast.expr, tag)
503 |
504 | // update parent
505 | !_system && tag.__item && parent.update()
506 | return true
507 |
508 | } else {
509 | tag.trigger('unmount')
510 | }
511 |
512 | }
513 |
514 | tag.update(0, true)
515 |
516 | // append to root
517 | while (dom.firstChild) {
518 | if (conf.before) mountNode.insertBefore(dom.firstChild, conf.before)
519 | else mountNode.appendChild(dom.firstChild)
520 | }
521 |
522 |
523 | tag.trigger('mount')
524 |
525 | all_tags.push(tag)
526 |
527 | return tag
528 | }
529 |
530 |
531 | function loop(expr, instance) {
532 |
533 | // initialize once
534 | if (expr.done) return
535 | expr.done = true
536 |
537 | var dom = expr.dom,
538 | prev = dom.previousSibling,
539 | root = dom.parentNode,
540 | template = dom.outerHTML,
541 | val = expr.expr,
542 | els = val.split(/\s+in\s+/),
543 | rendered = [],
544 | checksum,
545 | root,
546 | keys
547 |
548 |
549 | if (els[1]) {
550 | val = '{ ' + els[1]
551 | keys = els[0].slice(1).trim().split(/,\s*/)
552 | }
553 |
554 | // clean template code
555 | instance.one('mount', function() {
556 | var p = dom.parentNode
557 | if (p) {
558 | root = p
559 | root.removeChild(dom)
560 | }
561 | })
562 |
563 | function startPos() {
564 | return Array.prototype.indexOf.call(root.childNodes, prev) + 1
565 | }
566 |
567 | instance.on('updated', function() {
568 |
569 | var items = tmpl(val, instance)
570 | is_array = Array.isArray(items)
571 |
572 | if (is_array) items = items.slice(0)
573 |
574 | else {
575 |
576 | if (!items) return // some IE8 issue
577 |
578 | // detect Object changes
579 | var testsum = JSON.stringify(items)
580 | if (testsum == checksum) return
581 | checksum = testsum
582 |
583 | items = Object.keys(items).map(function(key, i) {
584 | var item = {}
585 | item[keys[0]] = key
586 | item[keys[1]] = items[key]
587 | return item
588 | })
589 |
590 | }
591 |
592 | // remove redundant
593 | diff(rendered, items).map(function(item) {
594 | var pos = rendered.indexOf(item)
595 | root.removeChild(root.childNodes[startPos() + pos])
596 | rendered.splice(pos, 1)
597 | })
598 |
599 | // add new
600 | diff(items, rendered).map(function(item, i) {
601 | var pos = items.indexOf(item)
602 |
603 | // string array
604 | if (keys && !checksum) {
605 | var obj = {}
606 | obj[keys[0]] = item
607 | obj[keys[1]] = i
608 | item = obj
609 | }
610 |
611 | var tag = createTag({
612 | before: root.childNodes[startPos() + pos],
613 | parent: instance,
614 | tmpl: template,
615 | item: item,
616 | root: root
617 | })
618 |
619 | instance.on('update', function() {
620 | tag.update(0, true)
621 | })
622 |
623 | })
624 |
625 | // assign rendered
626 | rendered = items
627 |
628 | })
629 |
630 | }
631 |
632 | riot.tag = function(name, tmpl, fn) {
633 | fn = fn || noop,
634 | tag_impl[name] = [tmpl, fn]
635 | }
636 |
637 | riot.mountTo = function(node, tagName, opts) {
638 | var tag = tag_impl[tagName]
639 | return tag && createTag({ tmpl: tag[0], fn: tag[1], root: node, opts: opts })
640 | }
641 |
642 | riot.mount = function(selector, opts) {
643 | if (selector == '*') selector = Object.keys(tag_impl).join(', ')
644 |
645 | var instances = []
646 |
647 | each(doc.querySelectorAll(selector), function(node) {
648 | if (node.riot) return
649 |
650 | var tagName = node.tagName.toLowerCase(),
651 | instance = riot.mountTo(node, tagName, opts)
652 |
653 | if (instance) {
654 | instances.push(instance)
655 | node.riot = 1
656 | }
657 | })
658 |
659 | return instances
660 | }
661 |
662 | // update everything
663 | riot.update = function() {
664 | return all_tags = all_tags.filter(function(tag) {
665 | return !!tag.update()
666 | })
667 | }
668 |
669 | })(riot, document)
670 |
671 |
--------------------------------------------------------------------------------
/demo/riotcontrol.js:
--------------------------------------------------------------------------------
1 | var _RiotControlApi = ['on','one','off','trigger']
2 | var RiotControl = {
3 | _stores: [],
4 | addStore: function(store) {
5 | this._stores.push(store)
6 | }
7 | }
8 | _RiotControlApi.forEach(function(api){
9 | RiotControl[api] = function() {
10 | var args = [].slice.call(arguments)
11 | this._stores.forEach(function(el){
12 | el[api].apply(null, args)
13 | })
14 | }
15 | })
16 |
--------------------------------------------------------------------------------
/demo/todo.css:
--------------------------------------------------------------------------------
1 |
2 | body {
3 | font-family: 'myriad pro', sans-serif;
4 | font-size: 20px;
5 | border: 0;
6 | }
7 |
8 | a {
9 | text-decoration: none;
10 | color: grey;
11 | display: block;
12 | max-width: 400px;
13 | margin: 5% auto;
14 | }
15 |
16 | todoapp {
17 | display: block;
18 | max-width: 400px;
19 | margin: 5% auto;
20 | }
21 |
22 | form input {
23 | font-size: 100%;
24 | padding: .6em;
25 | border: 1px solid #ccc;
26 | border-radius: 3px;
27 | }
28 |
29 | button {
30 | background-color: #1FADC5;
31 | border: 1px solid rgba(0,0,0,.2);
32 | font-size: 100%;
33 | color: #fff;
34 | padding: .6em 1.2em;
35 | border-radius: 3em;
36 | cursor: pointer;
37 | margin: 0 .3em;
38 | outline: none;
39 | }
40 |
41 | button[disabled] {
42 | background-color: #ddd;
43 | color: #aaa;
44 | }
45 |
46 | ul {
47 | padding: 0;
48 | }
49 |
50 | li {
51 | list-style-type: none;
52 | padding: .2em 0;
53 | }
54 |
55 | .completed {
56 | text-decoration: line-through;
57 | color: #ccc;
58 | }
59 |
60 | label {
61 | cursor: pointer;
62 | }
--------------------------------------------------------------------------------
/demo/todo.js:
--------------------------------------------------------------------------------
1 |
2 | riot.tag('todo', '{ opts.title } Remove ', function(opts) {
3 | var self = this
4 | self.disabled = true
5 | self.items = []
6 |
7 | self.on('mount', function() {
8 | RiotControl.trigger('todo_init')
9 | })
10 |
11 | RiotControl.on('todos_changed', function(items) {
12 | self.items = items
13 | self.update()
14 | })
15 |
16 | this.edit = function(e) {
17 | self.text = e.target.value
18 | }.bind(this)
19 |
20 | this.add = function(e) {
21 | if (self.text) {
22 | RiotControl.trigger('todo_add', { title: self.text })
23 | self.text = self.input.value = ''
24 | }
25 | }.bind(this)
26 |
27 | this.toggle = function(e) {
28 | var item = e.item
29 | item.done = !item.done
30 | return true
31 | }.bind(this)
32 |
33 | this.remove = function(e) {
34 | RiotControl.trigger('todo_remove')
35 | }.bind(this)
36 |
37 | })
--------------------------------------------------------------------------------
/demo/todo.tag:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | { opts.title }
5 |
6 |
13 |
14 |
18 | Remove
19 |
20 | var self = this
21 | self.disabled = true
22 | self.items = []
23 |
24 | self.on('mount', function() {
25 | // Trigger init event when component is mounted to page.
26 | // Any store could respond to this.
27 | RiotControl.trigger('todo_init')
28 | })
29 |
30 | // Register a listener for store change events.
31 | RiotControl.on('todos_changed', function(items) {
32 | self.items = items
33 | self.update()
34 | })
35 |
36 | edit(e) {
37 | self.text = e.target.value
38 | }
39 |
40 | add(e) {
41 | if (self.text) {
42 | // Trigger event to all stores registered in central dispatch.
43 | // This allows loosely coupled stores/components to react to same events.
44 | RiotControl.trigger('todo_add', { title: self.text })
45 | self.text = self.input.value = ''
46 | }
47 | }
48 |
49 | toggle(e) {
50 | var item = e.item
51 | item.done = !item.done
52 | return true
53 | }
54 |
55 | remove(e) {
56 | RiotControl.trigger('todo_remove')
57 | }
58 |
59 |
--------------------------------------------------------------------------------
/demo/todoapp.js:
--------------------------------------------------------------------------------
1 |
2 | riot.tag('todoapp', 'RiotControl A Simplistic Central Event Controller / Dispatcher For RiotJS, Inspired By Flux ', function(opts) {
3 | })
--------------------------------------------------------------------------------
/demo/todoapp.tag:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | RiotControl
5 | A Simplistic Central Event Controller / Dispatcher For RiotJS, Inspired By Flux
6 |
7 |
8 |
--------------------------------------------------------------------------------
/demo/todostore.js:
--------------------------------------------------------------------------------
1 | // TodoStore definition.
2 | // Flux stores house application logic and state that relate to a specific domain.
3 | // In this case, a list of todo items.
4 | function TodoStore() {
5 | riot.observable(this) // Riot provides our event emitter.
6 |
7 | var self = this
8 |
9 | self.todos = [
10 | { title: 'Task 1', done: false },
11 | { title: 'Task 2', done: false }
12 | ]
13 |
14 | // Our store's event handlers / API.
15 | // This is where we would use AJAX calls to interface with the server.
16 | // Any number of views can emit actions/events without knowing the specifics of the back-end.
17 | // This store can easily be swapped for another, while the view components remain untouched.
18 |
19 | self.on('todo_add', function(newTodo) {
20 | self.todos.push(newTodo)
21 | self.trigger('todos_changed', self.todos)
22 | })
23 |
24 | self.on('todo_remove', function() {
25 | self.todos.pop()
26 | self.trigger('todos_changed', self.todos)
27 | })
28 |
29 | self.on('todo_init', function() {
30 | self.trigger('todos_changed', self.todos)
31 | })
32 |
33 | // The store emits change events to any listening views, so that they may react and redraw themselves.
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "riotcontrol",
3 | "version": "0.0.3",
4 | "description": "Event Controller / Dispatcher For RiotJS, Inspired By Flux",
5 | "main": "riotcontrol.js",
6 | "files": [
7 | "riotcontrol.js",
8 | "README.md",
9 | "LICENSE.txt"
10 | ],
11 | "scripts": {
12 | "test": "echo \"Error: no test specified\" && exit 1"
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": "https://github.com/jimsparkman/RiotControl"
17 | },
18 | "keywords": [
19 | "riotjs",
20 | "flux"
21 | ],
22 | "author": "Jim Sparkman",
23 | "license": "MIT",
24 | "bugs": {
25 | "url": "https://github.com/jimsparkman/RiotControl/issues"
26 | },
27 | "homepage": "https://github.com/jimsparkman/RiotControl"
28 | }
29 |
--------------------------------------------------------------------------------
/riotcontrol.js:
--------------------------------------------------------------------------------
1 | /* RiotControl v0.0.3, @license MIT */
2 | var RiotControl = {
3 | _stores: [],
4 | addStore: function(store) {
5 | this._stores.push(store);
6 | },
7 | reset: function() {
8 | this._stores = [];
9 | }
10 | };
11 |
12 | ['on','one','off','trigger'].forEach(function(api){
13 | RiotControl[api] = function() {
14 | var args = [].slice.call(arguments);
15 | this._stores.forEach(function(el){
16 | el[api].apply(el, args);
17 | });
18 | };
19 | });
20 |
21 | if (typeof(module) !== 'undefined') module.exports = RiotControl;
22 |
--------------------------------------------------------------------------------
/routing_demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | RiotControl Demo
7 |
8 |
9 |
10 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/routing_demo/item-app.js:
--------------------------------------------------------------------------------
1 |
2 | riot.tag('item-app', ' Notice the URL routing, back button works as expected.
Cancel
', function(opts) {
3 | var self = this
4 |
5 | self.items = []
6 | self.txt = null
7 | self.detail = null
8 | self.edit = false
9 |
10 | this.search = function(e) {
11 | self.txt = e.target.value
12 | RiotControl.trigger('item_list_search', self.txt)
13 | }.bind(this)
14 |
15 | this.clear = function(e) {
16 | self.txt = ''
17 | this.input.value = ''
18 | RiotControl.trigger('item_list_search','')
19 | }.bind(this)
20 |
21 | this.add = function(e) {
22 | riot.route('add')
23 | }.bind(this)
24 |
25 | this.submit = function(e) {
26 | RiotControl.trigger('item_detail_add', this.title.value)
27 | this.title.value = ''
28 | this.edit = false
29 | riot.route('view')
30 | }.bind(this)
31 |
32 | this.cancel = function(e) {
33 | this.title.value = ''
34 | this.edit = false
35 | riot.route('view')
36 | }.bind(this)
37 |
38 | self.on('mount', function() {
39 | RiotControl.trigger('item_list_init')
40 | })
41 |
42 | RiotControl.on('item_list_changed', function(items) {
43 | self.items = items
44 | self.update()
45 | })
46 |
47 | RiotControl.on('item_detail_changed', function(item) {
48 | self.edit = false
49 | self.detail = item
50 | riot.update()
51 | })
52 |
53 | RiotControl.on('item_detail_create', function() {
54 | self.edit = true
55 | self.update()
56 | })
57 |
58 | })
--------------------------------------------------------------------------------
/routing_demo/item-app.tag:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Notice the URL routing, back button works as expected.
6 |
7 |
8 |
Search:
9 |
10 |
11 |
14 |
19 |
20 |
21 |
22 | Choose a product.
23 |
24 |
25 |
26 | Add
27 |
28 |
29 |
30 |
34 | Cancel
35 |
36 |
37 | // This is essentially the equivalent of the Flux view-controller.
38 | // Could be broken down further into more sub-componenets, if you wished to re-use views.
39 |
40 | var self = this
41 |
42 | self.items = []
43 | self.txt = null
44 | self.detail = null
45 | self.edit = false
46 |
47 | search(e) {
48 | self.txt = e.target.value
49 | RiotControl.trigger('item_list_search', self.txt)
50 | }
51 |
52 | clear(e) {
53 | self.txt = ''
54 | this.input.value = ''
55 | RiotControl.trigger('item_list_search','')
56 | }
57 |
58 | add(e) {
59 | riot.route('add')
60 | }
61 |
62 | submit(e) {
63 | RiotControl.trigger('item_detail_add', this.title.value)
64 | this.title.value = ''
65 | this.edit = false
66 | riot.route('view')
67 | }
68 |
69 | cancel(e) {
70 | this.title.value = ''
71 | this.edit = false
72 | riot.route('view')
73 | }
74 |
75 | self.on('mount', function() {
76 | RiotControl.trigger('item_list_init')
77 | })
78 |
79 | RiotControl.on('item_list_changed', function(items) {
80 | self.items = items
81 | self.update()
82 | })
83 |
84 | RiotControl.on('item_detail_changed', function(item) {
85 | self.edit = false
86 | self.detail = item
87 | riot.update()
88 | })
89 |
90 | RiotControl.on('item_detail_create', function() {
91 | self.edit = true
92 | self.update()
93 | })
94 |
95 |
--------------------------------------------------------------------------------
/routing_demo/item-detail.js:
--------------------------------------------------------------------------------
1 |
2 | riot.tag('item-detail', ' Item Details ID: { opts.item.id }
Name: { opts.item.title }
', function(opts) {
3 | })
4 |
--------------------------------------------------------------------------------
/routing_demo/item-detail.tag:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Item Details
6 |
ID: { opts.item.id }
7 |
Name: { opts.item.title }
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/routing_demo/itemstore.js:
--------------------------------------------------------------------------------
1 | // Manages our item data locally, for now.
2 | function ItemStore() {
3 | riot.observable(this)
4 |
5 | var self = this
6 |
7 | // Could pull this from a server API.
8 | self.items = [
9 | { id: 1, title: 'Foobar' },
10 | { id: 2, title: 'Foobaz' },
11 | { id: 3, title: 'Barbar' }
12 | ]
13 |
14 | // Init our list view.
15 | self.on('item_list_init', function() {
16 | self.trigger('item_list_changed', self.items)
17 | })
18 |
19 | // Search our item collection.
20 | self.on('item_list_search', function(txt) {
21 | var list = self.items
22 | if (txt.length > 0)
23 | list = self.items.filter(function(el) {
24 | if (el.title.toLowerCase().search(new RegExp(txt.toLowerCase())) == -1)
25 | return false
26 | else
27 | return true
28 | })
29 |
30 | self.trigger('item_list_changed', list)
31 | })
32 |
33 | // Add to our item collection.
34 | // Could push this to a server API.
35 | self.on('item_detail_add', function(title) {
36 | self.items.push({ id: self.items.length+1, title: title })
37 | self.trigger('item_list_changed', self.items)
38 | })
39 |
40 | // Pull item for URL route. (id)
41 | self.on('route_item', function(id) {
42 | var item = null
43 | self.items.forEach(function(el) {
44 | if (el.id == id)
45 | item = el
46 | })
47 | self.trigger('item_detail_changed', item)
48 | })
49 |
50 | // Emit event for add item route.
51 | self.on('route_item_add', function() {
52 | self.trigger('item_detail_create')
53 | })
54 |
55 | }
--------------------------------------------------------------------------------
/routing_demo/js/html5-shiv.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @preserve HTML5 Shiv 3.7.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
3 | */
4 | ;(function(window, document) {
5 | /*jshint evil:true */
6 | /** version */
7 | var version = '3.7.2';
8 |
9 | /** Preset options */
10 | var options = window.html5 || {};
11 |
12 | /** Used to skip problem elements */
13 | var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i;
14 |
15 | /** Not all elements can be cloned in IE **/
16 | var saveClones = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i;
17 |
18 | /** Detect whether the browser supports default html5 styles */
19 | var supportsHtml5Styles;
20 |
21 | /** Name of the expando, to work with multiple documents or to re-shiv one document */
22 | var expando = '_html5shiv';
23 |
24 | /** The id for the the documents expando */
25 | var expanID = 0;
26 |
27 | /** Cached data for each document */
28 | var expandoData = {};
29 |
30 | /** Detect whether the browser supports unknown elements */
31 | var supportsUnknownElements;
32 |
33 | (function() {
34 | try {
35 | var a = document.createElement('a');
36 | a.innerHTML = ' ';
37 | //if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles
38 | supportsHtml5Styles = ('hidden' in a);
39 |
40 | supportsUnknownElements = a.childNodes.length == 1 || (function() {
41 | // assign a false positive if unable to shiv
42 | (document.createElement)('a');
43 | var frag = document.createDocumentFragment();
44 | return (
45 | typeof frag.cloneNode == 'undefined' ||
46 | typeof frag.createDocumentFragment == 'undefined' ||
47 | typeof frag.createElement == 'undefined'
48 | );
49 | }());
50 | } catch(e) {
51 | // assign a false positive if detection fails => unable to shiv
52 | supportsHtml5Styles = true;
53 | supportsUnknownElements = true;
54 | }
55 |
56 | }());
57 |
58 | /*--------------------------------------------------------------------------*/
59 |
60 | /**
61 | * Creates a style sheet with the given CSS text and adds it to the document.
62 | * @private
63 | * @param {Document} ownerDocument The document.
64 | * @param {String} cssText The CSS text.
65 | * @returns {StyleSheet} The style element.
66 | */
67 | function addStyleSheet(ownerDocument, cssText) {
68 | var p = ownerDocument.createElement('p'),
69 | parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement;
70 |
71 | p.innerHTML = 'x';
72 | return parent.insertBefore(p.lastChild, parent.firstChild);
73 | }
74 |
75 | /**
76 | * Returns the value of `html5.elements` as an array.
77 | * @private
78 | * @returns {Array} An array of shived element node names.
79 | */
80 | function getElements() {
81 | var elements = html5.elements;
82 | return typeof elements == 'string' ? elements.split(' ') : elements;
83 | }
84 |
85 | /**
86 | * Extends the built-in list of html5 elements
87 | * @memberOf html5
88 | * @param {String|Array} newElements whitespace separated list or array of new element names to shiv
89 | * @param {Document} ownerDocument The context document.
90 | */
91 | function addElements(newElements, ownerDocument) {
92 | var elements = html5.elements;
93 | if(typeof elements != 'string'){
94 | elements = elements.join(' ');
95 | }
96 | if(typeof newElements != 'string'){
97 | newElements = newElements.join(' ');
98 | }
99 | html5.elements = elements +' '+ newElements;
100 | shivDocument(ownerDocument);
101 | }
102 |
103 | /**
104 | * Returns the data associated to the given document
105 | * @private
106 | * @param {Document} ownerDocument The document.
107 | * @returns {Object} An object of data.
108 | */
109 | function getExpandoData(ownerDocument) {
110 | var data = expandoData[ownerDocument[expando]];
111 | if (!data) {
112 | data = {};
113 | expanID++;
114 | ownerDocument[expando] = expanID;
115 | expandoData[expanID] = data;
116 | }
117 | return data;
118 | }
119 |
120 | /**
121 | * returns a shived element for the given nodeName and document
122 | * @memberOf html5
123 | * @param {String} nodeName name of the element
124 | * @param {Document} ownerDocument The context document.
125 | * @returns {Object} The shived element.
126 | */
127 | function createElement(nodeName, ownerDocument, data){
128 | if (!ownerDocument) {
129 | ownerDocument = document;
130 | }
131 | if(supportsUnknownElements){
132 | return ownerDocument.createElement(nodeName);
133 | }
134 | if (!data) {
135 | data = getExpandoData(ownerDocument);
136 | }
137 | var node;
138 |
139 | if (data.cache[nodeName]) {
140 | node = data.cache[nodeName].cloneNode();
141 | } else if (saveClones.test(nodeName)) {
142 | node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode();
143 | } else {
144 | node = data.createElem(nodeName);
145 | }
146 |
147 | // Avoid adding some elements to fragments in IE < 9 because
148 | // * Attributes like `name` or `type` cannot be set/changed once an element
149 | // is inserted into a document/fragment
150 | // * Link elements with `src` attributes that are inaccessible, as with
151 | // a 403 response, will cause the tab/window to crash
152 | // * Script elements appended to fragments will execute when their `src`
153 | // or `text` property is set
154 | return node.canHaveChildren && !reSkip.test(nodeName) && !node.tagUrn ? data.frag.appendChild(node) : node;
155 | }
156 |
157 | /**
158 | * returns a shived DocumentFragment for the given document
159 | * @memberOf html5
160 | * @param {Document} ownerDocument The context document.
161 | * @returns {Object} The shived DocumentFragment.
162 | */
163 | function createDocumentFragment(ownerDocument, data){
164 | if (!ownerDocument) {
165 | ownerDocument = document;
166 | }
167 | if(supportsUnknownElements){
168 | return ownerDocument.createDocumentFragment();
169 | }
170 | data = data || getExpandoData(ownerDocument);
171 | var clone = data.frag.cloneNode(),
172 | i = 0,
173 | elems = getElements(),
174 | l = elems.length;
175 | for(;i 0
12 | })
13 | }
14 | return el
15 | }
16 |
17 | el.off = function(events, fn) {
18 | if (events == '*') callbacks = {}
19 | else if (fn) {
20 | var arr = callbacks[events]
21 | for (var i = 0, cb; (cb = arr && arr[i]); ++i) {
22 | if (cb == fn) { arr.splice(i, 1); i-- }
23 | }
24 | } else {
25 | events.replace(/\S+/g, function(name) {
26 | callbacks[name] = []
27 | })
28 | }
29 | return el
30 | }
31 |
32 | // only single event supported
33 | el.one = function(name, fn) {
34 | if (fn) fn.one = 1
35 | return el.on(name, fn)
36 | }
37 |
38 | el.trigger = function(name) {
39 | var args = [].slice.call(arguments, 1),
40 | fns = callbacks[name] || []
41 |
42 | for (var i = 0, fn; (fn = fns[i]); ++i) {
43 | if (!fn.busy) {
44 | fn.busy = 1
45 | fn.apply(el, fn.typed ? [name].concat(args) : args)
46 | if (fn.one) { fns.splice(i, 1); i-- }
47 | fn.busy = 0
48 | }
49 | }
50 |
51 | return el
52 | }
53 |
54 | return el
55 |
56 | }
57 | ;(function(riot, evt) {
58 |
59 | // browsers only
60 | if (!this.top) return
61 |
62 | var loc = location,
63 | fns = riot.observable({}),
64 | current = hash(),
65 | win = window
66 |
67 | function hash() {
68 | return loc.hash.slice(1)
69 | }
70 |
71 | function emit(path) {
72 | if (path.type) path = hash()
73 |
74 | if (path != current) {
75 | fns.trigger.apply(null, ['H'].concat(path.split('/')))
76 | current = path
77 | }
78 | }
79 |
80 | var r = riot.route = function(arg) {
81 | // string
82 | if (arg[0]) {
83 | loc.hash = arg
84 | emit(arg)
85 |
86 | // function
87 | } else {
88 | fns.on('H', arg)
89 | }
90 | }
91 |
92 | r.exec = function(fn) {
93 | fn.apply(null, hash().split('/'))
94 | }
95 |
96 | win.addEventListener ? win.addEventListener(evt, emit, false) : win.attachEvent('on' + evt, emit)
97 |
98 | })(riot, 'hashchange')
99 | /*
100 |
101 | //// How it works?
102 |
103 |
104 | Three ways:
105 |
106 | 1. Expressions: tmpl('{ value }', data).
107 | Returns the result of evaluated expression as a raw object.
108 |
109 | 2. Templates: tmpl('Hi { name } { surname }', data).
110 | Returns a string with evaluated expressions.
111 |
112 | 3. Filters: tmpl('{ show: !done, highlight: active }', data).
113 | Returns a space separated list of trueish keys (mainly
114 | used for setting html classes), e.g. "show highlight".
115 |
116 |
117 | // Template examples
118 |
119 | tmpl('{ title || "Untitled" }', data)
120 | tmpl('Results are { results ? "ready" : "loading" }', data)
121 | tmpl('Today is { new Date() }', data)
122 | tmpl('{ message.length > 140 && "Message is too long" }', data)
123 | tmpl('This item got { Math.round(rating) } stars', data)
124 | tmpl('{ title } { body }', data)
125 |
126 |
127 | // Falsy expressions in templates
128 |
129 | In templates (as opposed to single expressions) all falsy values
130 | except zero (undefined/null/false) will default to empty string:
131 |
132 | tmpl('{ undefined } - { false } - { null } - { 0 }', {})
133 | // will return: " - - - 0"
134 |
135 | */
136 |
137 | riot._tmpl = (function() {
138 |
139 | var cache = {},
140 |
141 | // find variable names
142 | re_vars = /("|').+?[^\\]\1|\.\w*|\w*:|\b(?:this|true|false|null|new|typeof|Number|String|Object|Array|Math|Date)\b|([a-z_]\w*)/gi
143 | // [ 1 ][ 2 ][ 3 ][ 4 ][ 5 ]
144 | // 1. skip quoted strings: "a b", 'a b', 'a \'b\''
145 | // 2. skip object properties: .name
146 | // 3. skip object literals: name:
147 | // 4. skip reserved words
148 | // 5. match var name
149 |
150 | // build a template (or get it from cache), render with data
151 |
152 | return function(str, data) {
153 | return str && (cache[str] = cache[str] || tmpl(str))(data)
154 | }
155 |
156 |
157 | // create a template instance
158 |
159 | function tmpl(s, p) {
160 | p = (s || '{}')
161 |
162 | // temporarily convert \{ and \} to a non-character
163 | .replace(/\\{/g, '\uFFF0')
164 | .replace(/\\}/g, '\uFFF1')
165 |
166 | // split string to expression and non-expresion parts
167 | .split(/({[\s\S]*?})/)
168 |
169 | return new Function('d', 'return ' + (
170 |
171 | // is it a single expression or a template? i.e. {x} or {x}
172 | !p[0] && !p[2]
173 |
174 | // if expression, evaluate it
175 | ? expr(p[1])
176 |
177 | // if template, evaluate all expressions in it
178 | : '[' + p.map(function(s, i) {
179 |
180 | // is it an expression or a string (every second part is an expression)
181 | return i % 2
182 |
183 | // evaluate the expressions
184 | ? expr(s, 1)
185 |
186 | // process string parts of the template:
187 | : '"' + s
188 |
189 | // preserve new lines
190 | .replace(/\n/g, '\\n')
191 |
192 | // escape quotes
193 | .replace(/"/g, '\\"')
194 |
195 | + '"'
196 |
197 | }).join(',') + '].join("")'
198 | )
199 |
200 | // bring escaped { and } back
201 | .replace(/\uFFF0/g, '{')
202 | .replace(/\uFFF1/g, '}')
203 |
204 | )
205 |
206 | }
207 |
208 |
209 | // parse { ... } expression
210 |
211 | function expr(s, n) {
212 | s = s
213 |
214 | // convert new lines to spaces
215 | .replace(/\n/g, ' ')
216 |
217 | // trim whitespace, curly brackets, strip comments
218 | .replace(/^[{ ]+|[ }]+$|\/\*.+?\*\//g, '')
219 |
220 | // is it an object literal? i.e. { key : value }
221 | return /^\s*[\w-"']+ *:/.test(s)
222 |
223 | // if object literal, return trueish keys
224 | // e.g.: { show: isOpen(), done: item.done } -> "show done"
225 | ? '[' + s.replace(/\W*([\w-]+)\W*:([^,]+)/g, function(_, k, v) {
226 |
227 | // safely execute vars to prevent undefined value errors
228 | return v.replace(/\w[^,|& ]*/g, function(v) { return wrap(v, n) }) + '?"' + k + '":"",'
229 |
230 | }) + '].join(" ")'
231 |
232 | // if js expression, evaluate as javascript
233 | : wrap(s, n)
234 |
235 | }
236 |
237 |
238 | // execute js w/o breaking on errors or undefined vars
239 |
240 | function wrap(s, nonull) {
241 | return '(function(v){try{v='
242 |
243 | // prefix vars (name => data.name)
244 | + (s.replace(re_vars, function(s, _, v) { return v ? 'd.' + v : s })
245 |
246 | // break the expression if its empty (resulting in undefined value)
247 | || 'x')
248 |
249 | + '}finally{return '
250 |
251 | // default to empty string for falsy values except zero
252 | + (nonull ? '!v&&v!==0?"":v' : 'v')
253 |
254 | + '}}).call(d)'
255 | }
256 |
257 | })()
258 | ;(function(riot, doc) {
259 |
260 | var tmpl = riot._tmpl,
261 | all_tags = [],
262 | tag_impl = {}
263 |
264 | function each(nodes, fn) {
265 | for (var i = 0; i < (nodes || []).length; i++) {
266 | if (fn(nodes[i], i) === false) i--
267 | }
268 | }
269 |
270 | function extend(obj, from) {
271 | from && Object.keys(from).map(function(key) {
272 | obj[key] = from[key]
273 | })
274 | return obj
275 | }
276 |
277 | function diff(arr1, arr2) {
278 | return arr1.filter(function(el) {
279 | return arr2.indexOf(el) < 0
280 | })
281 | }
282 |
283 | function walk(dom, fn) {
284 | dom = fn(dom) === false ? dom.nextSibling : dom.firstChild
285 |
286 | while (dom) {
287 | walk(dom, fn)
288 | dom = dom.nextSibling
289 | }
290 | }
291 |
292 |
293 | function mkdom(tmpl) {
294 | var el = doc.createElement('div')
295 | el.innerHTML = tmpl
296 | return el
297 | }
298 |
299 |
300 | function update(expressions, instance) {
301 |
302 | // allow recalculation of context data
303 | instance.trigger('update')
304 |
305 | each(expressions, function(expr) {
306 | var tag = expr.tag,
307 | dom = expr.dom
308 |
309 | function remAttr(name) {
310 | dom.removeAttribute(name)
311 | }
312 |
313 | // loops first: TODO remove from expressions arr
314 | if (expr.loop) {
315 | remAttr('each')
316 | return loop(expr, instance)
317 | }
318 |
319 | // custom tag
320 | if (tag) return tag.update ? tag.update() :
321 | expr.tag = createTag({ tmpl: tag[0], fn: tag[1], root: dom, parent: instance })
322 |
323 |
324 | var attr_name = expr.attr,
325 | value = tmpl(expr.expr, instance)
326 |
327 | if (value == null) value = ''
328 |
329 | // no change
330 | if (expr.value === value) return
331 | expr.value = value
332 |
333 |
334 | // text node
335 | if (!attr_name) return dom.nodeValue = value
336 |
337 | // attribute
338 | if (!value && expr.bool || /obj|func/.test(typeof value)) remAttr(attr_name)
339 |
340 | // event handler
341 | if (typeof value == 'function') {
342 | dom[attr_name] = function(e) {
343 |
344 | // cross browser event fix
345 | e = e || window.event
346 | e.which = e.which || e.charCode || e.keyCode
347 | e.target = e.target || e.srcElement
348 | e.currentTarget = dom
349 |
350 | // currently looped item
351 | e.item = instance.__item || instance
352 |
353 | // prevent default behaviour (by default)
354 | if (value.call(instance, e) !== true) {
355 | e.preventDefault && e.preventDefault()
356 | e.returnValue = false
357 | }
358 |
359 | instance.update()
360 | }
361 |
362 | // show / hide / if
363 | } else if (/^(show|hide|if)$/.test(attr_name)) {
364 | remAttr(attr_name)
365 | if (attr_name == 'hide') value = !value
366 | dom.style.display = value ? '' : 'none'
367 |
368 | // normal attribute
369 | } else {
370 | if (expr.bool) {
371 | if (!value) return
372 | value = attr_name
373 | }
374 |
375 | dom.setAttribute(attr_name, value)
376 | }
377 |
378 | })
379 |
380 | instance.trigger('updated')
381 |
382 | }
383 |
384 | function parse(root) {
385 |
386 | var named_elements = {},
387 | expressions = []
388 |
389 | walk(root, function(dom) {
390 |
391 | var type = dom.nodeType,
392 | value = dom.nodeValue
393 |
394 | function addExpr(value, data) {
395 | if (value ? value.indexOf('{') >= 0 : data) {
396 | var expr = { dom: dom, expr: value }
397 | expressions.push(extend(expr, data || {}))
398 | }
399 | }
400 |
401 | // text node
402 | if (type == 3 && dom.parentNode.tagName != 'STYLE') {
403 | addExpr(value)
404 |
405 | // element
406 | } else if (type == 1) {
407 |
408 | // loop?
409 | value = dom.getAttribute('each')
410 |
411 | if (value) {
412 | addExpr(value, { loop: 1 })
413 | return false
414 | }
415 |
416 | // custom tag?
417 | var tag = tag_impl[dom.tagName.toLowerCase()]
418 |
419 | // attributes
420 | each(dom.attributes, function(attr) {
421 | var name = attr.name,
422 | value = attr.value
423 |
424 | // named elements
425 | if (/^(name|id)$/.test(name)) named_elements[value] = dom
426 |
427 | // expressions
428 | if (!tag) {
429 | var bool = name.split('__')[1]
430 | addExpr(value, { attr: bool || name, bool: bool })
431 | if (bool) {
432 | dom.removeAttribute(name)
433 | return false
434 | }
435 | }
436 |
437 | })
438 |
439 | if (tag) addExpr(0, { tag: tag })
440 |
441 | }
442 |
443 | })
444 |
445 | return { expr: expressions, elem: named_elements }
446 |
447 | }
448 |
449 |
450 |
451 | // create new custom tag (component)
452 | function createTag(conf) {
453 |
454 | var opts = conf.opts || {},
455 | dom = mkdom(conf.tmpl),
456 | mountNode = conf.root,
457 | parent = conf.parent,
458 | ast = parse(dom),
459 | tag = { root: mountNode, opts: opts, parent: parent, __item: conf.item },
460 | attributes = {}
461 |
462 | // named elements
463 | extend(tag, ast.elem)
464 |
465 | // attributes
466 | each(mountNode.attributes, function(attr) {
467 | attributes[attr.name] = attr.value
468 | })
469 |
470 | function updateOpts() {
471 | Object.keys(attributes).map(function(name) {
472 | var val = opts[name] = tmpl(attributes[name], parent || tag)
473 | if (typeof val == 'object') mountNode.removeAttribute(name)
474 | })
475 | }
476 |
477 | updateOpts()
478 |
479 | if (!tag.on) {
480 | riot.observable(tag)
481 | delete tag.off // off method not needed
482 | }
483 |
484 | if (conf.fn) conf.fn.call(tag, opts)
485 |
486 |
487 | tag.update = function(data, _system) {
488 |
489 | /*
490 | If loop is defined on the root of the HTML template
491 | the original parent is a temporary
by mkdom()
492 | */
493 | if (parent && dom && !dom.firstChild) {
494 | mountNode = parent.root
495 | dom = null
496 | }
497 |
498 | if (_system || doc.body.contains(mountNode)) {
499 | extend(tag, data)
500 | extend(tag, tag.__item)
501 | updateOpts()
502 | update(ast.expr, tag)
503 |
504 | // update parent
505 | !_system && tag.__item && parent.update()
506 | return true
507 |
508 | } else {
509 | tag.trigger('unmount')
510 | }
511 |
512 | }
513 |
514 | tag.update(0, true)
515 |
516 | // append to root
517 | while (dom.firstChild) {
518 | if (conf.before) mountNode.insertBefore(dom.firstChild, conf.before)
519 | else mountNode.appendChild(dom.firstChild)
520 | }
521 |
522 |
523 | tag.trigger('mount')
524 |
525 | all_tags.push(tag)
526 |
527 | return tag
528 | }
529 |
530 |
531 | function loop(expr, instance) {
532 |
533 | // initialize once
534 | if (expr.done) return
535 | expr.done = true
536 |
537 | var dom = expr.dom,
538 | prev = dom.previousSibling,
539 | root = dom.parentNode,
540 | template = dom.outerHTML,
541 | val = expr.expr,
542 | els = val.split(/\s+in\s+/),
543 | rendered = [],
544 | checksum,
545 | root,
546 | keys
547 |
548 |
549 | if (els[1]) {
550 | val = '{ ' + els[1]
551 | keys = els[0].slice(1).trim().split(/,\s*/)
552 | }
553 |
554 | // clean template code
555 | instance.one('mount', function() {
556 | var p = dom.parentNode
557 | if (p) {
558 | root = p
559 | root.removeChild(dom)
560 | }
561 | })
562 |
563 | function startPos() {
564 | return Array.prototype.indexOf.call(root.childNodes, prev) + 1
565 | }
566 |
567 | instance.on('updated', function() {
568 |
569 | var items = tmpl(val, instance)
570 | is_array = Array.isArray(items)
571 |
572 | if (is_array) items = items.slice(0)
573 |
574 | else {
575 |
576 | if (!items) return // some IE8 issue
577 |
578 | // detect Object changes
579 | var testsum = JSON.stringify(items)
580 | if (testsum == checksum) return
581 | checksum = testsum
582 |
583 | items = Object.keys(items).map(function(key, i) {
584 | var item = {}
585 | item[keys[0]] = key
586 | item[keys[1]] = items[key]
587 | return item
588 | })
589 |
590 | }
591 |
592 | // remove redundant
593 | diff(rendered, items).map(function(item) {
594 | var pos = rendered.indexOf(item)
595 | root.removeChild(root.childNodes[startPos() + pos])
596 | rendered.splice(pos, 1)
597 | })
598 |
599 | // add new
600 | diff(items, rendered).map(function(item, i) {
601 | var pos = items.indexOf(item)
602 |
603 | // string array
604 | if (keys && !checksum) {
605 | var obj = {}
606 | obj[keys[0]] = item
607 | obj[keys[1]] = i
608 | item = obj
609 | }
610 |
611 | var tag = createTag({
612 | before: root.childNodes[startPos() + pos],
613 | parent: instance,
614 | tmpl: template,
615 | item: item,
616 | root: root
617 | })
618 |
619 | instance.on('update', function() {
620 | tag.update(0, true)
621 | })
622 |
623 | })
624 |
625 | // assign rendered
626 | rendered = items
627 |
628 | })
629 |
630 | }
631 |
632 | riot.tag = function(name, tmpl, fn) {
633 | fn = fn || noop,
634 | tag_impl[name] = [tmpl, fn]
635 | }
636 |
637 | riot.mountTo = function(node, tagName, opts) {
638 | var tag = tag_impl[tagName]
639 | return tag && createTag({ tmpl: tag[0], fn: tag[1], root: node, opts: opts })
640 | }
641 |
642 | riot.mount = function(selector, opts) {
643 | if (selector == '*') selector = Object.keys(tag_impl).join(', ')
644 |
645 | var instances = []
646 |
647 | each(doc.querySelectorAll(selector), function(node) {
648 | if (node.riot) return
649 |
650 | var tagName = node.tagName.toLowerCase(),
651 | instance = riot.mountTo(node, tagName, opts)
652 |
653 | if (instance) {
654 | instances.push(instance)
655 | node.riot = 1
656 | }
657 | })
658 |
659 | return instances
660 | }
661 |
662 | // update everything
663 | riot.update = function() {
664 | return all_tags = all_tags.filter(function(tag) {
665 | return !!tag.update()
666 | })
667 | }
668 |
669 | })(riot, document)
670 |
671 |
--------------------------------------------------------------------------------
/routing_demo/riotcontrol.js:
--------------------------------------------------------------------------------
1 | var _RiotControlApi = ['on','one','off','trigger']
2 | var RiotControl = {
3 | _stores: [],
4 | addStore: function(store) {
5 | this._stores.push(store)
6 | }
7 | }
8 | _RiotControlApi.forEach(function(api){
9 | RiotControl[api] = function() {
10 | var args = [].slice.call(arguments)
11 | this._stores.forEach(function(el){
12 | el[api].apply(null, args)
13 | })
14 | }
15 | })
16 |
--------------------------------------------------------------------------------
/routing_demo/todo.css:
--------------------------------------------------------------------------------
1 |
2 | body {
3 | font-family: 'myriad pro', sans-serif;
4 | font-size: 20px;
5 | border: 0;
6 | }
7 |
8 | a {
9 | text-decoration: none;
10 | color: grey;
11 | }
12 |
13 | item-app {
14 | display: block;
15 | max-width: 400px;
16 | margin: 5% auto;
17 | }
18 |
19 | input {
20 | font-size: 100%;
21 | padding: .6em;
22 | border: 1px solid #ccc;
23 | border-radius: 3px;
24 | float: left;
25 | }
26 |
27 | button {
28 | background-color: #1FADC5;
29 | border: 1px solid rgba(0,0,0,.2);
30 | font-size: 100%;
31 | color: #fff;
32 | padding: .6em 1.2em;
33 | border-radius: 3em;
34 | cursor: pointer;
35 | margin: 0 .3em;
36 | outline: none;
37 | }
38 |
39 | button[disabled] {
40 | background-color: #ddd;
41 | color: #aaa;
42 | }
43 |
44 | ul {
45 | padding: 0;
46 | }
47 |
48 | li {
49 | list-style-type: none;
50 | padding: .2em 0;
51 | }
52 |
53 | .completed {
54 | text-decoration: line-through;
55 | color: #ccc;
56 | }
57 |
58 | label {
59 | cursor: pointer;
60 | }
--------------------------------------------------------------------------------