Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
T
todomvc
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Sven Franck
todomvc
Commits
9df98e41
Commit
9df98e41
authored
Nov 22, 2014
by
Olivier Scherrer
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Olives: Update Olives and Emily to latest 1.x versions
parent
b9c3b488
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
3934 additions
and
2909 deletions
+3934
-2909
examples/olives/bower.json
examples/olives/bower.json
+2
-2
examples/olives/bower_components/emily/build/Emily.js
examples/olives/bower_components/emily/build/Emily.js
+1423
-1005
examples/olives/bower_components/olives/build/Olives.js
examples/olives/bower_components/olives/build/Olives.js
+1518
-964
examples/olives/bower_components/olives/src/Bind.plugin.js
examples/olives/bower_components/olives/src/Bind.plugin.js
+604
-573
examples/olives/bower_components/olives/src/DomUtils.js
examples/olives/bower_components/olives/src/DomUtils.js
+2
-0
examples/olives/bower_components/olives/src/Event.plugin.js
examples/olives/bower_components/olives/src/Event.plugin.js
+118
-116
examples/olives/bower_components/olives/src/LocalStore.js
examples/olives/bower_components/olives/src/LocalStore.js
+3
-1
examples/olives/bower_components/olives/src/OObject.js
examples/olives/bower_components/olives/src/OObject.js
+180
-174
examples/olives/bower_components/olives/src/Place.plugin.js
examples/olives/bower_components/olives/src/Place.plugin.js
+67
-65
examples/olives/bower_components/olives/src/Plugins.js
examples/olives/bower_components/olives/src/Plugins.js
+2
-0
examples/olives/bower_components/olives/src/SocketIOTransport.js
...s/olives/bower_components/olives/src/SocketIOTransport.js
+15
-9
No files found.
examples/olives/bower.json
View file @
9df98e41
...
...
@@ -2,8 +2,8 @@
"name"
:
"todomvc-olives"
,
"version"
:
"0.0.0"
,
"dependencies"
:
{
"olives"
:
"~1.
4
.0"
,
"emily"
:
"~1.
3.5
"
,
"olives"
:
"~1.
6
.0"
,
"emily"
:
"~1.
8.1
"
,
"requirejs"
:
"~2.1.5"
,
"todomvc-common"
:
"~0.3.0"
}
...
...
examples/olives/bower_components/emily/build/Emily.js
View file @
9df98e41
...
...
@@ -7,7 +7,7 @@
*/
/**
* Emily
* Emily
.js - http://flams.github.com/emily/
* Copyright(c) 2012-2013 Olivier Scherrer <pode.fr@gmail.com>
* MIT Licensed
*/
...
...
@@ -19,303 +19,372 @@ define('Tools',[],
*/
function
Tools
(){
return
{
/**
* For applications that don't run in a browser, window is not the global object.
* This function returns the global object wherever the application runs.
* @returns {Object} the global object
*/
getGlobal
:
function
getGlobal
()
{
var
func
=
function
()
{
return
this
;
};
return
func
.
call
(
null
);
},
/**
* Mixes an object into another
* @param {Object} source object to get values from
* @param {Object} destination object to mix values into
* @param {Boolean} optional, set to true to prevent overriding
* @returns {Object} the destination object
*/
mixin
:
function
mixin
(
source
,
destination
,
dontOverride
)
{
this
.
loop
(
source
,
function
(
value
,
idx
)
{
if
(
!
destination
[
idx
]
||
!
dontOverride
)
{
destination
[
idx
]
=
source
[
idx
];
}
});
return
destination
;
},
/**
* Get the closest number in an array
* @param {Number} item the base number
* @param {Array} array the array to search into
* @param {Function} getDiff returns the difference between the base number and
* and the currently read item in the array. The item which returned the smallest difference wins.
* @private
*/
function
_getClosest
(
item
,
array
,
getDiff
)
{
var
closest
,
diff
;
/**
* Count the number of properties in an object
* It doesn't look up in the prototype chain
* @param {Object} object the object to count
* @returns {Number}
*/
count
:
function
count
(
object
)
{
var
nbItems
=
0
;
this
.
loop
(
object
,
function
()
{
nbItems
++
;
});
if
(
!
array
)
{
return
;
}
return
nbItems
;
},
array
.
forEach
(
function
(
comparedItem
,
comparedItemIndex
)
{
var
thisDiff
=
getDiff
(
comparedItem
,
item
);
/**
* Compares the properties of two objects and returns true if they're the same
* It's doesn't do it recursively
* @param {Object} first object
* @param {Object} second object
* @returns {Boolean} true if the two objets have the same properties
*/
compareObjects
:
function
compareObjects
(
object1
,
object2
)
{
var
getOwnProperties
=
function
(
object
)
{
return
Object
.
getOwnPropertyNames
(
object
).
sort
().
join
(
""
);
};
return
getOwnProperties
(
object1
)
==
getOwnProperties
(
object2
);
},
if
(
thisDiff
>=
0
&&
(
typeof
diff
==
"
undefined
"
||
thisDiff
<
diff
))
{
diff
=
thisDiff
;
closest
=
comparedItemIndex
;
}
});
/**
* Compares two numbers and tells if the first one is bigger (1), smaller (-1) or equal (0)
* @param {Number} number1 the first number
* @param {Number} number2 the second number
* @returns 1 if number1>number2, -1 if number2>number1, 0 if equal
*/
compareNumbers
:
function
compareNumbers
(
number1
,
number2
)
{
if
(
number1
>
number2
)
{
return
1
;
}
else
if
(
number1
<
number2
)
{
return
-
1
;
}
else
{
return
0
;
}
},
return
closest
;
}
/**
* Transform array-like objects to array, such as nodeLists or arguments
* @param {Array-like object}
* @returns {Array}
*/
toArray
:
function
toArray
(
array
)
{
return
[].
slice
.
call
(
array
);
},
return
{
/**
* For applications that don't run in a browser, window is not the global object.
* This function returns the global object wherever the application runs.
* @returns {Object} the global object
*/
getGlobal
:
function
getGlobal
()
{
var
func
=
function
()
{
return
this
;
};
return
func
.
call
(
null
);
},
/**
* Small adapter for looping over objects and arrays
* Warning: it's not meant to be used with nodeList
* To use with nodeList, convert to array first
* @param {Array/Object} iterated the array or object to loop through
* @param {Function} callback the function to execute for each iteration
* @param {Object} scope the scope in which to execute the callback
* @returns {Boolean} true if executed
*/
loop
:
function
loop
(
iterated
,
callback
,
scope
)
{
var
i
,
length
;
if
(
iterated
instanceof
Object
&&
callback
instanceof
Function
)
{
if
(
iterated
instanceof
Array
)
{
for
(
i
=
0
;
i
<
iterated
.
length
;
i
++
)
{
callback
.
call
(
scope
,
iterated
[
i
],
i
,
iterated
);
}
}
else
{
for
(
i
in
iterated
)
{
if
(
iterated
.
hasOwnProperty
(
i
))
{
callback
.
call
(
scope
,
iterated
[
i
],
i
,
iterated
);
}
}
}
return
true
;
}
else
{
return
false
;
}
},
/**
* Mixes an object into another
* @param {Object} source object to get values from
* @param {Object} destination object to mix values into
* @param {Boolean} optional, set to true to prevent overriding
* @returns {Object} the destination object
*/
mixin
:
function
mixin
(
source
,
destination
,
dontOverride
)
{
this
.
loop
(
source
,
function
(
value
,
idx
)
{
if
(
!
destination
[
idx
]
||
!
dontOverride
)
{
destination
[
idx
]
=
source
[
idx
];
}
});
return
destination
;
},
/**
* Make a diff between two objects
* @param {Array/Object} before is the object as it was before
* @param {Array/Object} after is what it is now
* @example:
* With objects:
*
* before = {a:1, b:2, c:3, d:4, f:6}
* after = {a:1, b:20, d: 4, e: 5}
* will return :
* {
* unchanged: ["a", "d"],
* updated: ["b"],
* deleted: ["f"],
* added: ["e"]
* }
*
* It also works with Arrays:
*
* before = [10, 20, 30]
* after = [15, 20]
* will return :
* {
* unchanged: [1],
* updated: [0],
* deleted: [2],
* added: []
* }
*
* @returns object
*/
objectsDiffs
:
function
objectsDiffs
(
before
,
after
)
{
if
(
before
instanceof
Object
&&
after
instanceof
Object
)
{
var
unchanged
=
[],
updated
=
[],
deleted
=
[],
added
=
[];
// Look through the after object
this
.
loop
(
after
,
function
(
value
,
idx
)
{
// To get the added
if
(
typeof
before
[
idx
]
==
"
undefined
"
)
{
added
.
push
(
idx
);
// The updated
}
else
if
(
value
!==
before
[
idx
])
{
updated
.
push
(
idx
);
// And the unchanged
}
else
if
(
value
===
before
[
idx
])
{
unchanged
.
push
(
idx
);
}
});
// Loop through the before object
this
.
loop
(
before
,
function
(
value
,
idx
)
{
// To get the deleted
if
(
typeof
after
[
idx
]
==
"
undefined
"
)
{
deleted
.
push
(
idx
);
}
});
return
{
updated
:
updated
,
unchanged
:
unchanged
,
added
:
added
,
deleted
:
deleted
};
/**
* Count the number of properties in an object
* It doesn't look up in the prototype chain
* @param {Object} object the object to count
* @returns {Number}
*/
count
:
function
count
(
object
)
{
var
nbItems
=
0
;
this
.
loop
(
object
,
function
()
{
nbItems
++
;
});
}
else
{
return
false
;
}
},
return
nbItems
;
},
/**
* Transforms Arrays and Objects into valid JSON
* @param {Object/Array} object the object to JSONify
* @returns the JSONified object or false if failed
*/
jsonify
:
function
jsonify
(
object
)
{
if
(
object
instanceof
Object
)
{
return
JSON
.
parse
(
JSON
.
stringify
(
object
));
}
else
{
return
false
;
}
},
/**
* Compares the properties of two objects and returns true if they're the same
* It's doesn't do it recursively
* @param {Object} first object
* @param {Object} second object
* @returns {Boolean} true if the two objets have the same properties
*/
compareObjects
:
function
compareObjects
(
object1
,
object2
)
{
var
getOwnProperties
=
function
(
object
)
{
return
Object
.
getOwnPropertyNames
(
object
).
sort
().
join
(
""
);
};
return
getOwnProperties
(
object1
)
==
getOwnProperties
(
object2
);
},
/**
* Clone an Array or an Object
* @param {Array/Object} object the object to clone
* @returns {Array/Object} the cloned object
*/
clone
:
function
clone
(
object
)
{
if
(
object
instanceof
Array
)
{
return
object
.
slice
(
0
);
}
else
if
(
typeof
object
==
"
object
"
&&
object
!==
null
&&
!
(
object
instanceof
RegExp
))
{
return
this
.
mixin
(
object
,
{});
}
else
{
return
false
;
}
},
/**
* Compares two numbers and tells if the first one is bigger (1), smaller (-1) or equal (0)
* @param {Number} number1 the first number
* @param {Number} number2 the second number
* @returns 1 if number1>number2, -1 if number2>number1, 0 if equal
*/
compareNumbers
:
function
compareNumbers
(
number1
,
number2
)
{
if
(
number1
>
number2
)
{
return
1
;
}
else
if
(
number1
<
number2
)
{
return
-
1
;
}
else
{
return
0
;
}
},
/**
* Transform array-like objects to array, such as nodeLists or arguments
* @param {Array-like object}
* @returns {Array}
*/
toArray
:
function
toArray
(
array
)
{
return
[].
slice
.
call
(
array
);
},
/**
*
*
*
*
* Refactoring needed for the following
*
*
*
*
*
*/
/**
* Small adapter for looping over objects and arrays
* Warning: it's not meant to be used with nodeList
* To use with nodeList, convert to array first
* @param {Array/Object} iterated the array or object to loop through
* @param {Function} callback the function to execute for each iteration
* @param {Object} scope the scope in which to execute the callback
* @returns {Boolean} true if executed
*/
loop
:
function
loop
(
iterated
,
callback
,
scope
)
{
var
i
,
length
;
if
(
iterated
instanceof
Object
&&
callback
instanceof
Function
)
{
if
(
iterated
instanceof
Array
)
{
for
(
i
=
0
;
i
<
iterated
.
length
;
i
++
)
{
callback
.
call
(
scope
,
iterated
[
i
],
i
,
iterated
);
}
}
else
{
for
(
i
in
iterated
)
{
if
(
iterated
.
hasOwnProperty
(
i
))
{
callback
.
call
(
scope
,
iterated
[
i
],
i
,
iterated
);
}
}
}
return
true
;
}
else
{
return
false
;
}
},
/**
* Get the property of an object nested in one or more objects
* given an object such as a.b.c.d = 5, getNestedProperty(a, "b.c.d") will return 5.
* @param {Object} object the object to get the property from
* @param {String} property the path to the property as a string
* @returns the object or the the property value if found
*/
getNestedProperty
:
function
getNestedProperty
(
object
,
property
)
{
if
(
object
&&
object
instanceof
Object
)
{
if
(
typeof
property
==
"
string
"
&&
property
!=
""
)
{
var
split
=
property
.
split
(
"
.
"
);
return
split
.
reduce
(
function
(
obj
,
prop
)
{
return
obj
&&
obj
[
prop
];
},
object
);
}
else
if
(
typeof
property
==
"
number
"
)
{
return
object
[
property
];
}
else
{
return
object
;
}
}
else
{
return
object
;
}
},
/**
* Make a diff between two objects
* @param {Array/Object} before is the object as it was before
* @param {Array/Object} after is what it is now
* @example:
* With objects:
*
* before = {a:1, b:2, c:3, d:4, f:6}
* after = {a:1, b:20, d: 4, e: 5}
* will return :
* {
* unchanged: ["a", "d"],
* updated: ["b"],
* deleted: ["f"],
* added: ["e"]
* }
*
* It also works with Arrays:
*
* before = [10, 20, 30]
* after = [15, 20]
* will return :
* {
* unchanged: [1],
* updated: [0],
* deleted: [2],
* added: []
* }
*
* @returns object
*/
objectsDiffs
:
function
objectsDiffs
(
before
,
after
)
{
if
(
before
instanceof
Object
&&
after
instanceof
Object
)
{
var
unchanged
=
[],
updated
=
[],
deleted
=
[],
added
=
[];
// Look through the after object
this
.
loop
(
after
,
function
(
value
,
idx
)
{
// To get the added
if
(
typeof
before
[
idx
]
==
"
undefined
"
)
{
added
.
push
(
idx
);
// The updated
}
else
if
(
value
!==
before
[
idx
])
{
updated
.
push
(
idx
);
// And the unchanged
}
else
if
(
value
===
before
[
idx
])
{
unchanged
.
push
(
idx
);
}
});
// Loop through the before object
this
.
loop
(
before
,
function
(
value
,
idx
)
{
// To get the deleted
if
(
typeof
after
[
idx
]
==
"
undefined
"
)
{
deleted
.
push
(
idx
);
}
});
return
{
updated
:
updated
,
unchanged
:
unchanged
,
added
:
added
,
deleted
:
deleted
};
}
else
{
return
false
;
}
},
/**
* Set the property of an object nested in one or more objects
* If the property doesn't exist, it gets created.
* @param {Object} object
* @param {String} property
* @param value the value to set
* @returns object if no assignment was made or the value if the assignment was made
*/
setNestedProperty
:
function
setNestedProperty
(
object
,
property
,
value
)
{
if
(
object
&&
object
instanceof
Object
)
{
if
(
typeof
property
==
"
string
"
&&
property
!=
""
)
{
var
split
=
property
.
split
(
"
.
"
);
return
split
.
reduce
(
function
(
obj
,
prop
,
idx
)
{
obj
[
prop
]
=
obj
[
prop
]
||
{};
if
(
split
.
length
==
(
idx
+
1
))
{
obj
[
prop
]
=
value
;
}
return
obj
[
prop
];
},
object
);
}
else
if
(
typeof
property
==
"
number
"
)
{
object
[
property
]
=
value
;
return
object
[
property
];
}
else
{
return
object
;
}
}
else
{
return
object
;
}
}
/**
* Transforms Arrays and Objects into valid JSON
* @param {Object/Array} object the object to JSONify
* @returns the JSONified object or false if failed
*/
jsonify
:
function
jsonify
(
object
)
{
if
(
object
instanceof
Object
)
{
return
JSON
.
parse
(
JSON
.
stringify
(
object
));
}
else
{
return
false
;
}
},
/**
* Clone an Array or an Object
* @param {Array/Object} object the object to clone
* @returns {Array/Object} the cloned object
*/
clone
:
function
clone
(
object
)
{
if
(
object
instanceof
Array
)
{
return
object
.
slice
(
0
);
}
else
if
(
typeof
object
==
"
object
"
&&
object
!==
null
&&
!
(
object
instanceof
RegExp
))
{
return
this
.
mixin
(
object
,
{});
}
else
{
return
false
;
}
},
};
/**
*
*
*
*
* Refactoring needed for the following
*
*
*
*
*
*/
/**
* Get the property of an object nested in one or more objects
* given an object such as a.b.c.d = 5, getNestedProperty(a, "b.c.d") will return 5.
* @param {Object} object the object to get the property from
* @param {String} property the path to the property as a string
* @returns the object or the the property value if found
*/
getNestedProperty
:
function
getNestedProperty
(
object
,
property
)
{
if
(
object
&&
object
instanceof
Object
)
{
if
(
typeof
property
==
"
string
"
&&
property
!==
""
)
{
var
split
=
property
.
split
(
"
.
"
);
return
split
.
reduce
(
function
(
obj
,
prop
)
{
return
obj
&&
obj
[
prop
];
},
object
);
}
else
if
(
typeof
property
==
"
number
"
)
{
return
object
[
property
];
}
else
{
return
object
;
}
}
else
{
return
object
;
}
},
/**
* Set the property of an object nested in one or more objects
* If the property doesn't exist, it gets created.
* @param {Object} object
* @param {String} property
* @param value the value to set
* @returns object if no assignment was made or the value if the assignment was made
*/
setNestedProperty
:
function
setNestedProperty
(
object
,
property
,
value
)
{
if
(
object
&&
object
instanceof
Object
)
{
if
(
typeof
property
==
"
string
"
&&
property
!==
""
)
{
var
split
=
property
.
split
(
"
.
"
);
return
split
.
reduce
(
function
(
obj
,
prop
,
idx
)
{
obj
[
prop
]
=
obj
[
prop
]
||
{};
if
(
split
.
length
==
(
idx
+
1
))
{
obj
[
prop
]
=
value
;
}
return
obj
[
prop
];
},
object
);
}
else
if
(
typeof
property
==
"
number
"
)
{
object
[
property
]
=
value
;
return
object
[
property
];
}
else
{
return
object
;
}
}
else
{
return
object
;
}
},
/**
* Get the closest number in an array given a base number
* Example: closest(30, [20, 0, 50, 29]) will return 3 as 29 is the closest item
* @param {Number} item the base number
* @param {Array} array the array of numbers to search into
* @returns {Number} the index of the closest item in the array
*/
closest
:
function
closest
(
item
,
array
)
{
return
_getClosest
(
item
,
array
,
function
(
comparedItem
,
item
)
{
return
Math
.
abs
(
comparedItem
-
item
);
});
},
/**
* Get the closest greater number in an array given a base number
* Example: closest(30, [20, 0, 50, 29]) will return 2 as 50 is the closest greater item
* @param {Number} item the base number
* @param {Array} array the array of numbers to search into
* @returns {Number} the index of the closest item in the array
*/
closestGreater
:
function
closestGreater
(
item
,
array
)
{
return
_getClosest
(
item
,
array
,
function
(
comparedItem
,
item
)
{
return
comparedItem
-
item
;
});
},
/**
* Get the closest lower number in an array given a base number
* Example: closest(30, [20, 0, 50, 29]) will return 0 as 20 is the closest lower item
* @param {Number} item the base number
* @param {Array} array the array of numbers to search into
* @returns {Number} the index of the closest item in the array
*/
closestLower
:
function
closestLower
(
item
,
array
)
{
return
_getClosest
(
item
,
array
,
function
(
comparedItem
,
item
)
{
return
item
-
comparedItem
;
});
}
};
});
/**
* Emily
* Emily
.js - http://flams.github.com/emily/
* Copyright(c) 2012-2013 Olivier Scherrer <pode.fr@gmail.com>
* MIT Licensed
*/
...
...
@@ -330,6 +399,8 @@ define('Observable',["Tools"],
*/
function
Observable
(
Tools
)
{
/**
* Defines the Observable
* @private
...
...
@@ -363,6 +434,21 @@ function Observable(Tools) {
}
};
/**
* Listen to an event just once before removing the handler
* @param {String} topic the topic to observe
* @param {Function} callback the callback to execute
* @param {Object} scope the scope in which to execute the callback
* @returns handle
*/
this
.
once
=
function
once
(
topic
,
callback
,
scope
)
{
var
handle
=
this
.
watch
(
topic
,
function
()
{
callback
.
apply
(
scope
,
arguments
);
this
.
unwatch
(
handle
);
},
this
);
return
handle
;
};
/**
* Remove an observer
* @param {Handle} handle returned by the watch method
...
...
@@ -398,14 +484,16 @@ function Observable(Tools) {
if
(
observers
)
{
Tools
.
loop
(
observers
,
function
(
value
)
{
try
{
value
&&
value
[
0
].
apply
(
value
[
1
]
||
null
,
args
);
if
(
value
)
{
value
[
0
].
apply
(
value
[
1
]
||
null
,
args
);
}
}
catch
(
err
)
{
}
});
return
true
;
}
else
{
return
false
;
}
}
,
}
;
/**
* Check if topic has the described observer
...
...
@@ -444,7 +532,7 @@ function Observable(Tools) {
});
/**
* Emily
* Emily
.js - http://flams.github.com/emily/
* Copyright(c) 2012-2013 Olivier Scherrer <pode.fr@gmail.com>
* MIT Licensed
*/
...
...
@@ -456,7 +544,9 @@ define('StateMachine',["Tools"],
*/
function
StateMachine
(
Tools
)
{
/**
/**
* @param initState {String} the initial state
* @param diagram {Object} the diagram that describes the state machine
* @example
...
...
@@ -475,228 +565,229 @@ function StateMachine(Tools) {
*
* @return the stateMachine object
*/
function
StateMachineConstructor
(
$initState
,
$diagram
)
{
/**
* The list of states
* @private
*/
var
_states
=
{},
function
StateMachineConstructor
(
$initState
,
$diagram
)
{
/**
* The current state
* @private
*/
_currentState
=
""
;
/**
* The list of states
* @private
*/
var
_states
=
{},
/**
* Set the initialization state
* @param {String} name the name of the init state
* @returns {Boolean}
*/
this
.
init
=
function
init
(
name
)
{
if
(
_states
[
name
])
{
_currentState
=
name
;
return
true
;
}
else
{
return
false
;
}
};
/**
* The current state
* @private
*/
_currentState
=
""
;
/**
* Add a new
state
* @priv
ate
* @param {String} name the name of the state
* @returns {State} a new state
*/
this
.
add
=
function
add
(
name
)
{
if
(
!
_states
[
name
])
{
return
_states
[
name
]
=
new
Transition
()
;
}
else
{
return
_states
[
name
]
;
}
};
/**
* Set the initialization
state
* @param {String} name the name of the init st
ate
* @returns {Boolean}
*/
this
.
init
=
function
init
(
name
)
{
if
(
_states
[
name
]
)
{
_currentState
=
name
;
return
true
;
}
else
{
return
false
;
}
};
/**
* Get an existing state
* @private
* @param {String} name the name of the state
* @returns {State} the state
*/
this
.
get
=
function
get
(
name
)
{
return
_states
[
name
];
};
/**
* Add a new state
* @private
* @param {String} name the name of the state
* @returns {State} a new state
*/
this
.
add
=
function
add
(
name
)
{
if
(
!
_states
[
name
])
{
var
transition
=
_states
[
name
]
=
new
Transition
();
return
transition
;
}
else
{
return
_states
[
name
];
}
};
/**
* Get the current state
* @returns {String}
*/
this
.
getCurrent
=
function
getCurrent
()
{
return
_currentState
;
};
/**
* Get an existing state
* @private
* @param {String} name the name of the state
* @returns {State} the state
*/
this
.
get
=
function
get
(
name
)
{
return
_states
[
name
];
};
/**
* Tell if the state machine has the given state
* @param {String} state the name of the state
* @returns {Boolean} true if it has the given state
*/
this
.
has
=
function
has
(
state
)
{
return
_states
.
hasOwnProperty
(
state
);
};
/**
* Get the current state
* @returns {String}
*/
this
.
getCurrent
=
function
getCurrent
()
{
return
_currentState
;
};
/**
* Advances the state machine to a given state
* @param {String} state the name of the state to advance the state machine to
* @returns {Boolean} true if it has the given state
*/
this
.
advance
=
function
advance
(
state
)
{
if
(
this
.
has
(
state
))
{
_currentState
=
state
;
return
true
;
}
else
{
return
false
;
}
};
/**
* Tell if the state machine has the given state
* @param {String} state the name of the state
* @returns {Boolean} true if it has the given state
*/
this
.
has
=
function
has
(
state
)
{
return
_states
.
hasOwnProperty
(
state
);
};
/**
* Pass an event to the state machine
* @param {String} name the name of the event
* @returns {Boolean} true if the event exists in the current state
*/
this
.
event
=
function
event
(
name
)
{
var
nextState
;
/**
* Advances the state machine to a given state
* @param {String} state the name of the state to advance the state machine to
* @returns {Boolean} true if it has the given state
*/
this
.
advance
=
function
advance
(
state
)
{
if
(
this
.
has
(
state
))
{
_currentState
=
state
;
return
true
;
}
else
{
return
false
;
}
};
nextState
=
_states
[
_currentState
].
event
.
apply
(
_states
[
_currentState
].
event
,
Tools
.
toArray
(
arguments
));
// False means that there's no such event
// But undefined means that the state doesn't change
if
(
nextState
===
false
)
{
return
false
;
}
else
{
// There could be no next state, so the current one remains
if
(
nextState
)
{
// Call the exit action if any
_states
[
_currentState
].
event
(
"
exit
"
);
_currentState
=
nextState
;
// Call the new state's entry action if any
_states
[
_currentState
].
event
(
"
entry
"
);
}
return
true
;
}
};
/**
* Pass an event to the state machine
* @param {String} name the name of the event
* @returns {Boolean} true if the event exists in the current state
*/
this
.
event
=
function
event
(
name
)
{
var
nextState
;
nextState
=
_states
[
_currentState
].
event
.
apply
(
_states
[
_currentState
].
event
,
Tools
.
toArray
(
arguments
));
// False means that there's no such event
// But undefined means that the state doesn't change
if
(
nextState
===
false
)
{
return
false
;
}
else
{
// There could be no next state, so the current one remains
if
(
nextState
)
{
// Call the exit action if any
_states
[
_currentState
].
event
(
"
exit
"
);
_currentState
=
nextState
;
// Call the new state's entry action if any
_states
[
_currentState
].
event
(
"
entry
"
);
}
return
true
;
}
};
/**
* Initializes the StateMachine with the given diagram
*/
Tools
.
loop
(
$diagram
,
function
(
transition
,
state
)
{
var
myState
=
this
.
add
(
state
);
transition
.
forEach
(
function
(
params
){
myState
.
add
.
apply
(
null
,
params
);
});
},
this
);
/**
* Initializes the StateMachine with the given diagram
*/
Tools
.
loop
(
$diagram
,
function
(
transition
,
state
)
{
var
myState
=
this
.
add
(
state
);
transition
.
forEach
(
function
(
params
){
myState
.
add
.
apply
(
null
,
params
);
});
},
this
);
/**
* Sets its initial state
*/
this
.
init
(
$initState
);
}
/**
* Sets its initial state
*/
this
.
init
(
$initState
);
}
/**
* Each state has associated transitions
/**
* Each state has associated transitions
* @constructor
*/
function
Transition
()
{
*/
function
Transition
()
{
/**
* The list of transitions associated to a state
* @private
*/
var
_transitions
=
{};
/**
* The list of transitions associated to a state
* @private
*/
var
_transitions
=
{};
/**
* Add a new transition
* @private
* @param {String} event the event that will trigger the transition
* @param {Function} action the function that is executed
* @param {Object} scope [optional] the scope in which to execute the action
* @param {String} next [optional] the name of the state to transit to.
* @returns {Boolean} true if success, false if the transition already exists
*/
this
.
add
=
function
add
(
event
,
action
,
scope
,
next
)
{
/**
* Add a new transition
* @private
* @param {String} event the event that will trigger the transition
* @param {Function} action the function that is executed
* @param {Object} scope [optional] the scope in which to execute the action
* @param {String} next [optional] the name of the state to transit to.
* @returns {Boolean} true if success, false if the transition already exists
*/
this
.
add
=
function
add
(
event
,
action
,
scope
,
next
)
{
var
arr
=
[];
var
arr
=
[];
if
(
_transitions
[
event
])
{
return
false
;
}
if
(
_transitions
[
event
])
{
return
false
;
}
if
(
typeof
event
==
"
string
"
&&
typeof
action
==
"
function
"
)
{
if
(
typeof
event
==
"
string
"
&&
typeof
action
==
"
function
"
)
{
arr
[
0
]
=
action
;
arr
[
0
]
=
action
;
if
(
typeof
scope
==
"
object
"
)
{
arr
[
1
]
=
scope
;
}
if
(
typeof
scope
==
"
object
"
)
{
arr
[
1
]
=
scope
;
}
if
(
typeof
scope
==
"
string
"
)
{
arr
[
2
]
=
scope
;
}
if
(
typeof
scope
==
"
string
"
)
{
arr
[
2
]
=
scope
;
}
if
(
typeof
next
==
"
string
"
)
{
arr
[
2
]
=
next
;
}
if
(
typeof
next
==
"
string
"
)
{
arr
[
2
]
=
next
;
}
_transitions
[
event
]
=
arr
;
return
true
;
}
_transitions
[
event
]
=
arr
;
return
true
;
}
return
false
;
};
return
false
;
};
/**
* Check if a transition can be triggered with given event
* @private
* @param {String} event the name of the event
* @returns {Boolean} true if exists
*/
this
.
has
=
function
has
(
event
)
{
return
!!
_transitions
[
event
];
};
/**
* Check if a transition can be triggered with given event
* @private
* @param {String} event the name of the event
* @returns {Boolean} true if exists
*/
this
.
has
=
function
has
(
event
)
{
return
!!
_transitions
[
event
];
};
/**
* Get a transition from it's event
* @private
* @param {String} event the name of the event
* @return the transition
*/
this
.
get
=
function
get
(
event
)
{
return
_transitions
[
event
]
||
false
;
};
/**
* Get a transition from it's event
* @private
* @param {String} event the name of the event
* @return the transition
*/
this
.
get
=
function
get
(
event
)
{
return
_transitions
[
event
]
||
false
;
};
/**
* Execute the action associated to the given event
* @param {String} event the name of the event
* @param {params} params to pass to the action
* @private
* @returns false if error, the next state or undefined if success (that sounds weird)
*/
this
.
event
=
function
event
(
e
vent
)
{
var
_transition
=
_transitions
[
e
vent
];
if
(
_transition
)
{
_transition
[
0
].
apply
(
_transition
[
1
],
Tools
.
toArray
(
arguments
).
slice
(
1
));
return
_transition
[
2
];
}
else
{
return
false
;
}
};
};
/**
* Execute the action associated to the given event
* @param {String} event the name of the event
* @param {params} params to pass to the action
* @private
* @returns false if error, the next state or undefined if success (that sounds weird)
*/
this
.
event
=
function
event
(
newE
vent
)
{
var
_transition
=
_transitions
[
newE
vent
];
if
(
_transition
)
{
_transition
[
0
].
apply
(
_transition
[
1
],
Tools
.
toArray
(
arguments
).
slice
(
1
));
return
_transition
[
2
];
}
else
{
return
false
;
}
};
}
return
StateMachineConstructor
;
return
StateMachineConstructor
;
});
/**
* Emily
* Emily
.js - http://flams.github.com/emily/
* Copyright(c) 2012-2013 Olivier Scherrer <pode.fr@gmail.com>
* MIT Licensed
*/
...
...
@@ -708,107 +799,109 @@ define('Promise',["Observable", "StateMachine"],
*/
function
Promise
(
Observable
,
StateMachine
)
{
return
function
PromiseConstructor
()
{
/**
* The fulfilled value
* @private
*/
var
_value
=
null
,
/**
* The rejection reason
* @private
*/
_reason
=
null
,
return
function
PromiseConstructor
()
{
/**
* The funky observabl
e
* @private
*/
_observable
=
new
Observable
,
/**
* The fulfilled valu
e
* @private
*/
var
_value
=
null
,
/**
* The state machine States & transitions
* @private
*/
_states
=
{
// The promise is pending
"
Pending
"
:
[
// It can only be fulfilled when pending
[
"
fulfill
"
,
function
onFulfill
(
value
)
{
_value
=
value
;
_observable
.
notify
(
"
fulfill
"
,
value
);
// Then it transits to the fulfilled state
},
"
Fulfilled
"
],
// it can only be rejected when pending
[
"
reject
"
,
function
onReject
(
reason
)
{
_reason
=
reason
;
_observable
.
notify
(
"
reject
"
,
reason
);
// Then it transits to the rejected state
},
"
Rejected
"
],
// When pending, add the resolver to an observable
[
"
toFulfill
"
,
function
toFulfill
(
resolver
)
{
_observable
.
watch
(
"
fulfill
"
,
resolver
);
}],
// When pending, add the resolver to an observable
[
"
toReject
"
,
function
toReject
(
resolver
)
{
_observable
.
watch
(
"
reject
"
,
resolver
);
}]],
// When fulfilled,
"
Fulfilled
"
:
[
// We directly call the resolver with the value
[
"
toFulfill
"
,
function
toFulfill
(
resolver
)
{
setTimeout
(
function
()
{
resolver
(
_value
);
},
0
);
}]],
// When rejected
"
Rejected
"
:
[
// We directly call the resolver with the reason
[
"
toReject
"
,
function
toReject
(
resolver
)
{
setTimeout
(
function
()
{
resolver
(
_reason
);
},
0
);
}]]
},
/**
* The rejection reason
* @private
*/
_reason
=
null
,
/**
* The stateMachin
e
* @private
*/
_stateMachine
=
new
StateMachine
(
"
Pending
"
,
_states
);
/**
* The funky observabl
e
* @private
*/
_observable
=
new
Observable
(),
/**
* Fulfilled the promise.
* A promise can be fulfilld only once.
* @param the fulfillment value
* @returns the promise
*/
this
.
fulfill
=
function
fulfill
(
value
)
{
_stateMachine
.
event
(
"
fulfill
"
,
value
);
return
this
;
};
/**
* The state machine States & transitions
* @private
*/
_states
=
{
// The promise is pending
"
Pending
"
:
[
// It can only be fulfilled when pending
[
"
fulfill
"
,
function
onFulfill
(
value
)
{
_value
=
value
;
_observable
.
notify
(
"
fulfill
"
,
value
);
// Then it transits to the fulfilled state
},
"
Fulfilled
"
],
// it can only be rejected when pending
[
"
reject
"
,
function
onReject
(
reason
)
{
_reason
=
reason
;
_observable
.
notify
(
"
reject
"
,
reason
);
// Then it transits to the rejected state
},
"
Rejected
"
],
// When pending, add the resolver to an observable
[
"
toFulfill
"
,
function
toFulfill
(
resolver
)
{
_observable
.
watch
(
"
fulfill
"
,
resolver
);
}],
// When pending, add the resolver to an observable
[
"
toReject
"
,
function
toReject
(
resolver
)
{
_observable
.
watch
(
"
reject
"
,
resolver
);
}]],
// When fulfilled,
"
Fulfilled
"
:
[
// We directly call the resolver with the value
[
"
toFulfill
"
,
function
toFulfill
(
resolver
)
{
setTimeout
(
function
()
{
resolver
(
_value
);
},
0
);
}]],
// When rejected
"
Rejected
"
:
[
// We directly call the resolver with the reason
[
"
toReject
"
,
function
toReject
(
resolver
)
{
setTimeout
(
function
()
{
resolver
(
_reason
);
},
0
);
}]]
},
/**
* Reject the promise.
* A promise can be rejected only once.
* @param the rejection value
* @returns true if the rejection function was called
*/
this
.
reject
=
function
reject
(
reason
)
{
_stateMachine
.
event
(
"
reject
"
,
reason
);
return
this
;
};
/**
* The stateMachine
* @private
*/
_stateMachine
=
new
StateMachine
(
"
Pending
"
,
_states
);
/**
/**
* Fulfilled the promise.
* A promise can be fulfilld only once.
* @param the fulfillment value
* @returns the promise
*/
this
.
fulfill
=
function
fulfill
(
value
)
{
_stateMachine
.
event
(
"
fulfill
"
,
value
);
return
this
;
};
/**
* Reject the promise.
* A promise can be rejected only once.
* @param the rejection value
* @returns true if the rejection function was called
*/
this
.
reject
=
function
reject
(
reason
)
{
_stateMachine
.
event
(
"
reject
"
,
reason
);
return
this
;
};
/**
* The callbacks to call after fulfillment or rejection
* @param {Function} fulfillmentCallback the first parameter is a success function, it can be followed by a scope
* @param {Function} the second, or third parameter is the rejection callback, it can also be followed by a scope
...
...
@@ -822,88 +915,88 @@ function Promise(Observable, StateMachine) {
* @returns {Promise} the new promise
*/
this
.
then
=
function
then
()
{
var
promise
=
new
PromiseConstructor
;
// If a fulfillment callback is given
if
(
arguments
[
0
]
instanceof
Function
)
{
// If the second argument is also a function, then no scope is given
if
(
arguments
[
1
]
instanceof
Function
)
{
_stateMachine
.
event
(
"
toFulfill
"
,
this
.
makeResolver
(
promise
,
arguments
[
0
]));
}
else
{
// If the second argument is not a function, it's the scope
_stateMachine
.
event
(
"
toFulfill
"
,
this
.
makeResolver
(
promise
,
arguments
[
0
],
arguments
[
1
]));
}
}
else
{
// If no fulfillment callback given, give a default one
_stateMachine
.
event
(
"
toFulfill
"
,
this
.
makeResolver
(
promise
,
function
()
{
promise
.
fulfill
(
_value
);
}));
}
// if the second arguments is a callback, it's the rejection one, and the next argument is the scope
if
(
arguments
[
1
]
instanceof
Function
)
{
_stateMachine
.
event
(
"
toReject
"
,
this
.
makeResolver
(
promise
,
arguments
[
1
],
arguments
[
2
]));
}
// if the third arguments is a callback, it's the rejection one, and the next arguments is the sopce
if
(
arguments
[
2
]
instanceof
Function
)
{
var
promise
=
new
PromiseConstructor
()
;
// If a fulfillment callback is given
if
(
arguments
[
0
]
instanceof
Function
)
{
// If the second argument is also a function, then no scope is given
if
(
arguments
[
1
]
instanceof
Function
)
{
_stateMachine
.
event
(
"
toFulfill
"
,
this
.
makeResolver
(
promise
,
arguments
[
0
]));
}
else
{
// If the second argument is not a function, it's the scope
_stateMachine
.
event
(
"
toFulfill
"
,
this
.
makeResolver
(
promise
,
arguments
[
0
],
arguments
[
1
]));
}
}
else
{
// If no fulfillment callback given, give a default one
_stateMachine
.
event
(
"
toFulfill
"
,
this
.
makeResolver
(
promise
,
function
()
{
promise
.
fulfill
(
_value
);
}));
}
// if the second arguments is a callback, it's the rejection one, and the next argument is the scope
if
(
arguments
[
1
]
instanceof
Function
)
{
_stateMachine
.
event
(
"
toReject
"
,
this
.
makeResolver
(
promise
,
arguments
[
1
],
arguments
[
2
]));
}
// if the third arguments is a callback, it's the rejection one, and the next arguments is the sopce
if
(
arguments
[
2
]
instanceof
Function
)
{
_stateMachine
.
event
(
"
toReject
"
,
this
.
makeResolver
(
promise
,
arguments
[
2
],
arguments
[
3
]));
}
}
// If no rejection callback is given, give a default one
if
(
!
(
arguments
[
1
]
instanceof
Function
)
&&
!
(
arguments
[
2
]
instanceof
Function
))
{
_stateMachine
.
event
(
"
toReject
"
,
this
.
makeResolver
(
promise
,
function
()
{
promise
.
reject
(
_reason
);
}));
}
// If no rejection callback is given, give a default one
if
(
!
(
arguments
[
1
]
instanceof
Function
)
&&
!
(
arguments
[
2
]
instanceof
Function
))
{
_stateMachine
.
event
(
"
toReject
"
,
this
.
makeResolver
(
promise
,
function
()
{
promise
.
reject
(
_reason
);
}));
}
return
promise
;
return
promise
;
};
/**
* Synchronize this promise with a thenable
* @returns {Boolean} false if the given sync is not a thenable
*/
this
.
sync
=
function
sync
(
syncWith
)
{
if
(
syncWith
instanceof
Object
&&
syncWith
.
then
)
{
var
onFulfilled
=
function
onFulfilled
(
value
)
{
this
.
fulfill
(
value
);
},
onRejected
=
function
onRejected
(
reason
)
{
this
.
reject
(
reason
);
};
syncWith
.
then
(
onFulfilled
.
bind
(
this
),
onRejected
.
bind
(
this
));
return
true
;
}
else
{
return
false
;
}
};
* Synchronize this promise with a thenable
* @returns {Boolean} false if the given sync is not a thenable
*/
this
.
sync
=
function
sync
(
syncWith
)
{
if
(
syncWith
instanceof
Object
&&
syncWith
.
then
)
{
var
onFulfilled
=
function
onFulfilled
(
value
)
{
this
.
fulfill
(
value
);
},
onRejected
=
function
onRejected
(
reason
)
{
this
.
reject
(
reason
);
};
syncWith
.
then
(
onFulfilled
.
bind
(
this
),
onRejected
.
bind
(
this
));
return
true
;
}
else
{
return
false
;
}
};
/**
* Make a resolver
* for debugging only
* @private
* @returns {Function} a closure
*/
*/
this
.
makeResolver
=
function
makeResolver
(
promise
,
func
,
scope
)
{
return
function
resolver
(
value
)
{
var
returnedPromise
;
try
{
returnedPromise
=
func
.
call
(
scope
,
value
);
if
(
!
promise
.
sync
(
returnedPromise
))
{
promise
.
fulfill
(
returnedPromise
);
}
}
catch
(
err
)
{
promise
.
reject
(
err
);
}
}
return
function
resolver
(
value
)
{
var
returnedPromise
;
try
{
returnedPromise
=
func
.
call
(
scope
,
value
);
if
(
!
promise
.
sync
(
returnedPromise
))
{
promise
.
fulfill
(
returnedPromise
);
}
}
catch
(
err
)
{
promise
.
reject
(
err
);
}
};
};
/**
...
...
@@ -912,7 +1005,7 @@ function Promise(Observable, StateMachine) {
* @private
*/
this
.
getReason
=
function
getReason
()
{
return
_reason
;
return
_reason
;
};
/**
...
...
@@ -921,40 +1014,40 @@ function Promise(Observable, StateMachine) {
* @private
*/
this
.
getValue
=
function
getValue
()
{
return
_value
;
return
_value
;
};
/**
* Get the promise's observable
* for debugging only
* @private
* @returns {Observable}
*/
this
.
getObservable
=
function
getObservable
()
{
return
_observable
;
};
/**
* Get the promise's observable
* for debugging only
* @private
* @returns {Observable}
*/
this
.
getObservable
=
function
getObservable
()
{
return
_observable
;
};
/**
* Get the promise's stateMachine
* for debugging only
* @private
* @returns {StateMachine}
*/
this
.
getStateMachine
=
function
getStateMachine
()
{
return
_stateMachine
;
};
/**
* Get the promise's stateMachine
* for debugging only
* @private
* @returns {StateMachine}
*/
this
.
getStateMachine
=
function
getStateMachine
()
{
return
_stateMachine
;
};
/**
* Get the statesMachine's states
* for debugging only
* @private
* @returns {Object}
*/
this
.
getStates
=
function
getStates
()
{
return
_states
;
};
/**
* Get the statesMachine's states
* for debugging only
* @private
* @returns {Object}
*/
this
.
getStates
=
function
getStates
()
{
return
_states
;
};
}
};
...
...
@@ -962,7 +1055,7 @@ function Promise(Observable, StateMachine) {
});
/**
* Emily
* Emily
.js - http://flams.github.com/emily/
* Copyright(c) 2012-2013 Olivier Scherrer <pode.fr@gmail.com>
* MIT Licensed
*/
...
...
@@ -975,297 +1068,379 @@ define('Store',["Observable", "Tools"],
*/
function
Store
(
Observable
,
Tools
)
{
/**
* Defines the Store
* @param {Array/Object} the data to initialize the store with
* @returns
*/
return
function
StoreConstructor
(
$data
)
{
/**
* Where the data is stored
* @private
*/
var
_data
=
Tools
.
clone
(
$data
)
||
{},
/**
* Defines the Store
* @param {Array/Object} the data to initialize the store with
* @returns
*/
return
function
StoreConstructor
(
$data
)
{
/**
* The observable for publishing changes on the store iself
* @private
*/
_storeObservable
=
new
Observable
()
,
/**
* Where the data is stored
* @private
*/
var
_data
=
Tools
.
clone
(
$data
)
||
{}
,
/**
* The observable for publishing changes on a value
* @private
*/
_valu
eObservable
=
new
Observable
(),
/**
* The observable for publishing changes on the store iself
* @private
*/
_stor
eObservable
=
new
Observable
(),
/**
* Gets the difference between two objects and notifies them
* @private
* @param {Object} previousData
*/
_notifyDiffs
=
function
_notifyDiffs
(
previousData
)
{
var
diffs
=
Tools
.
objectsDiffs
(
previousData
,
_data
);
[
"
updated
"
,
"
deleted
"
,
"
added
"
].
forEach
(
function
(
value
)
{
diffs
[
value
].
forEach
(
function
(
dataIndex
)
{
_storeObservable
.
notify
(
value
,
dataIndex
,
_data
[
dataIndex
]);
_valueObservable
.
notify
(
dataIndex
,
_data
[
dataIndex
],
value
);
});
});
};
/**
* The observable for publishing changes on a value
* @private
*/
_valueObservable
=
new
Observable
(),
/**
* Get the number of items in the store
* @returns {Number} the number of items in the store
*/
this
.
getNbItems
=
function
()
{
return
_data
instanceof
Array
?
_data
.
length
:
Tools
.
count
(
_data
);
};
/**
* Saves the handles for the subscriptions of the computed properties
* @private
*/
_computed
=
[],
/**
* Count is an alias for getNbItems
* @returns {Number} the number of items in the store
*/
this
.
count
=
this
.
getNbItems
;
/**
* Gets the difference between two objects and notifies them
* @private
* @param {Object} previousData
*/
_notifyDiffs
=
function
_notifyDiffs
(
previousData
)
{
var
diffs
=
Tools
.
objectsDiffs
(
previousData
,
_data
);
[
"
updated
"
,
"
deleted
"
,
"
added
"
].
forEach
(
function
(
value
)
{
diffs
[
value
].
forEach
(
function
(
dataIndex
)
{
_storeObservable
.
notify
(
value
,
dataIndex
,
_data
[
dataIndex
]);
_valueObservable
.
notify
(
dataIndex
,
_data
[
dataIndex
],
value
);
});
});
};
/**
* Get a value from its index
* @param {String} name the name of the index
* @returns the value
*/
this
.
get
=
function
get
(
name
)
{
return
_data
[
name
];
};
/**
* Get the number of items in the store
* @returns {Number} the number of items in the store
*/
this
.
getNbItems
=
function
()
{
return
_data
instanceof
Array
?
_data
.
length
:
Tools
.
count
(
_data
);
};
/**
* Checks if the store has a given value
* @param {String} name the name of the index
* @returns {Boolean} true if the value exists
*/
this
.
has
=
function
has
(
name
)
{
return
_data
.
hasOwnProperty
(
name
);
};
/**
* Count is an alias for getNbItems
* @returns {Number} the number of items in the store
*/
this
.
count
=
this
.
getNbItems
;
/**
* Set a new value and overrides an existing one
* @param {String} name the name of the index
* @param value the value to assign
* @returns true if value is set
*/
this
.
set
=
function
set
(
name
,
value
)
{
var
hasPrevious
,
previousValue
,
action
;
if
(
typeof
name
!=
"
undefined
"
)
{
hasPrevious
=
this
.
has
(
name
);
previousValue
=
this
.
get
(
name
);
_data
[
name
]
=
value
;
action
=
hasPrevious
?
"
updated
"
:
"
added
"
;
_storeObservable
.
notify
(
action
,
name
,
_data
[
name
],
previousValue
);
_valueObservable
.
notify
(
name
,
_data
[
name
],
action
,
previousValue
);
return
true
;
}
else
{
return
false
;
}
};
/**
* Get a value from its index
* @param {String} name the name of the index
* @returns the value
*/
this
.
get
=
function
get
(
name
)
{
return
_data
[
name
];
};
/**
* Update the property of an item.
* @param {String} name the name of the index
* @param {String} property the property to modify.
* @param value the value to assign
* @returns false if the Store has no name index
*/
this
.
update
=
function
update
(
name
,
property
,
value
)
{
var
item
;
if
(
this
.
has
(
name
))
{
item
=
this
.
get
(
name
);
Tools
.
setNestedProperty
(
item
,
property
,
value
);
_storeObservable
.
notify
(
"
updated
"
,
property
,
value
);
_valueObservable
.
notify
(
name
,
item
,
"
updated
"
);
return
true
;
}
else
{
return
false
;
}
};
/**
* Checks if the store has a given value
* @param {String} name the name of the index
* @returns {Boolean} true if the value exists
*/
this
.
has
=
function
has
(
name
)
{
return
_data
.
hasOwnProperty
(
name
);
};
/**
* Delete value from its index
* @param {String} name the name of the index from which to delete the value
* @returns true if successfully deleted.
*/
this
.
del
=
function
del
(
name
)
{
if
(
this
.
has
(
name
))
{
if
(
!
this
.
alter
(
"
splice
"
,
name
,
1
))
{
delete
_data
[
name
];
_storeObservable
.
notify
(
"
deleted
"
,
name
);
_valueObservable
.
notify
(
name
,
_data
[
name
],
"
deleted
"
);
}
return
true
;
}
else
{
return
false
;
}
};
/**
* Set a new value and overrides an existing one
* @param {String} name the name of the index
* @param value the value to assign
* @returns true if value is set
*/
this
.
set
=
function
set
(
name
,
value
)
{
var
hasPrevious
,
previousValue
,
action
;
if
(
typeof
name
!=
"
undefined
"
)
{
hasPrevious
=
this
.
has
(
name
);
previousValue
=
this
.
get
(
name
);
_data
[
name
]
=
value
;
action
=
hasPrevious
?
"
updated
"
:
"
added
"
;
_storeObservable
.
notify
(
action
,
name
,
_data
[
name
],
previousValue
);
_valueObservable
.
notify
(
name
,
_data
[
name
],
action
,
previousValue
);
return
true
;
}
else
{
return
false
;
}
};
/**
* Delete multiple indexes. Prefer this one over multiple del calls.
* @param {Array}
* @returns false if param is not an array.
*/
this
.
delAll
=
function
delAll
(
indexes
)
{
if
(
indexes
instanceof
Array
)
{
// Indexes must be removed from the greatest to the lowest
// To avoid trying to remove indexes that don't exist.
// i.e: given [0, 1, 2], remove 1, then 2, 2 doesn't exist anymore
indexes
.
sort
(
Tools
.
compareNumbers
)
.
reverse
()
.
forEach
(
this
.
del
,
this
);
return
true
;
}
else
{
return
false
;
}
};
/**
* Update the property of an item.
* @param {String} name the name of the index
* @param {String} property the property to modify.
* @param value the value to assign
* @returns false if the Store has no name index
*/
this
.
update
=
function
update
(
name
,
property
,
value
)
{
var
item
;
if
(
this
.
has
(
name
))
{
item
=
this
.
get
(
name
);
Tools
.
setNestedProperty
(
item
,
property
,
value
);
_storeObservable
.
notify
(
"
updated
"
,
property
,
value
);
_valueObservable
.
notify
(
name
,
item
,
"
updated
"
);
return
true
;
}
else
{
return
false
;
}
};
/**
* Alter the data be calling one of it's method
* When the modifications are done, it notifies on changes.
* @param {String} func the name of the method
* @returns the result of the method call
*/
this
.
alter
=
function
alter
(
func
)
{
var
apply
,
previousData
;
if
(
_data
[
func
])
{
previousData
=
Tools
.
clone
(
_data
);
apply
=
_data
[
func
].
apply
(
_data
,
Array
.
prototype
.
slice
.
call
(
arguments
,
1
));
_notifyDiffs
(
previousData
);
return
apply
;
}
else
{
return
false
;
}
};
/**
* Delete value from its index
* @param {String} name the name of the index from which to delete the value
* @returns true if successfully deleted.
*/
this
.
del
=
function
del
(
name
)
{
if
(
this
.
has
(
name
))
{
if
(
!
this
.
alter
(
"
splice
"
,
name
,
1
))
{
delete
_data
[
name
];
_storeObservable
.
notify
(
"
deleted
"
,
name
);
_valueObservable
.
notify
(
name
,
_data
[
name
],
"
deleted
"
);
}
return
true
;
}
else
{
return
false
;
}
};
/**
* proxy is an alias for alter
*/
this
.
proxy
=
this
.
alter
;
/**
* Delete multiple indexes. Prefer this one over multiple del calls.
* @param {Array}
* @returns false if param is not an array.
*/
this
.
delAll
=
function
delAll
(
indexes
)
{
if
(
indexes
instanceof
Array
)
{
// Indexes must be removed from the greatest to the lowest
// To avoid trying to remove indexes that don't exist.
// i.e: given [0, 1, 2], remove 1, then 2, 2 doesn't exist anymore
indexes
.
sort
(
Tools
.
compareNumbers
)
.
reverse
()
.
forEach
(
this
.
del
,
this
);
return
true
;
}
else
{
return
false
;
}
};
/**
* Watch the store's modifications
* @param {String} added/updated/deleted
* @param {Function} func the function to execute
* @param {Object} scope the scope in which to execute the function
* @returns {Handle} the subscribe's handler to use to stop watching
*/
this
.
watch
=
function
watch
(
name
,
func
,
scope
)
{
return
_storeObservable
.
watch
(
name
,
func
,
scope
);
};
/**
* Alter the data by calling one of it's method
* When the modifications are done, it notifies on changes.
* If the function called doesn't alter the data, consider using proxy instead
* which is much, much faster.
* @param {String} func the name of the method
* @params {*} any number of params to be given to the func
* @returns the result of the method call
*/
this
.
alter
=
function
alter
(
func
)
{
var
apply
,
previousData
;
if
(
_data
[
func
])
{
previousData
=
Tools
.
clone
(
_data
);
apply
=
this
.
proxy
.
apply
(
this
,
arguments
);
_notifyDiffs
(
previousData
);
_storeObservable
.
notify
(
"
altered
"
,
_data
,
previousData
);
return
apply
;
}
else
{
return
false
;
}
};
/**
* Unwatch the store modifications
* @param {Handle} handle the handler returned by the watch function
* @returns
*/
this
.
unwatch
=
function
unwatch
(
handle
)
{
return
_storeObservable
.
unwatch
(
handle
);
};
/**
* Proxy is similar to alter but doesn't trigger events.
* It's preferable to call proxy for functions that don't
* update the interal data source, like slice or filter.
* @param {String} func the name of the method
* @params {*} any number of params to be given to the func
* @returns the result of the method call
*/
this
.
proxy
=
function
proxy
(
func
)
{
if
(
_data
[
func
])
{
return
_data
[
func
].
apply
(
_data
,
Array
.
prototype
.
slice
.
call
(
arguments
,
1
));
}
else
{
return
false
;
}
};
/**
* Get the observable used for watching store's modifications
* Should be used only for debugging
* @returns {Observable} the Observable
*/
this
.
getStoreObservable
=
function
getStoreObservable
()
{
return
_storeObservable
;
};
/**
* Watch the store's modifications
* @param {String} added/updated/deleted
* @param {Function} func the function to execute
* @param {Object} scope the scope in which to execute the function
* @returns {Handle} the subscribe's handler to use to stop watching
*/
this
.
watch
=
function
watch
(
name
,
func
,
scope
)
{
return
_storeObservable
.
watch
(
name
,
func
,
scope
);
};
/**
* Watch a value's modifications
* @param {String} name the name of the value to watch for
* @param {Function} func the function to execute
* @param {Object} scope the scope in which to execute the function
* @returns handler to pass to unwatchValue
*/
this
.
watchValue
=
function
watchValue
(
name
,
func
,
scope
)
{
return
_valueObservable
.
watch
(
name
,
func
,
scope
);
};
/**
* Unwatch the store modifications
* @param {Handle} handle the handler returned by the watch function
* @returns
*/
this
.
unwatch
=
function
unwatch
(
handle
)
{
return
_storeObservable
.
unwatch
(
handle
);
};
/**
* Unwatch the value's modifications
* @param {Handler} handler the handler returned by the watchValue function
* @private
* @returns true if unwatched
*/
this
.
unwatchValue
=
function
unwatchValue
(
handler
)
{
return
_valueObservable
.
unwatch
(
handler
);
};
/**
* Get the observable used for watching store's modifications
* Should be used only for debugging
* @returns {Observable} the Observable
*/
this
.
getStoreObservable
=
function
getStoreObservable
()
{
return
_storeObservable
;
};
/**
* Get the observable used for watching value's modifications
* Should be used only for debugging
* @private
* @returns {Observable} the Observable
*/
this
.
getValueObservable
=
function
getValueObservable
()
{
return
_valueObservable
;
};
/**
* Watch a value's modifications
* @param {String} name the name of the value to watch for
* @param {Function} func the function to execute
* @param {Object} scope the scope in which to execute the function
* @returns handler to pass to unwatchValue
*/
this
.
watchValue
=
function
watchValue
(
name
,
func
,
scope
)
{
return
_valueObservable
.
watch
(
name
,
func
,
scope
);
};
/**
* Loop through the data
* @param {Function} func the function to execute on each data
* @param {Object} scope the scope in wich to run the callback
*/
this
.
loop
=
function
loop
(
func
,
scope
)
{
Tools
.
loop
(
_data
,
func
,
scope
);
};
/**
* Unwatch the value's modifications
* @param {Handler} handler the handler returned by the watchValue function
* @private
* @returns true if unwatched
*/
this
.
unwatchValue
=
function
unwatchValue
(
handler
)
{
return
_valueObservable
.
unwatch
(
handler
);
};
/**
* Reset all data and get notifications on changes
* @param {Arra/Object} data the new data
* @returns {Boolean}
*/
this
.
reset
=
function
reset
(
data
)
{
if
(
data
instanceof
Object
)
{
var
previousData
=
Tools
.
clone
(
_data
);
_data
=
Tools
.
clone
(
data
)
||
{};
_notifyDiffs
(
previousData
);
return
true
;
}
else
{
return
false
;
}
/**
* Get the observable used for watching value's modifications
* Should be used only for debugging
* @private
* @returns {Observable} the Observable
*/
this
.
getValueObservable
=
function
getValueObservable
()
{
return
_valueObservable
;
};
};
/**
* Loop through the data
* @param {Function} func the function to execute on each data
* @param {Object} scope the scope in wich to run the callback
*/
this
.
loop
=
function
loop
(
func
,
scope
)
{
Tools
.
loop
(
_data
,
func
,
scope
);
};
/**
* Returns a JSON version of the data
* Use dump if you want all the data as a plain js object
* @returns {String} the JSON
*/
this
.
toJSON
=
function
toJSON
()
{
return
JSON
.
stringify
(
_data
);
};
/**
* Reset all data and get notifications on changes
* @param {Arra/Object} data the new data
* @returns {Boolean}
*/
this
.
reset
=
function
reset
(
data
)
{
if
(
data
instanceof
Object
)
{
var
previousData
=
Tools
.
clone
(
_data
);
_data
=
Tools
.
clone
(
data
)
||
{};
_notifyDiffs
(
previousData
);
_storeObservable
.
notify
(
"
resetted
"
,
_data
,
previousData
);
return
true
;
}
else
{
return
false
;
}
/**
* Returns the store's data
* @returns {Object} the data
*/
this
.
dump
=
function
dump
()
{
return
_data
;
};
};
};
/**
* Compute a new property from other properties.
* The computed property will look exactly similar to any none
* computed property, it can be watched upon.
* @param {String} name the name of the computed property
* @param {Array} computeFrom a list of properties to compute from
* @param {Function} callback the callback to compute the property
* @param {Object} scope the scope in which to execute the callback
* @returns {Boolean} false if wrong params given to the function
*/
this
.
compute
=
function
compute
(
name
,
computeFrom
,
callback
,
scope
)
{
var
args
=
[];
if
(
typeof
name
==
"
string
"
&&
typeof
computeFrom
==
"
object
"
&&
typeof
callback
==
"
function
"
&&
!
this
.
isCompute
(
name
))
{
_computed
[
name
]
=
[];
Tools
.
loop
(
computeFrom
,
function
(
property
)
{
_computed
[
name
].
push
(
this
.
watchValue
(
property
,
function
()
{
this
.
set
(
name
,
callback
.
call
(
scope
));
},
this
));
},
this
);
this
.
set
(
name
,
callback
.
call
(
scope
));
return
true
;
}
else
{
return
false
;
}
};
/**
* Remove a computed property
* @param {String} name the name of the computed to remove
* @returns {Boolean} true if the property is removed
*/
this
.
removeCompute
=
function
removeCompute
(
name
)
{
if
(
this
.
isCompute
(
name
))
{
Tools
.
loop
(
_computed
[
name
],
function
(
handle
)
{
this
.
unwatchValue
(
handle
);
},
this
);
this
.
del
(
name
);
return
true
;
}
else
{
return
false
;
}
};
/**
* Tells if a property is a computed property
* @param {String} name the name of the property to test
* @returns {Boolean} true if it's a computed property
*/
this
.
isCompute
=
function
isCompute
(
name
)
{
return
!!
_computed
[
name
];
};
/**
* Returns a JSON version of the data
* Use dump if you want all the data as a plain js object
* @returns {String} the JSON
*/
this
.
toJSON
=
function
toJSON
()
{
return
JSON
.
stringify
(
_data
);
};
/**
* Returns the store's data
* @returns {Object} the data
*/
this
.
dump
=
function
dump
()
{
return
_data
;
};
};
});
/**
* Emily
* Emily
.js - http://flams.github.com/emily/
* Copyright(c) 2012-2013 Olivier Scherrer <pode.fr@gmail.com>
* MIT Licensed
*/
define
(
'
Transport
'
,[],
/**
* @class
...
...
@@ -1275,96 +1450,339 @@ define('Transport',[],
*/
function
Transport
()
{
/**
* Create a Transport
* @param {Emily Store} [optionanl] $reqHandlers an object containing the request handlers
* @returns
*/
return
function
TransportConstructor
(
$reqHandlers
)
{
/**
* The request handlers
* @private
*/
var
_reqHandlers
=
null
;
/**
* Create a Transport
* @param {Emily Store} [optionanl] $reqHandlers an object containing the request handlers
* @returns
*/
return
function
TransportConstructor
(
$reqHandlers
)
{
/**
* Set the requests handlers object
* @param {Emily Store} reqHandlers an object containing the requests handlers
* @returns
*/
this
.
setReqHandlers
=
function
setReqHandlers
(
reqHandlers
)
{
if
(
reqHandlers
instanceof
Object
)
{
_reqHandlers
=
reqHandlers
;
return
true
;
}
else
{
return
false
;
}
};
/**
* The request handlers
* @private
*/
var
_reqHandlers
=
null
;
/**
* Get the requests handlers
* @returns{ Emily Store} reqHandlers the object containing the requests handlers
*/
this
.
getReqHandlers
=
function
getReqHandlers
()
{
return
_reqHandlers
;
};
/**
* Set the requests handlers object
* @param {Emily Store} reqHandlers an object containing the requests handlers
* @returns
*/
this
.
setReqHandlers
=
function
setReqHandlers
(
reqHandlers
)
{
if
(
reqHandlers
instanceof
Object
)
{
_reqHandlers
=
reqHandlers
;
return
true
;
}
else
{
return
false
;
}
};
/**
* Issue a request to a request handler
* @param {String} reqHandler the name of the request handler to issue the request to
* @param {Object} data the data, or payload, to send to the request handler
* @param {Function} callback the function to execute with the result
* @param {Object} scope the scope in which to execute the callback
* @returns
*/
this
.
request
=
function
request
(
reqHandler
,
data
,
callback
,
scope
)
{
if
(
_reqHandlers
.
has
(
reqHandler
)
&&
typeof
data
!=
"
undefined
"
)
{
/**
* Get the requests handlers
* @returns{ Emily Store} reqHandlers the object containing the requests handlers
*/
this
.
getReqHandlers
=
function
getReqHandlers
()
{
return
_reqHandlers
;
};
_reqHandlers
.
get
(
reqHandler
)(
data
,
function
()
{
callback
&&
callback
.
apply
(
scope
,
arguments
);
});
return
true
;
}
else
{
return
false
;
}
};
/**
* Issue a request to a request handler
* @param {String} reqHandler the name of the request handler to issue the request to
* @param {Object} data the data, or payload, to send to the request handler
* @param {Function} callback the function to execute with the result
* @param {Object} scope the scope in which to execute the callback
* @returns
*/
this
.
request
=
function
request
(
reqHandler
,
data
,
callback
,
scope
)
{
if
(
_reqHandlers
.
has
(
reqHandler
)
&&
typeof
data
!=
"
undefined
"
)
{
_reqHandlers
.
get
(
reqHandler
)(
data
,
function
()
{
if
(
callback
)
{
callback
.
apply
(
scope
,
arguments
);
}
});
return
true
;
}
else
{
return
false
;
}
};
/**
* Issue a request to a reqHandler but keep listening for the response as it can be sent in several chunks
* or remain open as long as the abort funciton is not called
* @param {String} reqHandler the name of the request handler to issue the request to
* @param {Object} data the data, or payload, to send to the request handler
* @param {Function} callback the function to execute with the result
* @param {Object} scope the scope in which to execute the callback
* @returns {Function} the abort function to call to stop listening
*/
this
.
listen
=
function
listen
(
reqHandler
,
data
,
callback
,
scope
)
{
if
(
_reqHandlers
.
has
(
reqHandler
)
&&
typeof
data
!=
"
undefined
"
&&
typeof
callback
==
"
function
"
)
{
var
func
=
function
()
{
callback
.
apply
(
scope
,
arguments
);
},
abort
;
abort
=
_reqHandlers
.
get
(
reqHandler
)(
data
,
func
,
func
);
return
function
()
{
if
(
typeof
abort
==
"
function
"
)
{
abort
();
}
else
if
(
typeof
abort
==
"
object
"
&&
typeof
abort
.
func
==
"
function
"
)
{
abort
.
func
.
call
(
abort
.
scope
);
}
};
}
else
{
return
false
;
}
};
/**
* Issue a request to a reqHandler but keep listening for the response as it can be sent in several chunks
* or remain open as long as the abort funciton is not called
* @param {String} reqHandler the name of the request handler to issue the request to
* @param {Object} data the data, or payload, to send to the request handler
* @param {Function} callback the function to execute with the result
* @param {Object} scope the scope in which to execute the callback
* @returns {Function} the abort function to call to stop listening
*/
this
.
listen
=
function
listen
(
reqHandler
,
data
,
callback
,
scope
)
{
if
(
_reqHandlers
.
has
(
reqHandler
)
&&
typeof
data
!=
"
undefined
"
&&
typeof
callback
==
"
function
"
)
{
var
func
=
function
()
{
callback
.
apply
(
scope
,
arguments
);
},
abort
;
abort
=
_reqHandlers
.
get
(
reqHandler
)(
data
,
func
,
func
);
return
function
()
{
if
(
typeof
abort
==
"
function
"
)
{
abort
();
}
else
if
(
typeof
abort
==
"
object
"
&&
typeof
abort
.
func
==
"
function
"
)
{
abort
.
func
.
call
(
abort
.
scope
);
}
};
}
else
{
return
false
;
}
};
this
.
setReqHandlers
(
$reqHandlers
);
this
.
setReqHandlers
(
$reqHandlers
);
};
};
});
/**
* Emily.js - http://flams.github.com/emily/
* Copyright(c) 2012-2013 Olivier Scherrer <pode.fr@gmail.com>
* MIT Licensed
*/
define
(
'
Router
'
,[
"
Observable
"
,
"
Store
"
,
"
Tools
"
],
/**
* @class
* Routing allows for navigating in an application by defining routes.
*/
function
Router
(
Observable
,
Store
,
Tools
)
{
return
function
RouterConstructor
()
{
/**
* The routes observable (the applications use it)
* @private
*/
var
_routes
=
new
Observable
(),
/**
* The events observable (used by Routing)
* @private
*/
_events
=
new
Observable
(),
/**
* The routing history
* @private
*/
_history
=
new
Store
([]),
/**
* For navigating through the history, remembers the current position
* @private
*/
_currentPos
=
-
1
,
/**
* The depth of the history
* @private
*/
_maxHistory
=
10
;
/**
* Only for debugging
* @private
*/
this
.
getRoutesObservable
=
function
getRoutesObservable
()
{
return
_routes
;
};
/**
* Only for debugging
* @private
*/
this
.
getEventsObservable
=
function
getEventsObservable
()
{
return
_events
;
};
/**
* Set the maximum length of history
* As the user navigates through the application, the
* routeur keeps track of the history. Set the depth of the history
* depending on your need and the amount of memory that you can allocate it
* @param {Number} maxHistory the depth of history
* @returns {Boolean} true if maxHistory is equal or greater than 0
*/
this
.
setMaxHistory
=
function
setMaxHistory
(
maxHistory
)
{
if
(
maxHistory
>=
0
)
{
_maxHistory
=
maxHistory
;
return
true
;
}
else
{
return
false
;
}
};
/**
* Get the current max history setting
* @returns {Number} the depth of history
*/
this
.
getMaxHistory
=
function
getMaxHistory
()
{
return
_maxHistory
;
};
/**
* Set a new route
* @param {String} route the name of the route
* @param {Function} func the function to be execute when navigating to the route
* @param {Object} scope the scope in which to execute the function
* @returns a handle to remove the route
*/
this
.
set
=
function
set
()
{
return
_routes
.
watch
.
apply
(
_routes
,
arguments
);
};
/**
* Remove a route
* @param {Object} handle the handle provided by the set method
* @returns true if successfully removed
*/
this
.
unset
=
function
unset
(
handle
)
{
return
_routes
.
unwatch
(
handle
);
};
/**
* Navigate to a route
* @param {String} route the route to navigate to
* @param {*} *params
* @returns
*/
this
.
navigate
=
function
get
(
route
,
params
)
{
if
(
this
.
load
.
apply
(
this
,
arguments
))
{
// Before adding a new route to the history, we must clear the forward history
_history
.
proxy
(
"
splice
"
,
_currentPos
+
1
,
_history
.
count
());
_history
.
proxy
(
"
push
"
,
Tools
.
toArray
(
arguments
));
this
.
ensureMaxHistory
(
_history
);
_currentPos
=
_history
.
count
()
-
1
;
return
true
;
}
else
{
return
false
;
}
};
/**
* Ensure that history doesn't grow bigger than the max history setting
* @param {Store} history the history store
* @private
*/
this
.
ensureMaxHistory
=
function
ensureMaxHistory
(
history
)
{
var
count
=
history
.
count
(),
max
=
this
.
getMaxHistory
(),
excess
=
count
-
max
;
if
(
excess
>
0
)
{
history
.
proxy
(
"
splice
"
,
0
,
excess
);
}
};
/**
* Actually loads the route
* @private
*/
this
.
load
=
function
load
()
{
var
copy
=
Tools
.
toArray
(
arguments
);
if
(
_routes
.
notify
.
apply
(
_routes
,
copy
))
{
copy
.
unshift
(
"
route
"
);
_events
.
notify
.
apply
(
_events
,
copy
);
return
true
;
}
else
{
return
false
;
}
};
/**
* Watch for route changes
* @param {Function} func the func to execute when the route changes
* @param {Object} scope the scope in which to execute the function
* @returns {Object} the handle to unwatch for route changes
*/
this
.
watch
=
function
watch
(
func
,
scope
)
{
return
_events
.
watch
(
"
route
"
,
func
,
scope
);
};
/**
* Unwatch routes changes
* @param {Object} handle the handle was returned by the watch function
* @returns true if unwatch
*/
this
.
unwatch
=
function
unwatch
(
handle
)
{
return
_events
.
unwatch
(
handle
);
};
/**
* Get the history store, for debugging only
* @private
*/
this
.
getHistoryStore
=
function
getHistoryStore
()
{
return
_history
;
};
/**
* Get the current length of history
* @returns {Number} the length of history
*/
this
.
getHistoryCount
=
function
getHistoryCount
()
{
return
_history
.
count
();
};
/**
* Flush the entire history
*/
this
.
clearHistory
=
function
clearHistory
()
{
_history
.
reset
([]);
};
/**
* Go back and forth in the history
* @param {Number} nb the amount of history to rewind/forward
* @returns true if history exists
*/
this
.
go
=
function
go
(
nb
)
{
var
history
=
_history
.
get
(
_currentPos
+
nb
);
if
(
history
)
{
_currentPos
+=
nb
;
this
.
load
.
apply
(
this
,
history
);
return
true
;
}
else
{
return
false
;
}
};
/**
* Go back in the history, short for go(-1)
* @returns
*/
this
.
back
=
function
back
()
{
return
this
.
go
(
-
1
);
};
/**
* Go forward in the history, short for go(1)
* @returns
*/
this
.
forward
=
function
forward
()
{
return
this
.
go
(
1
);
};
};
});
\ No newline at end of file
examples/olives/bower_components/olives/build/Olives.js
View file @
9df98e41
...
...
@@ -12,6 +12,8 @@
define
(
'
DomUtils
'
,[
"
Tools
"
],
function
(
Tools
)
{
return
{
/**
* Returns a NodeList including the given dom node,
...
...
@@ -127,375 +129,406 @@ define('Bind.plugin',["Store", "Observable", "Tools", "DomUtils"],
*/
function
BindPlugin
(
Store
,
Observable
,
Tools
,
DomUtils
)
{
return
function
BindPluginConstructor
(
$model
,
$bindings
)
{
/**
* The model to watch
* @private
*/
var
_model
=
null
,
/**
* The list of custom bindings
* @private
*/
_bindings
=
{},
/**
* The list of itemRenderers
* each foreach has its itemRenderer
* @private
*/
_itemRenderers
=
{};
/**
* The observers handlers
* for debugging only
* @private
*/
this
.
observers
=
{};
/**
* Define the model to watch for
* @param {Store} model the model to watch for changes
* @returns {Boolean} true if the model was set
*/
this
.
setModel
=
function
setModel
(
model
)
{
if
(
model
instanceof
Store
)
{
// Set the model
_model
=
model
;
return
true
;
}
else
{
return
false
;
}
};
/**
* Get the store that is watched for
* for debugging only
* @private
* @returns the Store
*/
this
.
getModel
=
function
getModel
()
{
return
_model
;
};
/**
* The item renderer defines a dom node that can be duplicated
* It is made available for debugging purpose, don't use it
* @private
*/
this
.
ItemRenderer
=
function
ItemRenderer
(
$plugins
,
$rootNode
)
{
/**
* The node that will be cloned
* @private
*/
var
_node
=
null
,
/**
* The object that contains plugins.name and plugins.apply
* @private
*/
_plugins
=
null
,
/**
* The _rootNode where to append the created items
* @private
*/
_rootNode
=
null
,
/**
* The lower boundary
* @private
*/
_start
=
null
,
/**
* The number of item to display
* @private
*/
_nb
=
null
;
/**
* Set the duplicated node
* @private
*/
this
.
setRenderer
=
function
setRenderer
(
node
)
{
_node
=
node
;
return
true
;
};
/**
* Returns the node that is going to be used for rendering
* @private
* @returns the node that is duplicated
*/
this
.
getRenderer
=
function
getRenderer
()
{
return
_node
;
};
/**
* Sets the rootNode and gets the node to copy
* @private
* @param {HTMLElement|SVGElement} rootNode
* @returns
*/
this
.
setRootNode
=
function
setRootNode
(
rootNode
)
{
var
renderer
;
if
(
DomUtils
.
isAcceptedType
(
rootNode
))
{
_rootNode
=
rootNode
;
renderer
=
_rootNode
.
querySelector
(
"
*
"
);
this
.
setRenderer
(
renderer
);
renderer
&&
_rootNode
.
removeChild
(
renderer
);
return
true
;
}
else
{
return
false
;
}
};
/**
* Gets the rootNode
* @private
* @returns _rootNode
*/
this
.
getRootNode
=
function
getRootNode
()
{
return
_rootNode
;
};
/**
* Set the plugins objet that contains the name and the apply function
* @private
* @param plugins
* @returns true
*/
this
.
setPlugins
=
function
setPlugins
(
plugins
)
{
_plugins
=
plugins
;
return
true
;
};
/**
* Get the plugins object
* @private
* @returns the plugins object
*/
this
.
getPlugins
=
function
getPlugins
()
{
return
_plugins
;
};
/**
* The nodes created from the items are stored here
* @private
*/
this
.
items
=
new
Store
([]);
/**
* Set the start limit
* @private
* @param {Number} start the value to start rendering the items from
* @returns the value
*/
this
.
setStart
=
function
setStart
(
start
)
{
return
_start
=
parseInt
(
start
,
10
);
};
/**
* Get the start value
* @private
* @returns the start value
*/
this
.
getStart
=
function
getStart
()
{
return
_start
;
};
/**
* Set the number of item to display
* @private
* @param {Number/String} nb the number of item to display or "*" for all
* @returns the value
*/
this
.
setNb
=
function
setNb
(
nb
)
{
return
_nb
=
nb
==
"
*
"
?
nb
:
parseInt
(
nb
,
10
);
};
/**
* Get the number of item to display
* @private
* @returns the value
*/
this
.
getNb
=
function
getNb
()
{
return
_nb
;
};
/**
* Adds a new item and adds it in the items list
* @private
* @param {Number} id the id of the item
* @returns
*/
this
.
addItem
=
function
addItem
(
id
)
{
var
node
,
next
;
if
(
typeof
id
==
"
number
"
&&
!
this
.
items
.
get
(
id
))
{
node
=
this
.
create
(
id
);
if
(
node
)
{
// IE (until 9) apparently fails to appendChild when insertBefore's second argument is null, hence this.
next
=
this
.
getNextItem
(
id
);
next
?
_rootNode
.
insertBefore
(
node
,
next
)
:
_rootNode
.
appendChild
(
node
);
return
true
;
}
else
{
return
false
;
}
}
else
{
return
false
;
}
};
/**
* Get the next item in the item store given an id.
* @private
* @param {Number} id the id to start from
* @returns
*/
this
.
getNextItem
=
function
getNextItem
(
id
)
{
return
this
.
items
.
alter
(
"
slice
"
,
id
+
1
).
filter
(
function
(
value
)
{
if
(
DomUtils
.
isAcceptedType
(
value
))
{
return
true
;
}
})[
0
];
};
/**
* Remove an item from the dom and the items list
* @private
* @param {Number} id the id of the item to remove
* @returns
*/
this
.
removeItem
=
function
removeItem
(
id
)
{
var
item
=
this
.
items
.
get
(
id
);
if
(
item
)
{
_rootNode
.
removeChild
(
item
);
this
.
items
.
set
(
id
);
return
true
;
}
else
{
return
false
;
}
};
/**
* create a new node. Actually makes a clone of the initial one
* and adds pluginname_id to each node, then calls plugins.apply to apply all plugins
* @private
* @param id
* @param pluginName
* @returns the associated node
*/
this
.
create
=
function
create
(
id
)
{
if
(
_model
.
has
(
id
))
{
var
newNode
=
_node
.
cloneNode
(
true
),
nodes
=
DomUtils
.
getNodes
(
newNode
);
Tools
.
toArray
(
nodes
).
forEach
(
function
(
child
)
{
child
.
setAttribute
(
"
data-
"
+
_plugins
.
name
+
"
_id
"
,
id
);
});
this
.
items
.
set
(
id
,
newNode
);
_plugins
.
apply
(
newNode
);
return
newNode
;
}
};
/**
* Renders the dom tree, adds nodes that are in the boundaries
* and removes the others
* @private
* @returns true boundaries are set
*/
this
.
render
=
function
render
()
{
// If the number of items to render is all (*)
// Then get the number of items
var
_tmpNb
=
_nb
==
"
*
"
?
_model
.
getNbItems
()
:
_nb
;
// This will store the items to remove
var
marked
=
[];
// Render only if boundaries have been set
if
(
_nb
!==
null
&&
_start
!==
null
)
{
// Loop through the existing items
this
.
items
.
loop
(
function
(
value
,
idx
)
{
// If an item is out of the boundary
if
(
idx
<
_start
||
idx
>=
(
_start
+
_tmpNb
)
||
!
_model
.
has
(
idx
))
{
// Mark it
marked
.
push
(
idx
);
}
},
this
);
// Remove the marked item from the highest id to the lowest
// Doing this will avoid the id change during removal
// (removing id 2 will make id 3 becoming 2)
marked
.
sort
(
Tools
.
compareNumbers
).
reverse
().
forEach
(
this
.
removeItem
,
this
);
// Now that we have removed the old nodes
// Add the missing one
for
(
var
i
=
_start
,
l
=
_tmpNb
+
_start
;
i
<
l
;
i
++
)
{
this
.
addItem
(
i
);
}
return
true
;
}
else
{
return
false
;
}
};
this
.
setPlugins
(
$plugins
);
this
.
setRootNode
(
$rootNode
);
};
/**
* Save an itemRenderer according to its id
* @private
* @param {String} id the id of the itemRenderer
* @param {ItemRenderer} itemRenderer an itemRenderer object
*/
this
.
setItemRenderer
=
function
setItemRenderer
(
id
,
itemRenderer
)
{
id
=
id
||
"
default
"
;
_itemRenderers
[
id
]
=
itemRenderer
;
};
/**
* Get an itemRenderer
* @private
* @param {String} id the name of the itemRenderer
* @returns the itemRenderer
*/
this
.
getItemRenderer
=
function
getItemRenderer
(
id
)
{
return
_itemRenderers
[
id
];
};
/**
* Expands the inner dom nodes of a given dom node, filling it with model's values
* @param {HTMLElement|SVGElement} node the dom node to apply foreach to
*/
this
.
foreach
=
function
foreach
(
node
,
idItemRenderer
,
start
,
nb
)
{
var
itemRenderer
=
new
this
.
ItemRenderer
(
this
.
plugins
,
node
);
itemRenderer
.
setStart
(
start
||
0
);
itemRenderer
.
setNb
(
nb
||
"
*
"
);
itemRenderer
.
render
();
// Add the newly created item
return
function
BindPluginConstructor
(
$model
,
$bindings
)
{
/**
* The model to watch
* @private
*/
var
_model
=
null
,
/**
* The list of custom bindings
* @private
*/
_bindings
=
{},
/**
* The list of itemRenderers
* each foreach has its itemRenderer
* @private
*/
_itemRenderers
=
{},
/**
* The observers handlers
* @private
*/
_observers
=
{};
/**
* Exposed for debugging purpose
* @private
*/
this
.
observers
=
_observers
;
function
_removeObserversForId
(
id
)
{
if
(
_observers
[
id
])
{
_observers
[
id
].
forEach
(
function
(
handler
)
{
_model
.
unwatchValue
(
handler
);
});
delete
_observers
[
id
];
}
}
/**
* Define the model to watch for
* @param {Store} model the model to watch for changes
* @returns {Boolean} true if the model was set
*/
this
.
setModel
=
function
setModel
(
model
)
{
if
(
model
instanceof
Store
)
{
// Set the model
_model
=
model
;
return
true
;
}
else
{
return
false
;
}
};
/**
* Get the store that is watched for
* for debugging only
* @private
* @returns the Store
*/
this
.
getModel
=
function
getModel
()
{
return
_model
;
};
/**
* The item renderer defines a dom node that can be duplicated
* It is made available for debugging purpose, don't use it
* @private
*/
this
.
ItemRenderer
=
function
ItemRenderer
(
$plugins
,
$rootNode
)
{
/**
* The node that will be cloned
* @private
*/
var
_node
=
null
,
/**
* The object that contains plugins.name and plugins.apply
* @private
*/
_plugins
=
null
,
/**
* The _rootNode where to append the created items
* @private
*/
_rootNode
=
null
,
/**
* The lower boundary
* @private
*/
_start
=
null
,
/**
* The number of item to display
* @private
*/
_nb
=
null
;
/**
* Set the duplicated node
* @private
*/
this
.
setRenderer
=
function
setRenderer
(
node
)
{
_node
=
node
;
return
true
;
};
/**
* Returns the node that is going to be used for rendering
* @private
* @returns the node that is duplicated
*/
this
.
getRenderer
=
function
getRenderer
()
{
return
_node
;
};
/**
* Sets the rootNode and gets the node to copy
* @private
* @param {HTMLElement|SVGElement} rootNode
* @returns
*/
this
.
setRootNode
=
function
setRootNode
(
rootNode
)
{
var
renderer
;
if
(
DomUtils
.
isAcceptedType
(
rootNode
))
{
_rootNode
=
rootNode
;
renderer
=
_rootNode
.
querySelector
(
"
*
"
);
this
.
setRenderer
(
renderer
);
if
(
renderer
)
{
_rootNode
.
removeChild
(
renderer
);
}
return
true
;
}
else
{
return
false
;
}
};
/**
* Gets the rootNode
* @private
* @returns _rootNode
*/
this
.
getRootNode
=
function
getRootNode
()
{
return
_rootNode
;
};
/**
* Set the plugins objet that contains the name and the apply function
* @private
* @param plugins
* @returns true
*/
this
.
setPlugins
=
function
setPlugins
(
plugins
)
{
_plugins
=
plugins
;
return
true
;
};
/**
* Get the plugins object
* @private
* @returns the plugins object
*/
this
.
getPlugins
=
function
getPlugins
()
{
return
_plugins
;
};
/**
* The nodes created from the items are stored here
* @private
*/
this
.
items
=
{};
/**
* Set the start limit
* @private
* @param {Number} start the value to start rendering the items from
* @returns the value
*/
this
.
setStart
=
function
setStart
(
start
)
{
_start
=
parseInt
(
start
,
10
);
return
_start
;
};
/**
* Get the start value
* @private
* @returns the start value
*/
this
.
getStart
=
function
getStart
()
{
return
_start
;
};
/**
* Set the number of item to display
* @private
* @param {Number/String} nb the number of item to display or "*" for all
* @returns the value
*/
this
.
setNb
=
function
setNb
(
nb
)
{
_nb
=
nb
==
"
*
"
?
nb
:
parseInt
(
nb
,
10
);
return
_nb
;
};
/**
* Get the number of item to display
* @private
* @returns the value
*/
this
.
getNb
=
function
getNb
()
{
return
_nb
;
};
/**
* Adds a new item and adds it in the items list
* @private
* @param {Number} id the id of the item
* @returns
*/
this
.
addItem
=
function
addItem
(
id
)
{
var
node
,
next
;
if
(
typeof
id
==
"
number
"
&&
!
this
.
items
[
id
])
{
next
=
this
.
getNextItem
(
id
);
node
=
this
.
create
(
id
);
if
(
node
)
{
// IE (until 9) apparently fails to appendChild when insertBefore's second argument is null, hence this.
if
(
next
)
{
_rootNode
.
insertBefore
(
node
,
next
);
}
else
{
_rootNode
.
appendChild
(
node
);
}
return
true
;
}
else
{
return
false
;
}
}
else
{
return
false
;
}
};
/**
* Get the next item in the item store given an id.
* @private
* @param {Number} id the id to start from
* @returns
*/
this
.
getNextItem
=
function
getNextItem
(
id
)
{
var
keys
=
Object
.
keys
(
this
.
items
).
map
(
function
(
string
)
{
return
Number
(
string
);
}),
closest
=
Tools
.
closestGreater
(
id
,
keys
),
closestId
=
keys
[
closest
];
// Only return if different
if
(
closestId
!=
id
)
{
return
this
.
items
[
closestId
];
}
else
{
return
;
}
};
/**
* Remove an item from the dom and the items list
* @private
* @param {Number} id the id of the item to remove
* @returns
*/
this
.
removeItem
=
function
removeItem
(
id
)
{
var
item
=
this
.
items
[
id
];
if
(
item
)
{
_rootNode
.
removeChild
(
item
);
delete
this
.
items
[
id
];
_removeObserversForId
(
id
);
return
true
;
}
else
{
return
false
;
}
};
/**
* create a new node. Actually makes a clone of the initial one
* and adds pluginname_id to each node, then calls plugins.apply to apply all plugins
* @private
* @param id
* @param pluginName
* @returns the associated node
*/
this
.
create
=
function
create
(
id
)
{
if
(
_model
.
has
(
id
))
{
var
newNode
=
_node
.
cloneNode
(
true
),
nodes
=
DomUtils
.
getNodes
(
newNode
);
Tools
.
toArray
(
nodes
).
forEach
(
function
(
child
)
{
child
.
setAttribute
(
"
data-
"
+
_plugins
.
name
+
"
_id
"
,
id
);
});
this
.
items
[
id
]
=
newNode
;
_plugins
.
apply
(
newNode
);
return
newNode
;
}
};
/**
* Renders the dom tree, adds nodes that are in the boundaries
* and removes the others
* @private
* @returns true boundaries are set
*/
this
.
render
=
function
render
()
{
// If the number of items to render is all (*)
// Then get the number of items
var
_tmpNb
=
_nb
==
"
*
"
?
_model
.
getNbItems
()
:
_nb
;
// This will store the items to remove
var
marked
=
[];
// Render only if boundaries have been set
if
(
_nb
!==
null
&&
_start
!==
null
)
{
// Loop through the existing items
Tools
.
loop
(
this
.
items
,
function
(
value
,
idx
)
{
// If an item is out of the boundary
idx
=
Number
(
idx
);
if
(
idx
<
_start
||
idx
>=
(
_start
+
_tmpNb
)
||
!
_model
.
has
(
idx
))
{
// Mark it
marked
.
push
(
idx
);
}
},
this
);
// Remove the marked item from the highest id to the lowest
// Doing this will avoid the id change during removal
// (removing id 2 will make id 3 becoming 2)
marked
.
sort
(
Tools
.
compareNumbers
).
reverse
().
forEach
(
this
.
removeItem
,
this
);
// Now that we have removed the old nodes
// Add the missing one
for
(
var
i
=
_start
,
l
=
_tmpNb
+
_start
;
i
<
l
;
i
++
)
{
this
.
addItem
(
i
);
}
return
true
;
}
else
{
return
false
;
}
};
this
.
setPlugins
(
$plugins
);
this
.
setRootNode
(
$rootNode
);
};
/**
* Save an itemRenderer according to its id
* @private
* @param {String} id the id of the itemRenderer
* @param {ItemRenderer} itemRenderer an itemRenderer object
*/
this
.
setItemRenderer
=
function
setItemRenderer
(
id
,
itemRenderer
)
{
id
=
id
||
"
default
"
;
_itemRenderers
[
id
]
=
itemRenderer
;
};
/**
* Get an itemRenderer
* @private
* @param {String} id the name of the itemRenderer
* @returns the itemRenderer
*/
this
.
getItemRenderer
=
function
getItemRenderer
(
id
)
{
return
_itemRenderers
[
id
];
};
/**
* Expands the inner dom nodes of a given dom node, filling it with model's values
* @param {HTMLElement|SVGElement} node the dom node to apply foreach to
*/
this
.
foreach
=
function
foreach
(
node
,
idItemRenderer
,
start
,
nb
)
{
var
itemRenderer
=
new
this
.
ItemRenderer
(
this
.
plugins
,
node
);
itemRenderer
.
setStart
(
start
||
0
);
itemRenderer
.
setNb
(
nb
||
"
*
"
);
itemRenderer
.
render
();
// Add the newly created item
_model
.
watch
(
"
added
"
,
itemRenderer
.
render
,
itemRenderer
);
// If an item is deleted
// If an item is deleted
_model
.
watch
(
"
deleted
"
,
function
(
idx
)
{
itemRenderer
.
render
();
itemRenderer
.
render
();
// Also remove all observers
this
.
observers
[
idx
]
&&
this
.
observers
[
idx
].
forEach
(
function
(
handler
)
{
_model
.
unwatchValue
(
handler
);
},
this
);
delete
this
.
observers
[
idx
];
_removeObserversForId
(
idx
);
},
this
);
this
.
setItemRenderer
(
idItemRenderer
,
itemRenderer
);
...
...
@@ -508,13 +541,13 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
* @returns true if the foreach exists
*/
this
.
updateStart
=
function
updateStart
(
id
,
start
)
{
var
itemRenderer
=
this
.
getItemRenderer
(
id
);
if
(
itemRenderer
)
{
itemRenderer
.
setStart
(
start
);
return
true
;
}
else
{
return
false
;
}
var
itemRenderer
=
this
.
getItemRenderer
(
id
);
if
(
itemRenderer
)
{
itemRenderer
.
setStart
(
start
);
return
true
;
}
else
{
return
false
;
}
};
/**
...
...
@@ -524,13 +557,13 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
* @returns true if the foreach exists
*/
this
.
updateNb
=
function
updateNb
(
id
,
nb
)
{
var
itemRenderer
=
this
.
getItemRenderer
(
id
);
if
(
itemRenderer
)
{
itemRenderer
.
setNb
(
nb
);
return
true
;
}
else
{
return
false
;
}
var
itemRenderer
=
this
.
getItemRenderer
(
id
);
if
(
itemRenderer
)
{
itemRenderer
.
setNb
(
nb
);
return
true
;
}
else
{
return
false
;
}
};
/**
...
...
@@ -539,202 +572,202 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
* @returns true if the foreach exists
*/
this
.
refresh
=
function
refresh
(
id
)
{
var
itemRenderer
=
this
.
getItemRenderer
(
id
);
if
(
itemRenderer
)
{
itemRenderer
.
render
();
return
true
;
}
else
{
return
false
;
}
var
itemRenderer
=
this
.
getItemRenderer
(
id
);
if
(
itemRenderer
)
{
itemRenderer
.
render
();
return
true
;
}
else
{
return
false
;
}
};
/**
* Both ways binding between a dom node attributes and the model
* @param {HTMLElement|SVGElement} node the dom node to apply the plugin to
* @param {String} name the name of the property to look for in the model's value
* @returns
*/
this
.
bind
=
function
bind
(
node
,
property
,
name
)
{
// Name can be unset if the value of a row is plain text
name
=
name
||
""
;
// In case of an array-like model the id is the index of the model's item to look for.
// The _id is added by the foreach function
var
id
=
node
.
getAttribute
(
"
data-
"
+
this
.
plugins
.
name
+
"
_id
"
),
// Else, it is the first element of the following
split
=
name
.
split
(
"
.
"
),
// So the index of the model is either id or the first element of split
modelIdx
=
id
||
split
.
shift
(),
// And the name of the property to look for in the value is
prop
=
id
?
name
:
split
.
join
(
"
.
"
),
// Get the model's value
get
=
Tools
.
getNestedProperty
(
_model
.
get
(
modelIdx
),
prop
),
// When calling bind like bind:newBinding,param1, param2... we need to get them
extraParam
=
Tools
.
toArray
(
arguments
).
slice
(
3
);
// 0 and false are acceptable falsy values
if
(
get
||
get
===
0
||
get
===
false
)
{
// If the binding hasn't been overriden
if
(
!
this
.
execBinding
.
apply
(
this
,
[
node
,
property
,
get
]
// Extra params are passed to the new binding too
.
concat
(
extraParam
)))
{
// Execute the default one which is a simple assignation
//node[property] = get;
DomUtils
.
setAttribute
(
node
,
property
,
get
);
}
}
// Only watch for changes (double way data binding) if the binding
// has not been redefined
if
(
!
this
.
hasBinding
(
property
))
{
node
.
addEventListener
(
"
change
"
,
function
(
event
)
{
if
(
_model
.
has
(
modelIdx
))
{
if
(
prop
)
{
_model
.
update
(
modelIdx
,
name
,
node
[
property
]);
}
else
{
_model
.
set
(
modelIdx
,
node
[
property
]);
}
}
},
true
);
}
// Watch for changes
this
.
observers
[
modelIdx
]
=
this
.
observers
[
modelIdx
]
||
[];
this
.
observers
[
modelIdx
].
push
(
_model
.
watchValue
(
modelIdx
,
function
(
value
)
{
if
(
!
this
.
execBinding
.
apply
(
this
,
[
node
,
property
,
Tools
.
getNestedProperty
(
value
,
prop
)]
// passing extra params too
.
concat
(
extraParam
)))
{
//node[property] = Tools.getNestedProperty(value, prop);
DomUtils
.
setAttribute
(
node
,
property
,
Tools
.
getNestedProperty
(
value
,
prop
));
}
},
this
));
};
/**
* Set the node's value into the model, the name is the model's property
* @private
* @param {HTMLElement|SVGElement} node
* @returns true if the property is added
*/
this
.
set
=
function
set
(
node
)
{
if
(
DomUtils
.
isAcceptedType
(
node
)
&&
node
.
name
)
{
_model
.
set
(
node
.
name
,
node
.
value
);
return
true
;
}
else
{
return
false
;
}
};
this
.
getItemIndex
=
function
getElementId
(
dom
)
{
var
dataset
=
DomUtils
.
getDataset
(
dom
);
if
(
dataset
&&
typeof
dataset
[
this
.
plugins
.
name
+
"
_id
"
]
!=
"
undefined
"
)
{
return
+
dataset
[
this
.
plugins
.
name
+
"
_id
"
];
}
else
{
return
false
;
}
};
/**
* Prevents the submit and set the model with all form's inputs
* @param {HTMLFormElement} for
m
* @returns true if valid form
*/
this
.
form
=
function
form
(
form
)
{
if
(
form
&&
form
.
nodeName
==
"
FORM
"
)
{
var
that
=
this
;
form
.
addEventListener
(
"
submit
"
,
function
(
event
)
{
Tools
.
toArray
(
form
.
querySelectorAll
(
"
[name]
"
)).
forEach
(
that
.
set
,
that
);
event
.
preventDefault
();
},
true
);
return
true
;
}
else
{
return
false
;
}
};
/**
* Add a new way to handle a binding
* @param {String} name of the binding
* @param {Function} binding the function to handle the binding
* @returns
*/
this
.
addBinding
=
function
addBinding
(
name
,
binding
)
{
if
(
name
&&
typeof
name
==
"
string
"
&&
typeof
binding
==
"
function
"
)
{
_bindings
[
name
]
=
binding
;
return
true
;
}
else
{
return
false
;
}
};
/**
* Execute a binding
* Only used by the plugin
* @private
* @param {HTMLElement} node the dom node on which to execute the binding
* @param {String} name the name of the binding
* @param {Any type} value the value to pass to the function
* @returns
*/
this
.
execBinding
=
function
execBinding
(
node
,
name
)
{
if
(
this
.
hasBinding
(
name
))
{
_bindings
[
name
].
apply
(
node
,
Array
.
prototype
.
slice
.
call
(
arguments
,
2
));
return
true
;
}
else
{
return
false
;
}
};
/**
* Check if the binding exists
* @private
* @param {String} name the name of the binding
* @returns
*/
this
.
hasBinding
=
function
hasBinding
(
name
)
{
return
_bindings
.
hasOwnProperty
(
name
);
};
/**
* Get a binding
* For debugging only
* @private
* @param {String} name the name of the binding
* @returns
*/
this
.
getBinding
=
function
getBinding
(
name
)
{
return
_bindings
[
name
];
};
/**
* Add multiple binding at once
* @param {Object} list the list of bindings to add
* @returns
*/
this
.
addBindings
=
function
addBindings
(
list
)
{
return
Tools
.
loop
(
list
,
function
(
binding
,
name
)
{
this
.
addBinding
(
name
,
binding
);
},
this
);
};
// Inits the model
this
.
setModel
(
$model
);
// Inits bindings
this
.
addBindings
(
$bindings
);
};
/**
* Both ways binding between a dom node attributes and the model
* @param {HTMLElement|SVGElement} node the dom node to apply the plugin to
* @param {String} name the name of the property to look for in the model's value
* @returns
*/
this
.
bind
=
function
bind
(
node
,
property
,
name
)
{
// Name can be unset if the value of a row is plain text
name
=
name
||
""
;
// In case of an array-like model the id is the index of the model's item to look for.
// The _id is added by the foreach function
var
id
=
node
.
getAttribute
(
"
data-
"
+
this
.
plugins
.
name
+
"
_id
"
),
// Else, it is the first element of the following
split
=
name
.
split
(
"
.
"
),
// So the index of the model is either id or the first element of split
modelIdx
=
id
||
split
.
shift
(),
// And the name of the property to look for in the value is
prop
=
id
?
name
:
split
.
join
(
"
.
"
),
// Get the model's value
get
=
Tools
.
getNestedProperty
(
_model
.
get
(
modelIdx
),
prop
),
// When calling bind like bind:newBinding,param1, param2... we need to get them
extraParam
=
Tools
.
toArray
(
arguments
).
slice
(
3
);
// 0 and false are acceptable falsy values
if
(
get
||
get
===
0
||
get
===
false
)
{
// If the binding hasn't been overriden
if
(
!
this
.
execBinding
.
apply
(
this
,
[
node
,
property
,
get
]
// Extra params are passed to the new binding too
.
concat
(
extraParam
)))
{
// Execute the default one which is a simple assignation
//node[property] = get;
DomUtils
.
setAttribute
(
node
,
property
,
get
);
}
}
// Only watch for changes (double way data binding) if the binding
// has not been redefined
if
(
!
this
.
hasBinding
(
property
))
{
node
.
addEventListener
(
"
change
"
,
function
(
event
)
{
if
(
_model
.
has
(
modelIdx
))
{
if
(
prop
)
{
_model
.
update
(
modelIdx
,
name
,
node
[
property
]);
}
else
{
_model
.
set
(
modelIdx
,
node
[
property
]);
}
}
},
true
);
}
// Watch for changes
this
.
observers
[
modelIdx
]
=
this
.
observers
[
modelIdx
]
||
[];
this
.
observers
[
modelIdx
].
push
(
_model
.
watchValue
(
modelIdx
,
function
(
value
)
{
if
(
!
this
.
execBinding
.
apply
(
this
,
[
node
,
property
,
Tools
.
getNestedProperty
(
value
,
prop
)]
// passing extra params too
.
concat
(
extraParam
)))
{
//node[property] = Tools.getNestedProperty(value, prop);
DomUtils
.
setAttribute
(
node
,
property
,
Tools
.
getNestedProperty
(
value
,
prop
));
}
},
this
));
};
/**
* Set the node's value into the model, the name is the model's property
* @private
* @param {HTMLElement|SVGElement} node
* @returns true if the property is added
*/
this
.
set
=
function
set
(
node
)
{
if
(
DomUtils
.
isAcceptedType
(
node
)
&&
node
.
name
)
{
_model
.
set
(
node
.
name
,
node
.
value
);
return
true
;
}
else
{
return
false
;
}
};
this
.
getItemIndex
=
function
getElementId
(
dom
)
{
var
dataset
=
DomUtils
.
getDataset
(
dom
);
if
(
dataset
&&
typeof
dataset
[
this
.
plugins
.
name
+
"
_id
"
]
!=
"
undefined
"
)
{
return
+
dataset
[
this
.
plugins
.
name
+
"
_id
"
];
}
else
{
return
false
;
}
};
/**
* Prevents the submit and set the model with all form's inputs
* @param {HTMLFormElement} DOMfro
m
* @returns true if valid form
*/
this
.
form
=
function
form
(
DOM
form
)
{
if
(
DOMform
&&
DOM
form
.
nodeName
==
"
FORM
"
)
{
var
that
=
this
;
DOM
form
.
addEventListener
(
"
submit
"
,
function
(
event
)
{
Tools
.
toArray
(
DOM
form
.
querySelectorAll
(
"
[name]
"
)).
forEach
(
that
.
set
,
that
);
event
.
preventDefault
();
},
true
);
return
true
;
}
else
{
return
false
;
}
};
/**
* Add a new way to handle a binding
* @param {String} name of the binding
* @param {Function} binding the function to handle the binding
* @returns
*/
this
.
addBinding
=
function
addBinding
(
name
,
binding
)
{
if
(
name
&&
typeof
name
==
"
string
"
&&
typeof
binding
==
"
function
"
)
{
_bindings
[
name
]
=
binding
;
return
true
;
}
else
{
return
false
;
}
};
/**
* Execute a binding
* Only used by the plugin
* @private
* @param {HTMLElement} node the dom node on which to execute the binding
* @param {String} name the name of the binding
* @param {Any type} value the value to pass to the function
* @returns
*/
this
.
execBinding
=
function
execBinding
(
node
,
name
)
{
if
(
this
.
hasBinding
(
name
))
{
_bindings
[
name
].
apply
(
node
,
Array
.
prototype
.
slice
.
call
(
arguments
,
2
));
return
true
;
}
else
{
return
false
;
}
};
/**
* Check if the binding exists
* @private
* @param {String} name the name of the binding
* @returns
*/
this
.
hasBinding
=
function
hasBinding
(
name
)
{
return
_bindings
.
hasOwnProperty
(
name
);
};
/**
* Get a binding
* For debugging only
* @private
* @param {String} name the name of the binding
* @returns
*/
this
.
getBinding
=
function
getBinding
(
name
)
{
return
_bindings
[
name
];
};
/**
* Add multiple binding at once
* @param {Object} list the list of bindings to add
* @returns
*/
this
.
addBindings
=
function
addBindings
(
list
)
{
return
Tools
.
loop
(
list
,
function
(
binding
,
name
)
{
this
.
addBinding
(
name
,
binding
);
},
this
);
};
// Inits the model
this
.
setModel
(
$model
);
// Inits bindings
this
.
addBindings
(
$bindings
);
};
});
...
...
@@ -754,122 +787,124 @@ define('Event.plugin',["DomUtils"],
*/
function
EventPlugin
(
Utils
)
{
/**
* The event plugin constructor.
* ex: new EventPlugin({method: function(){} ...}, false);
* @param {Object} the object that has the event handling methods
* @param {Boolean} $isMobile if the event handler has to map with touch events
*/
return
function
EventPluginConstructor
(
$parent
,
$isMobile
)
{
/**
* The parent callback
* @private
*/
var
_parent
=
null
,
/**
* The mapping object.
* @private
*/
_map
=
{
"
mousedown
"
:
"
touchstart
"
,
"
mouseup
"
:
"
touchend
"
,
"
mousemove
"
:
"
touchmove
"
},
/**
* Is touch device.
* @private
*/
_isMobile
=
!!
$isMobile
;
/**
* Add mapped event listener (for testing purpose).
* @private
*/
this
.
addEventListener
=
function
addEventListener
(
node
,
event
,
callback
,
useCapture
)
{
node
.
addEventListener
(
this
.
map
(
event
),
callback
,
!!
useCapture
);
};
/**
* Listen to DOM events.
* @param {Object} node DOM node
* @param {String} name event's name
* @param {String} listener callback's name
* @param {String} useCapture string
*/
this
.
listen
=
function
listen
(
node
,
name
,
listener
,
useCapture
)
{
this
.
addEventListener
(
node
,
name
,
function
(
e
){
_parent
[
listener
].
call
(
_parent
,
e
,
node
);
},
!!
useCapture
);
};
/**
* Delegate the event handling to a parent DOM element
* @param {Object} node DOM node
* @param {String} selector CSS3 selector to the element that listens to the event
* @param {String} name event's name
* @param {String} listener callback's name
* @param {String} useCapture string
*/
this
.
delegate
=
function
delegate
(
node
,
selector
,
name
,
listener
,
useCapture
)
{
this
.
addEventListener
(
node
,
name
,
function
(
event
){
if
(
Utils
.
matches
(
node
,
selector
,
event
.
target
))
{
_parent
[
listener
].
call
(
_parent
,
event
,
node
);
}
},
!!
useCapture
);
};
/**
* Get the parent object.
* @return {Object} the parent object
*/
this
.
getParent
=
function
getParent
()
{
return
_parent
;
};
/**
* Set the parent object.
* The parent object is an object which the functions are called by node listeners.
* @param {Object} the parent object
* @return true if object has been set
*/
this
.
setParent
=
function
setParent
(
parent
)
{
if
(
parent
instanceof
Object
){
_parent
=
parent
;
return
true
;
}
return
false
;
};
/**
* Get event mapping.
* @param {String} event's name
* @return the mapped event's name
*/
this
.
map
=
function
map
(
name
)
{
return
_isMobile
?
(
_map
[
name
]
||
name
)
:
name
;
};
/**
* Set event mapping.
* @param {String} event's name
* @param {String} event's value
* @return true if mapped
*/
this
.
setMap
=
function
setMap
(
name
,
value
)
{
if
(
typeof
name
==
"
string
"
&&
typeof
value
==
"
string
"
)
{
_map
[
name
]
=
value
;
return
true
;
}
return
false
;
};
//init
this
.
setParent
(
$parent
);
};
/**
* The event plugin constructor.
* ex: new EventPlugin({method: function(){} ...}, false);
* @param {Object} the object that has the event handling methods
* @param {Boolean} $isMobile if the event handler has to map with touch events
*/
return
function
EventPluginConstructor
(
$parent
,
$isMobile
)
{
/**
* The parent callback
* @private
*/
var
_parent
=
null
,
/**
* The mapping object.
* @private
*/
_map
=
{
"
mousedown
"
:
"
touchstart
"
,
"
mouseup
"
:
"
touchend
"
,
"
mousemove
"
:
"
touchmove
"
},
/**
* Is touch device.
* @private
*/
_isMobile
=
!!
$isMobile
;
/**
* Add mapped event listener (for testing purpose).
* @private
*/
this
.
addEventListener
=
function
addEventListener
(
node
,
event
,
callback
,
useCapture
)
{
node
.
addEventListener
(
this
.
map
(
event
),
callback
,
!!
useCapture
);
};
/**
* Listen to DOM events.
* @param {Object} node DOM node
* @param {String} name event's name
* @param {String} listener callback's name
* @param {String} useCapture string
*/
this
.
listen
=
function
listen
(
node
,
name
,
listener
,
useCapture
)
{
this
.
addEventListener
(
node
,
name
,
function
(
e
){
_parent
[
listener
].
call
(
_parent
,
e
,
node
);
},
!!
useCapture
);
};
/**
* Delegate the event handling to a parent DOM element
* @param {Object} node DOM node
* @param {String} selector CSS3 selector to the element that listens to the event
* @param {String} name event's name
* @param {String} listener callback's name
* @param {String} useCapture string
*/
this
.
delegate
=
function
delegate
(
node
,
selector
,
name
,
listener
,
useCapture
)
{
this
.
addEventListener
(
node
,
name
,
function
(
event
){
if
(
Utils
.
matches
(
node
,
selector
,
event
.
target
))
{
_parent
[
listener
].
call
(
_parent
,
event
,
node
);
}
},
!!
useCapture
);
};
/**
* Get the parent object.
* @return {Object} the parent object
*/
this
.
getParent
=
function
getParent
()
{
return
_parent
;
};
/**
* Set the parent object.
* The parent object is an object which the functions are called by node listeners.
* @param {Object} the parent object
* @return true if object has been set
*/
this
.
setParent
=
function
setParent
(
parent
)
{
if
(
parent
instanceof
Object
){
_parent
=
parent
;
return
true
;
}
return
false
;
};
/**
* Get event mapping.
* @param {String} event's name
* @return the mapped event's name
*/
this
.
map
=
function
map
(
name
)
{
return
_isMobile
?
(
_map
[
name
]
||
name
)
:
name
;
};
/**
* Set event mapping.
* @param {String} event's name
* @param {String} event's value
* @return true if mapped
*/
this
.
setMap
=
function
setMap
(
name
,
value
)
{
if
(
typeof
name
==
"
string
"
&&
typeof
value
==
"
string
"
)
{
_map
[
name
]
=
value
;
return
true
;
}
return
false
;
};
//init
this
.
setParent
(
$parent
);
};
});
...
...
@@ -890,6 +925,8 @@ define('LocalStore',["Store", "Tools"],
*/
function
LocalStore
(
Store
,
Tools
)
{
function
LocalStoreConstructor
()
{
/**
...
...
@@ -971,7 +1008,7 @@ function LocalStore(Store, Tools) {
return
function
LocalStoreFactory
(
init
)
{
LocalStoreConstructor
.
prototype
=
new
Store
(
init
);
return
new
LocalStoreConstructor
;
return
new
LocalStoreConstructor
()
;
};
});
...
...
@@ -995,6 +1032,8 @@ define('Plugins',["Tools", "DomUtils"],
*/
function
Plugins
(
Tools
,
DomUtils
)
{
return
function
PluginsConstructor
(
$plugins
)
{
/**
...
...
@@ -1147,277 +1186,285 @@ define('OObject',["StateMachine", "Store", "Plugins", "DomUtils", "Tools"],
*/
function
OObject
(
StateMachine
,
Store
,
Plugins
,
DomUtils
,
Tools
)
{
return
function
OObjectConstructor
(
otherStore
)
{
return
function
OObjectConstructor
(
otherStore
)
{
/**
* This function creates the dom of the UI from its template
* It then queries the dom for data- attributes
* It can't be executed if the template is not set
* @private
*/
var
render
=
function
render
(
UI
)
{
// The place where the template will be created
// is either the currentPlace where the node is placed
// or a temporary div
var
baseNode
=
_currentPlace
||
document
.
createElement
(
"
div
"
);
// If the template is set
if
(
UI
.
template
)
{
// In this function, the thisObject is the UI's prototype
// UI is the UI that has OObject as prototype
if
(
typeof
UI
.
template
==
"
string
"
)
{
// Let the browser do the parsing, can't be faster & easier.
baseNode
.
innerHTML
=
UI
.
template
.
trim
();
}
else
if
(
DomUtils
.
isAcceptedType
(
UI
.
template
))
{
// If it's already an HTML element
baseNode
.
appendChild
(
UI
.
template
);
}
// The UI must be placed in a unique dom node
// If not, there can't be multiple UIs placed in the same parentNode
// as it wouldn't be possible to know which node would belong to which UI
// This is probably a DOM limitation.
if
(
baseNode
.
childNodes
.
length
>
1
)
{
throw
new
Error
(
"
UI.template should have only one parent node
"
);
}
else
{
UI
.
dom
=
baseNode
.
childNodes
[
0
];
}
UI
.
plugins
.
apply
(
UI
.
dom
);
}
else
{
// An explicit message I hope
throw
new
Error
(
"
UI.template must be set prior to render
"
);
}
},
/**
* This function appends the dom tree to the given dom node.
* This dom node should be somewhere in the dom of the application
* @private
*/
place
=
function
place
(
UI
,
DOMplace
,
beforeNode
)
{
if
(
DOMplace
)
{
// IE (until 9) apparently fails to appendChild when insertBefore's second argument is null, hence this.
if
(
beforeNode
)
{
DOMplace
.
insertBefore
(
UI
.
dom
,
beforeNode
);
}
else
{
DOMplace
.
appendChild
(
UI
.
dom
);
}
// Also save the new place, so next renderings
// will be made inside it
_currentPlace
=
DOMplace
;
}
},
/**
* Does rendering & placing in one function
* @private
*/
renderNPlace
=
function
renderNPlace
(
UI
,
dom
)
{
render
(
UI
);
place
.
apply
(
null
,
Tools
.
toArray
(
arguments
));
},
/**
* This stores the current place
* If this is set, this is the place where new templates
* will be appended
* @private
*/
_currentPlace
=
null
,
/**
* The UI's stateMachine.
* Much better than if(stuff) do(stuff) else if (!stuff and stuff but not stouff) do (otherstuff)
* Please open an issue if you want to propose a better one
* @private
*/
_stateMachine
=
new
StateMachine
(
"
Init
"
,
{
"
Init
"
:
[[
"
render
"
,
render
,
this
,
"
Rendered
"
],
[
"
place
"
,
renderNPlace
,
this
,
"
Rendered
"
]],
"
Rendered
"
:
[[
"
place
"
,
place
,
this
],
[
"
render
"
,
render
,
this
]]
});
/**
* The UI's Store
* It has set/get/del/has/watch/unwatch methods
* @see Emily's doc for more info on how it works.
*/
this
.
model
=
otherStore
instanceof
Store
?
otherStore
:
new
Store
();
/**
* The module that will manage the plugins for this UI
* @see Olives/Plugins' doc for more info on how it works.
*/
this
.
plugins
=
new
Plugins
();
/**
* Describes the template, can either be like "<p></p>" or HTMLElements
* @type string or HTMLElement|SVGElement
*/
this
.
template
=
null
;
/**
* This will hold the dom nodes built from the template.
*/
this
.
dom
=
null
;
/**
* Place the UI in a given dom node
* @param node the node on which to append the UI
* @param beforeNode the dom before which to append the UI
*/
this
.
place
=
function
place
(
node
,
beforeNode
)
{
_stateMachine
.
event
(
"
place
"
,
this
,
node
,
beforeNode
);
};
/**
* Renders the template to dom nodes and applies the plugins on it
* It requires the template to be set first
*/
this
.
render
=
function
render
()
{
_stateMachine
.
event
(
"
render
"
,
this
);
};
/**
* Set the UI's template from a DOM element
* @param {HTMLElement|SVGElement} dom the dom element that'll become the template of the UI
* @returns true if dom is an HTMLElement|SVGElement
*/
this
.
setTemplateFromDom
=
function
setTemplateFromDom
(
dom
)
{
if
(
DomUtils
.
isAcceptedType
(
dom
))
{
this
.
template
=
dom
;
return
true
;
}
else
{
return
false
;
}
};
/**
* Transforms dom nodes into a UI.
* It basically does a setTemplateFromDOM, then a place
* It's a helper function
* @param {HTMLElement|SVGElement} node the dom to transform to a UI
* @returns true if dom is an HTMLElement|SVGElement
*/
this
.
alive
=
function
alive
(
dom
)
{
if
(
DomUtils
.
isAcceptedType
(
dom
))
{
this
.
setTemplateFromDom
(
dom
);
this
.
place
(
dom
.
parentNode
,
dom
.
nextElementSibling
);
return
true
;
}
else
{
return
false
;
}
};
/**
* Get the current dom node where the UI is placed.
* for debugging purpose
* @private
* @return {HTMLElement} node the dom where the UI is placed.
*/
this
.
getCurrentPlace
=
function
(){
return
_currentPlace
;
};
};
/**
* This function creates the dom of the UI from its template
* It then queries the dom for data- attributes
* It can't be executed if the template is not set
* @private
*/
var
render
=
function
render
(
UI
)
{
// The place where the template will be created
// is either the currentPlace where the node is placed
// or a temporary div
var
baseNode
=
_currentPlace
||
document
.
createElement
(
"
div
"
);
// If the template is set
if
(
UI
.
template
)
{
// In this function, the thisObject is the UI's prototype
// UI is the UI that has OObject as prototype
if
(
typeof
UI
.
template
==
"
string
"
)
{
// Let the browser do the parsing, can't be faster & easier.
baseNode
.
innerHTML
=
UI
.
template
.
trim
();
}
else
if
(
DomUtils
.
isAcceptedType
(
UI
.
template
))
{
// If it's already an HTML element
baseNode
.
appendChild
(
UI
.
template
);
}
});
// The UI must be placed in a unique dom node
// If not, there can't be multiple UIs placed in the same parentNode
// as it wouldn't be possible to know which node would belong to which UI
// This is probably a DOM limitation.
if
(
baseNode
.
childNodes
.
length
>
1
)
{
throw
Error
(
"
UI.template should have only one parent node
"
);
}
else
{
UI
.
dom
=
baseNode
.
childNodes
[
0
];
}
/**
* Olives http://flams.github.com/olives
* The MIT License (MIT)
* Copyright (c) 2012-2013 Olivier Scherrer <pode.fr@gmail.com> - Olivier Wietrich <olivier.wietrich@gmail.com>
*/
UI
.
plugins
.
apply
(
UI
.
dom
);
define
(
'
Place.plugin
'
,[
"
OObject
"
,
"
Tools
"
],
/**
* @class
* Place plugin places OObject in the DOM.
* @requires OObject, Tools
*/
function
PlacePlugin
(
OObject
,
Tools
)
{
}
else
{
// An explicit message I hope
throw
Error
(
"
UI.template must be set prior to render
"
);
}
},
/**
* Intilialize a Place.plugin with a list of OObjects
* @param {Object} $uis a list of OObjects such as:
* {
* "header": new OObject(),
* "list": new OObject()
* }
* @Constructor
*/
return
function
PlacePluginConstructor
(
$uis
)
{
/**
* The list of uis currently set in this place plugin
* @private
*/
var
_uis
=
{};
/**
* Attach an OObject to this DOM element
* @param {HTML|SVGElement} node the dom node where to attach the OObject
* @param {String} the name of the OObject to attach
* @throws {NoSuchOObject} an error if there's no OObject for the given name
*/
this
.
place
=
function
place
(
node
,
name
)
{
if
(
_uis
[
name
]
instanceof
OObject
)
{
_uis
[
name
].
place
(
node
);
}
else
{
throw
new
Error
(
name
+
"
is not an OObject UI in place:
"
+
name
);
}
};
/**
* Add an OObject that can be attached to a dom element
* @param {String} the name of the OObject to add to the list
* @param {OObject} ui the OObject to add the list
* @returns {Boolean} true if the OObject was added
*/
this
.
set
=
function
set
(
name
,
ui
)
{
if
(
typeof
name
==
"
string
"
&&
ui
instanceof
OObject
)
{
_uis
[
name
]
=
ui
;
return
true
;
}
else
{
return
false
;
}
};
/**
* Add multiple dom elements at once
* @param {Object} $uis a list of OObjects such as:
* {
* "header": new OObject(),
* "list": new OObject()
* }
*/
this
.
setAll
=
function
setAll
(
uis
)
{
Tools
.
loop
(
uis
,
function
(
ui
,
name
)
{
this
.
set
(
name
,
ui
);
},
this
);
};
/**
* Returns an OObject from the list given its name
* @param {String} the name of the OObject to get
* @returns {OObject} OObject for the given name
*/
this
.
get
=
function
get
(
name
)
{
return
_uis
[
name
];
};
this
.
setAll
(
$uis
);
};
/**
* This function appends the dom tree to the given dom node.
* This dom node should be somewhere in the dom of the application
* @private
*/
place
=
function
place
(
UI
,
place
,
beforeNode
)
{
if
(
place
)
{
// IE (until 9) apparently fails to appendChild when insertBefore's second argument is null, hence this.
beforeNode
?
place
.
insertBefore
(
UI
.
dom
,
beforeNode
)
:
place
.
appendChild
(
UI
.
dom
);
// Also save the new place, so next renderings
// will be made inside it
_currentPlace
=
place
;
}
},
});
/**
* Does rendering & placing in one function
* @private
*/
renderNPlace
=
function
renderNPlace
(
UI
,
dom
)
{
render
(
UI
);
place
.
apply
(
null
,
Tools
.
toArray
(
arguments
));
},
/**
* This stores the current place
* If this is set, this is the place where new templates
* will be appended
* @private
*/
_currentPlace
=
null
,
/**
* The UI's stateMachine.
* Much better than if(stuff) do(stuff) else if (!stuff and stuff but not stouff) do (otherstuff)
* Please open an issue if you want to propose a better one
* @private
*/
_stateMachine
=
new
StateMachine
(
"
Init
"
,
{
"
Init
"
:
[[
"
render
"
,
render
,
this
,
"
Rendered
"
],
[
"
place
"
,
renderNPlace
,
this
,
"
Rendered
"
]],
"
Rendered
"
:
[[
"
place
"
,
place
,
this
],
[
"
render
"
,
render
,
this
]]
});
/**
* The UI's Store
* It has set/get/del/has/watch/unwatch methods
* @see Emily's doc for more info on how it works.
*/
this
.
model
=
otherStore
instanceof
Store
?
otherStore
:
new
Store
;
/**
* The module that will manage the plugins for this UI
* @see Olives/Plugins' doc for more info on how it works.
*/
this
.
plugins
=
new
Plugins
();
/**
* Describes the template, can either be like "<p></p>" or HTMLElements
* @type string or HTMLElement|SVGElement
*/
this
.
template
=
null
;
/**
* This will hold the dom nodes built from the template.
*/
this
.
dom
=
null
;
/**
* Place the UI in a given dom node
* @param node the node on which to append the UI
* @param beforeNode the dom before which to append the UI
*/
this
.
place
=
function
place
(
node
,
beforeNode
)
{
_stateMachine
.
event
(
"
place
"
,
this
,
node
,
beforeNode
);
};
/**
* Renders the template to dom nodes and applies the plugins on it
* It requires the template to be set first
*/
this
.
render
=
function
render
()
{
_stateMachine
.
event
(
"
render
"
,
this
);
};
/**
* Set the UI's template from a DOM element
* @param {HTMLElement|SVGElement} dom the dom element that'll become the template of the UI
* @returns true if dom is an HTMLElement|SVGElement
*/
this
.
setTemplateFromDom
=
function
setTemplateFromDom
(
dom
)
{
if
(
DomUtils
.
isAcceptedType
(
dom
))
{
this
.
template
=
dom
;
return
true
;
}
else
{
return
false
;
}
};
/**
* Transforms dom nodes into a UI.
* It basically does a setTemplateFromDOM, then a place
* It's a helper function
* @param {HTMLElement|SVGElement} node the dom to transform to a UI
* @returns true if dom is an HTMLElement|SVGElement
*/
this
.
alive
=
function
alive
(
dom
)
{
if
(
DomUtils
.
isAcceptedType
(
dom
))
{
this
.
setTemplateFromDom
(
dom
);
this
.
place
(
dom
.
parentNode
,
dom
.
nextElementSibling
);
return
true
;
}
else
{
return
false
;
}
};
/**
* Get the current dom node where the UI is placed.
* for debugging purpose
* @private
* @return {HTMLElement} node the dom where the UI is placed.
*/
this
.
getCurrentPlace
=
function
(){
return
_currentPlace
;
};
};
});
/**
* Olives http://flams.github.com/olives
* The MIT License (MIT)
* Copyright (c) 2012-2013 Olivier Scherrer <pode.fr@gmail.com> - Olivier Wietrich <olivier.wietrich@gmail.com>
*/
define
(
'
Place.plugin
'
,[
"
OObject
"
,
"
Tools
"
],
/**
* @class
* Place plugin places OObject in the DOM.
* @requires OObject, Tools
*/
function
PlacePlugin
(
OObject
,
Tools
)
{
/**
* Intilialize a Place.plugin with a list of OObjects
* @param {Object} $uis a list of OObjects such as:
* {
* "header": new OObject(),
* "list": new OObject()
* }
* @Constructor
*/
return
function
PlacePluginConstructor
(
$uis
)
{
/**
* The list of uis currently set in this place plugin
* @private
*/
var
_uis
=
{};
/**
* Attach an OObject to this DOM element
* @param {HTML|SVGElement} node the dom node where to attach the OObject
* @param {String} the name of the OObject to attach
* @throws {NoSuchOObject} an error if there's no OObject for the given name
*/
this
.
place
=
function
place
(
node
,
name
)
{
if
(
_uis
[
name
]
instanceof
OObject
)
{
_uis
[
name
].
place
(
node
);
}
else
{
throw
new
Error
(
name
+
"
is not an OObject UI in place:
"
+
name
);
}
};
/**
* Add an OObject that can be attached to a dom element
* @param {String} the name of the OObject to add to the list
* @param {OObject} ui the OObject to add the list
* @returns {Boolean} true if the OObject was added
*/
this
.
set
=
function
set
(
name
,
ui
)
{
if
(
typeof
name
==
"
string
"
&&
ui
instanceof
OObject
)
{
_uis
[
name
]
=
ui
;
return
true
;
}
else
{
return
false
;
}
};
/**
* Add multiple dom elements at once
* @param {Object} $uis a list of OObjects such as:
* {
* "header": new OObject(),
* "list": new OObject()
* }
*/
this
.
setAll
=
function
setAll
(
uis
)
{
Tools
.
loop
(
uis
,
function
(
ui
,
name
)
{
this
.
set
(
name
,
ui
);
},
this
);
};
/**
* Returns an OObject from the list given its name
* @param {String} the name of the OObject to get
* @returns {OObject} OObject for the given name
*/
this
.
get
=
function
get
(
name
)
{
return
_uis
[
name
];
};
this
.
setAll
(
$uis
);
};
});
/**
* Olives http://flams.github.com/olives
* The MIT License (MIT)
* Copyright (c) 2012-2013 Olivier Scherrer <pode.fr@gmail.com> - Olivier Wietrich <olivier.wietrich@gmail.com>
*/
/**
* Olives http://flams.github.com/olives
* The MIT License (MIT)
* Copyright (c) 2012-2013 Olivier Scherrer <pode.fr@gmail.com> - Olivier Wietrich <olivier.wietrich@gmail.com>
*/
define
(
'
SocketIOTransport
'
,[
"
Observable
"
,
"
Tools
"
],
/**
...
...
@@ -1427,6 +1474,8 @@ define('SocketIOTransport',["Observable", "Tools"],
*/
function
SocketIOTransport
(
Observable
,
Tools
)
{
/**
* Defines the SocketIOTransport
* @private
...
...
@@ -1462,7 +1511,7 @@ function SocketIOTransport(Observable, Tools) {
*/
this
.
getSocket
=
function
getSocket
()
{
return
_socket
;
}
,
}
;
/**
* Subscribe to a socket event
...
...
@@ -1471,7 +1520,7 @@ function SocketIOTransport(Observable, Tools) {
*/
this
.
on
=
function
on
(
event
,
func
)
{
return
_socket
.
on
(
event
,
func
);
}
,
}
;
/**
* Subscribe to a socket event but disconnect as soon as it fires.
...
...
@@ -1510,15 +1559,17 @@ function SocketIOTransport(Observable, Tools) {
* @param {Object} scope the scope in which to execute the callback
*/
this
.
request
=
function
request
(
channel
,
data
,
func
,
scope
)
{
if
(
typeof
channel
==
"
string
"
&&
typeof
data
!=
"
undefined
"
)
{
if
(
typeof
channel
==
"
string
"
&&
typeof
data
!=
"
undefined
"
)
{
var
reqData
=
{
eventId
:
Date
.
now
()
+
Math
.
floor
(
Math
.
random
()
*
1
e6
),
data
:
data
},
boundCallback
=
function
()
{
func
&&
func
.
apply
(
scope
||
null
,
arguments
);
if
(
func
)
{
func
.
apply
(
scope
||
null
,
arguments
);
}
};
this
.
once
(
reqData
.
eventId
,
boundCallback
);
...
...
@@ -1540,9 +1591,9 @@ function SocketIOTransport(Observable, Tools) {
* @returns
*/
this
.
listen
=
function
listen
(
channel
,
data
,
func
,
scope
)
{
if
(
typeof
channel
==
"
string
"
&&
typeof
data
!=
"
undefined
"
&&
typeof
func
==
"
function
"
)
{
if
(
typeof
channel
==
"
string
"
&&
typeof
data
!=
"
undefined
"
&&
typeof
func
==
"
function
"
)
{
var
reqData
=
{
eventId
:
Date
.
now
()
+
Math
.
floor
(
Math
.
random
()
*
1
e6
),
...
...
@@ -1550,7 +1601,9 @@ function SocketIOTransport(Observable, Tools) {
keepAlive
:
true
},
boundCallback
=
function
()
{
func
&&
func
.
apply
(
scope
||
null
,
arguments
);
if
(
func
)
{
func
.
apply
(
scope
||
null
,
arguments
);
}
},
that
=
this
;
...
...
@@ -1573,3 +1626,504 @@ function SocketIOTransport(Observable, Tools) {
this
.
setSocket
(
$socket
);
};
});
/**
* Olives http://flams.github.com/olives
* The MIT License (MIT)
* Copyright (c) 2012-2013 Olivier Scherrer <pode.fr@gmail.com> - Olivier Wietrich <olivier.wietrich@gmail.com>
*/
define
(
'
Stack
'
,[
'
Tools
'
],
/**
* @class
* A Stack is a tool for managing DOM elements as groups. Within a group, dom elements
* can be added, removed, moved around. The group can be moved to another parent node
* while keeping the DOM elements in the same order, excluding the parent dom elements's
* children that are not in the Stack.
*/
function
Stack
()
{
var
Tools
=
require
(
"
Tools
"
);
return
function
StackConstructor
(
$parent
)
{
/**
* The parent DOM element is a documentFragment by default
* @private
*/
var
_parent
=
document
.
createDocumentFragment
(),
/**
* The place where the dom elements hide
* @private
*/
_hidePlace
=
document
.
createElement
(
"
div
"
),
/**
* The list of dom elements that are part of the stack
* Helps for excluding elements that are not part of it
* @private
*/
_childNodes
=
[],
_lastTransit
=
null
;
/**
* Add a DOM element to the stack. It will be appended.
* @param {HTMLElement} dom the DOM element to add
* @returns {HTMLElement} dom
*/
this
.
add
=
function
add
(
dom
)
{
if
(
!
this
.
has
(
dom
)
&&
dom
instanceof
HTMLElement
)
{
_parent
.
appendChild
(
dom
);
_childNodes
.
push
(
dom
);
return
dom
;
}
else
{
return
false
;
}
};
/**
* Remove a DOM element from the stack.
* @param {HTMLElement} dom the DOM element to remove
* @returns {HTMLElement} dom
*/
this
.
remove
=
function
remove
(
dom
)
{
var
index
;
if
(
this
.
has
(
dom
))
{
index
=
_childNodes
.
indexOf
(
dom
);
_parent
.
removeChild
(
dom
);
_childNodes
.
splice
(
index
,
1
);
return
dom
;
}
else
{
return
false
;
}
};
/**
* Place a stack by appending its DOM elements to a new parent
* @param {HTMLElement} newParentDom the new DOM element to append the stack to
* @returns {HTMLElement} newParentDom
*/
this
.
place
=
function
place
(
newParentDom
)
{
if
(
newParentDom
instanceof
HTMLElement
)
{
[].
slice
.
call
(
_parent
.
childNodes
).
forEach
(
function
(
childDom
)
{
if
(
this
.
has
(
childDom
))
{
newParentDom
.
appendChild
(
childDom
);
}
},
this
);
return
this
.
_setParent
(
newParentDom
);
}
else
{
return
false
;
}
};
/**
* Move an element up in the stack
* @param {HTMLElement} dom the dom element to move up
* @returns {HTMLElement} dom
*/
this
.
up
=
function
up
(
dom
)
{
if
(
this
.
has
(
dom
))
{
var
domPosition
=
this
.
getPosition
(
dom
);
this
.
move
(
dom
,
domPosition
+
1
);
return
dom
;
}
else
{
return
false
;
}
};
/**
* Move an element down in the stack
* @param {HTMLElement} dom the dom element to move down
* @returns {HTMLElement} dom
*/
this
.
down
=
function
down
(
dom
)
{
if
(
this
.
has
(
dom
))
{
var
domPosition
=
this
.
getPosition
(
dom
);
this
.
move
(
dom
,
domPosition
-
1
);
return
dom
;
}
else
{
return
false
;
}
};
/**
* Move an element that is already in the stack to a new position
* @param {HTMLElement} dom the dom element to move
* @param {Number} position the position to which to move the DOM element
* @returns {HTMLElement} dom
*/
this
.
move
=
function
move
(
dom
,
position
)
{
if
(
this
.
has
(
dom
))
{
var
domIndex
=
_childNodes
.
indexOf
(
dom
);
_childNodes
.
splice
(
domIndex
,
1
);
// Preventing a bug in IE when insertBefore is not given a valid
// second argument
var
nextElement
=
getNextElementInDom
(
position
);
if
(
nextElement
)
{
_parent
.
insertBefore
(
dom
,
nextElement
);
}
else
{
_parent
.
appendChild
(
dom
);
}
_childNodes
.
splice
(
position
,
0
,
dom
);
return
dom
;
}
else
{
return
false
;
}
};
function
getNextElementInDom
(
position
)
{
if
(
position
>=
_childNodes
.
length
)
{
return
;
}
var
nextElement
=
_childNodes
[
position
];
if
(
Tools
.
toArray
(
_parent
.
childNodes
).
indexOf
(
nextElement
)
==
-
1
)
{
return
getNextElementInDom
(
position
+
1
);
}
else
{
return
nextElement
;
}
}
/**
* Insert a new element at a specific position in the stack
* @param {HTMLElement} dom the dom element to insert
* @param {Number} position the position to which to insert the DOM element
* @returns {HTMLElement} dom
*/
this
.
insert
=
function
insert
(
dom
,
position
)
{
if
(
!
this
.
has
(
dom
)
&&
dom
instanceof
HTMLElement
)
{
_childNodes
.
splice
(
position
,
0
,
dom
);
_parent
.
insertBefore
(
dom
,
_parent
.
childNodes
[
position
]);
return
dom
;
}
else
{
return
false
;
}
};
/**
* Get the position of an element in the stack
* @param {HTMLElement} dom the dom to get the position from
* @returns {HTMLElement} dom
*/
this
.
getPosition
=
function
getPosition
(
dom
)
{
return
_childNodes
.
indexOf
(
dom
);
};
/**
* Count the number of elements in a stack
* @returns {Number} the number of items
*/
this
.
count
=
function
count
()
{
return
_parent
.
childNodes
.
length
;
};
/**
* Tells if a DOM element is in the stack
* @param {HTMLElement} dom the dom to tell if its in the stack
* @returns {HTMLElement} dom
*/
this
.
has
=
function
has
(
childDom
)
{
return
this
.
getPosition
(
childDom
)
>=
0
;
};
/**
* Hide a dom element that was previously added to the stack
* It will be taken out of the dom until displayed again
* @param {HTMLElement} dom the dom to hide
* @return {boolean} if dom element is in the stack
*/
this
.
hide
=
function
hide
(
dom
)
{
if
(
this
.
has
(
dom
))
{
_hidePlace
.
appendChild
(
dom
);
return
true
;
}
else
{
return
false
;
}
};
/**
* Show a dom element that was previously hidden
* It will be added back to the dom
* @param {HTMLElement} dom the dom to show
* @return {boolean} if dom element is current hidden
*/
this
.
show
=
function
show
(
dom
)
{
if
(
this
.
has
(
dom
)
&&
dom
.
parentNode
===
_hidePlace
)
{
this
.
move
(
dom
,
_childNodes
.
indexOf
(
dom
));
return
true
;
}
else
{
return
false
;
}
};
/**
* Helper function for hiding all the dom elements
*/
this
.
hideAll
=
function
hideAll
()
{
_childNodes
.
forEach
(
this
.
hide
,
this
);
};
/**
* Helper function for showing all the dom elements
*/
this
.
showAll
=
function
showAll
()
{
_childNodes
.
forEach
(
this
.
show
,
this
);
};
/**
* Get the parent node that a stack is currently attached to
* @returns {HTMLElement} parent node
*/
this
.
getParent
=
function
_getParent
()
{
return
_parent
;
};
/**
* Set the parent element (without appending the stacks dom elements to)
* @private
*/
this
.
_setParent
=
function
_setParent
(
parent
)
{
if
(
parent
instanceof
HTMLElement
)
{
_parent
=
parent
;
return
_parent
;
}
else
{
return
false
;
}
};
/**
* Get the place where the DOM elements are hidden
* @private
*/
this
.
getHidePlace
=
function
getHidePlace
()
{
return
_hidePlace
;
};
/**
* Set the place where the DOM elements are hidden
* @private
*/
this
.
setHidePlace
=
function
setHidePlace
(
hidePlace
)
{
if
(
hidePlace
instanceof
HTMLElement
)
{
_hidePlace
=
hidePlace
;
return
true
;
}
else
{
return
false
;
}
};
/**
* Get the last dom element that the stack transitted to
* @returns {HTMLElement} the last dom element
*/
this
.
getLastTransit
=
function
getLastTransit
()
{
return
_lastTransit
;
};
/**
* Transit between views, will show the new one and hide the previous
* element that the stack transitted to, if any.
* @param {HTMLElement} dom the element to transit to
* @returns {Boolean} false if the element can't be shown
*/
this
.
transit
=
function
transit
(
dom
)
{
if
(
_lastTransit
)
{
this
.
hide
(
_lastTransit
);
}
if
(
this
.
show
(
dom
))
{
_lastTransit
=
dom
;
return
true
;
}
else
{
return
false
;
}
};
this
.
_setParent
(
$parent
);
};
});
/**
* Olives http://flams.github.com/olives
* The MIT License (MIT)
* Copyright (c) 2012-2013 Olivier Scherrer <pode.fr@gmail.com> - Olivier Wietrich <olivier.wietrich@gmail.com>
*/
define
(
'
LocationRouter
'
,[
"
Router
"
,
"
Tools
"
],
/**
* @class
* A locationRouter is a router which navigates to the route defined in the URL and updates this URL
* while navigating. It's a subtype of Emily's Router
*/
function
LocationRouter
(
Router
,
Tools
)
{
function
LocationRouterConstructor
()
{
/**
* The handle on the watch
* @private
*/
var
_watchHandle
,
/**
* The default route to navigate to when nothing is supplied in the url
* @private
*/
_defaultRoute
=
""
,
/**
* The last route that was navigated to
* @private
*/
_lastRoute
=
window
.
location
.
hash
;
/**
* Navigates to the current hash or to the default route if none is supplied in the url
* @private
*/
/*jshint validthis:true*/
function
doNavigate
()
{
if
(
window
.
location
.
hash
)
{
var
parsedHash
=
this
.
parse
(
window
.
location
.
hash
);
this
.
navigate
.
apply
(
this
,
parsedHash
);
}
else
{
this
.
navigate
(
_defaultRoute
);
}
}
/**
* Set the default route to navigate to when nothing is defined in the url
* @param {String} defaultRoute the defaultRoute to navigate to
* @returns {Boolean} true if it's not an empty string
*/
this
.
setDefaultRoute
=
function
setDefaultRoute
(
defaultRoute
)
{
if
(
defaultRoute
&&
typeof
defaultRoute
==
"
string
"
)
{
_defaultRoute
=
defaultRoute
;
return
true
;
}
else
{
return
false
;
}
};
/**
* Get the currently set default route
* @returns {String} the default route
*/
this
.
getDefaultRoute
=
function
getDefaultRoute
()
{
return
_defaultRoute
;
};
/**
* The function that parses the url to determine the route to navigate to.
* It has a default behavior explained below, but can be overriden as long as
* it has the same contract.
* @param {String} hash the hash coming from window.location.has
* @returns {Array} has to return an array with the list of arguments to call
* navigate with. The first item of the array must be the name of the route.
*
* Example: #album/holiday/2013
* will navigate to the route "album" and give two arguments "holiday" and "2013"
*/
this
.
parse
=
function
parse
(
hash
)
{
return
hash
.
split
(
"
#
"
).
pop
().
split
(
"
/
"
);
};
/**
* The function that converts, or serialises the route and its arguments to a valid URL.
* It has a default behavior below, but can be overriden as long as it has the same contract.
* @param {Array} args the list of arguments to serialize
* @returns {String} the serialized arguments to add to the url hashmark
*
* Example:
* ["album", "holiday", "2013"];
* will give "album/holiday/2013"
*
*/
this
.
toUrl
=
function
toUrl
(
args
)
{
return
args
.
join
(
"
/
"
);
};
/**
* When all the routes and handlers have been defined, start the location router
* so it parses the URL and navigates to the corresponding route.
* It will also start listening to route changes and hashmark changes to navigate.
* While navigating, the hashmark itself will also change to reflect the current route state
*/
this
.
start
=
function
start
(
defaultRoute
)
{
this
.
setDefaultRoute
(
defaultRoute
);
doNavigate
.
call
(
this
);
this
.
bindOnHashChange
();
this
.
bindOnRouteChange
();
};
/**
* Remove the events handler for cleaning.
*/
this
.
destroy
=
function
destroy
()
{
this
.
unwatch
(
_watchHandle
);
window
.
removeEventListener
(
"
hashchange
"
,
this
.
boundOnHashChange
,
true
);
};
/**
* Parse the hash and navigate to the corresponding url
* @private
*/
this
.
onHashChange
=
function
onHashChange
()
{
if
(
window
.
location
.
hash
!=
_lastRoute
)
{
doNavigate
.
call
(
this
);
}
};
/**
* The bound version of onHashChange for add/removeEventListener
* @private
*/
this
.
boundOnHashChange
=
this
.
onHashChange
.
bind
(
this
);
/**
* Add an event listener to hashchange to navigate to the corresponding route
* when it changes
* @private
*/
this
.
bindOnHashChange
=
function
bindOnHashChange
()
{
window
.
addEventListener
(
"
hashchange
"
,
this
.
boundOnHashChange
,
true
);
};
/**
* Watch route change events from the router to update the location
* @private
*/
this
.
bindOnRouteChange
=
function
bindOnRouteChange
()
{
_watchHandle
=
this
.
watch
(
this
.
onRouteChange
,
this
);
};
/**
* The handler for when the route changes
* It updates the location
* @private
*/
this
.
onRouteChange
=
function
onRouteChange
()
{
window
.
location
.
hash
=
this
.
toUrl
(
Tools
.
toArray
(
arguments
));
_lastRoute
=
window
.
location
.
hash
;
};
this
.
getLastRoute
=
function
getLastRoute
()
{
return
_lastRoute
;
};
}
return
function
LocationRouterFactory
()
{
LocationRouterConstructor
.
prototype
=
new
Router
();
return
new
LocationRouterConstructor
();
};
});
examples/olives/bower_components/olives/src/Bind.plugin.js
View file @
9df98e41
...
...
@@ -13,375 +13,406 @@ define(["Store", "Observable", "Tools", "DomUtils"],
*/
function
BindPlugin
(
Store
,
Observable
,
Tools
,
DomUtils
)
{
return
function
BindPluginConstructor
(
$model
,
$bindings
)
{
/**
* The model to watch
* @private
*/
var
_model
=
null
,
/**
* The list of custom bindings
* @private
*/
_bindings
=
{},
/**
* The list of itemRenderers
* each foreach has its itemRenderer
* @private
*/
_itemRenderers
=
{};
/**
* The observers handlers
* for debugging only
* @private
*/
this
.
observers
=
{};
/**
* Define the model to watch for
* @param {Store} model the model to watch for changes
* @returns {Boolean} true if the model was set
*/
this
.
setModel
=
function
setModel
(
model
)
{
if
(
model
instanceof
Store
)
{
// Set the model
_model
=
model
;
return
true
;
}
else
{
return
false
;
}
};
/**
* Get the store that is watched for
* for debugging only
* @private
* @returns the Store
*/
this
.
getModel
=
function
getModel
()
{
return
_model
;
};
/**
* The item renderer defines a dom node that can be duplicated
* It is made available for debugging purpose, don't use it
* @private
*/
this
.
ItemRenderer
=
function
ItemRenderer
(
$plugins
,
$rootNode
)
{
/**
* The node that will be cloned
* @private
*/
var
_node
=
null
,
/**
* The object that contains plugins.name and plugins.apply
* @private
*/
_plugins
=
null
,
/**
* The _rootNode where to append the created items
* @private
*/
_rootNode
=
null
,
/**
* The lower boundary
* @private
*/
_start
=
null
,
/**
* The number of item to display
* @private
*/
_nb
=
null
;
/**
* Set the duplicated node
* @private
*/
this
.
setRenderer
=
function
setRenderer
(
node
)
{
_node
=
node
;
return
true
;
};
/**
* Returns the node that is going to be used for rendering
* @private
* @returns the node that is duplicated
*/
this
.
getRenderer
=
function
getRenderer
()
{
return
_node
;
};
/**
* Sets the rootNode and gets the node to copy
* @private
* @param {HTMLElement|SVGElement} rootNode
* @returns
*/
this
.
setRootNode
=
function
setRootNode
(
rootNode
)
{
var
renderer
;
if
(
DomUtils
.
isAcceptedType
(
rootNode
))
{
_rootNode
=
rootNode
;
renderer
=
_rootNode
.
querySelector
(
"
*
"
);
this
.
setRenderer
(
renderer
);
renderer
&&
_rootNode
.
removeChild
(
renderer
);
return
true
;
}
else
{
return
false
;
}
};
/**
* Gets the rootNode
* @private
* @returns _rootNode
*/
this
.
getRootNode
=
function
getRootNode
()
{
return
_rootNode
;
};
/**
* Set the plugins objet that contains the name and the apply function
* @private
* @param plugins
* @returns true
*/
this
.
setPlugins
=
function
setPlugins
(
plugins
)
{
_plugins
=
plugins
;
return
true
;
};
/**
* Get the plugins object
* @private
* @returns the plugins object
*/
this
.
getPlugins
=
function
getPlugins
()
{
return
_plugins
;
};
/**
* The nodes created from the items are stored here
* @private
*/
this
.
items
=
new
Store
([]);
/**
* Set the start limit
* @private
* @param {Number} start the value to start rendering the items from
* @returns the value
*/
this
.
setStart
=
function
setStart
(
start
)
{
return
_start
=
parseInt
(
start
,
10
);
};
/**
* Get the start value
* @private
* @returns the start value
*/
this
.
getStart
=
function
getStart
()
{
return
_start
;
};
/**
* Set the number of item to display
* @private
* @param {Number/String} nb the number of item to display or "*" for all
* @returns the value
*/
this
.
setNb
=
function
setNb
(
nb
)
{
return
_nb
=
nb
==
"
*
"
?
nb
:
parseInt
(
nb
,
10
);
};
/**
* Get the number of item to display
* @private
* @returns the value
*/
this
.
getNb
=
function
getNb
()
{
return
_nb
;
};
/**
* Adds a new item and adds it in the items list
* @private
* @param {Number} id the id of the item
* @returns
*/
this
.
addItem
=
function
addItem
(
id
)
{
var
node
,
next
;
if
(
typeof
id
==
"
number
"
&&
!
this
.
items
.
get
(
id
))
{
node
=
this
.
create
(
id
);
if
(
node
)
{
// IE (until 9) apparently fails to appendChild when insertBefore's second argument is null, hence this.
next
=
this
.
getNextItem
(
id
);
next
?
_rootNode
.
insertBefore
(
node
,
next
)
:
_rootNode
.
appendChild
(
node
);
return
true
;
}
else
{
return
false
;
}
}
else
{
return
false
;
}
};
/**
* Get the next item in the item store given an id.
* @private
* @param {Number} id the id to start from
* @returns
*/
this
.
getNextItem
=
function
getNextItem
(
id
)
{
return
this
.
items
.
alter
(
"
slice
"
,
id
+
1
).
filter
(
function
(
value
)
{
if
(
DomUtils
.
isAcceptedType
(
value
))
{
return
true
;
}
})[
0
];
};
/**
* Remove an item from the dom and the items list
* @private
* @param {Number} id the id of the item to remove
* @returns
*/
this
.
removeItem
=
function
removeItem
(
id
)
{
var
item
=
this
.
items
.
get
(
id
);
if
(
item
)
{
_rootNode
.
removeChild
(
item
);
this
.
items
.
set
(
id
);
return
true
;
}
else
{
return
false
;
}
};
/**
* create a new node. Actually makes a clone of the initial one
* and adds pluginname_id to each node, then calls plugins.apply to apply all plugins
* @private
* @param id
* @param pluginName
* @returns the associated node
*/
this
.
create
=
function
create
(
id
)
{
if
(
_model
.
has
(
id
))
{
var
newNode
=
_node
.
cloneNode
(
true
),
nodes
=
DomUtils
.
getNodes
(
newNode
);
Tools
.
toArray
(
nodes
).
forEach
(
function
(
child
)
{
child
.
setAttribute
(
"
data-
"
+
_plugins
.
name
+
"
_id
"
,
id
);
});
this
.
items
.
set
(
id
,
newNode
);
_plugins
.
apply
(
newNode
);
return
newNode
;
}
};
/**
* Renders the dom tree, adds nodes that are in the boundaries
* and removes the others
* @private
* @returns true boundaries are set
*/
this
.
render
=
function
render
()
{
// If the number of items to render is all (*)
// Then get the number of items
var
_tmpNb
=
_nb
==
"
*
"
?
_model
.
getNbItems
()
:
_nb
;
// This will store the items to remove
var
marked
=
[];
// Render only if boundaries have been set
if
(
_nb
!==
null
&&
_start
!==
null
)
{
// Loop through the existing items
this
.
items
.
loop
(
function
(
value
,
idx
)
{
// If an item is out of the boundary
if
(
idx
<
_start
||
idx
>=
(
_start
+
_tmpNb
)
||
!
_model
.
has
(
idx
))
{
// Mark it
marked
.
push
(
idx
);
}
},
this
);
// Remove the marked item from the highest id to the lowest
// Doing this will avoid the id change during removal
// (removing id 2 will make id 3 becoming 2)
marked
.
sort
(
Tools
.
compareNumbers
).
reverse
().
forEach
(
this
.
removeItem
,
this
);
// Now that we have removed the old nodes
// Add the missing one
for
(
var
i
=
_start
,
l
=
_tmpNb
+
_start
;
i
<
l
;
i
++
)
{
this
.
addItem
(
i
);
}
return
true
;
}
else
{
return
false
;
}
};
this
.
setPlugins
(
$plugins
);
this
.
setRootNode
(
$rootNode
);
};
/**
* Save an itemRenderer according to its id
* @private
* @param {String} id the id of the itemRenderer
* @param {ItemRenderer} itemRenderer an itemRenderer object
*/
this
.
setItemRenderer
=
function
setItemRenderer
(
id
,
itemRenderer
)
{
id
=
id
||
"
default
"
;
_itemRenderers
[
id
]
=
itemRenderer
;
};
/**
* Get an itemRenderer
* @private
* @param {String} id the name of the itemRenderer
* @returns the itemRenderer
*/
this
.
getItemRenderer
=
function
getItemRenderer
(
id
)
{
return
_itemRenderers
[
id
];
};
/**
* Expands the inner dom nodes of a given dom node, filling it with model's values
* @param {HTMLElement|SVGElement} node the dom node to apply foreach to
*/
this
.
foreach
=
function
foreach
(
node
,
idItemRenderer
,
start
,
nb
)
{
var
itemRenderer
=
new
this
.
ItemRenderer
(
this
.
plugins
,
node
);
itemRenderer
.
setStart
(
start
||
0
);
itemRenderer
.
setNb
(
nb
||
"
*
"
);
itemRenderer
.
render
();
// Add the newly created item
"
use strict
"
;
return
function
BindPluginConstructor
(
$model
,
$bindings
)
{
/**
* The model to watch
* @private
*/
var
_model
=
null
,
/**
* The list of custom bindings
* @private
*/
_bindings
=
{},
/**
* The list of itemRenderers
* each foreach has its itemRenderer
* @private
*/
_itemRenderers
=
{},
/**
* The observers handlers
* @private
*/
_observers
=
{};
/**
* Exposed for debugging purpose
* @private
*/
this
.
observers
=
_observers
;
function
_removeObserversForId
(
id
)
{
if
(
_observers
[
id
])
{
_observers
[
id
].
forEach
(
function
(
handler
)
{
_model
.
unwatchValue
(
handler
);
});
delete
_observers
[
id
];
}
}
/**
* Define the model to watch for
* @param {Store} model the model to watch for changes
* @returns {Boolean} true if the model was set
*/
this
.
setModel
=
function
setModel
(
model
)
{
if
(
model
instanceof
Store
)
{
// Set the model
_model
=
model
;
return
true
;
}
else
{
return
false
;
}
};
/**
* Get the store that is watched for
* for debugging only
* @private
* @returns the Store
*/
this
.
getModel
=
function
getModel
()
{
return
_model
;
};
/**
* The item renderer defines a dom node that can be duplicated
* It is made available for debugging purpose, don't use it
* @private
*/
this
.
ItemRenderer
=
function
ItemRenderer
(
$plugins
,
$rootNode
)
{
/**
* The node that will be cloned
* @private
*/
var
_node
=
null
,
/**
* The object that contains plugins.name and plugins.apply
* @private
*/
_plugins
=
null
,
/**
* The _rootNode where to append the created items
* @private
*/
_rootNode
=
null
,
/**
* The lower boundary
* @private
*/
_start
=
null
,
/**
* The number of item to display
* @private
*/
_nb
=
null
;
/**
* Set the duplicated node
* @private
*/
this
.
setRenderer
=
function
setRenderer
(
node
)
{
_node
=
node
;
return
true
;
};
/**
* Returns the node that is going to be used for rendering
* @private
* @returns the node that is duplicated
*/
this
.
getRenderer
=
function
getRenderer
()
{
return
_node
;
};
/**
* Sets the rootNode and gets the node to copy
* @private
* @param {HTMLElement|SVGElement} rootNode
* @returns
*/
this
.
setRootNode
=
function
setRootNode
(
rootNode
)
{
var
renderer
;
if
(
DomUtils
.
isAcceptedType
(
rootNode
))
{
_rootNode
=
rootNode
;
renderer
=
_rootNode
.
querySelector
(
"
*
"
);
this
.
setRenderer
(
renderer
);
if
(
renderer
)
{
_rootNode
.
removeChild
(
renderer
);
}
return
true
;
}
else
{
return
false
;
}
};
/**
* Gets the rootNode
* @private
* @returns _rootNode
*/
this
.
getRootNode
=
function
getRootNode
()
{
return
_rootNode
;
};
/**
* Set the plugins objet that contains the name and the apply function
* @private
* @param plugins
* @returns true
*/
this
.
setPlugins
=
function
setPlugins
(
plugins
)
{
_plugins
=
plugins
;
return
true
;
};
/**
* Get the plugins object
* @private
* @returns the plugins object
*/
this
.
getPlugins
=
function
getPlugins
()
{
return
_plugins
;
};
/**
* The nodes created from the items are stored here
* @private
*/
this
.
items
=
{};
/**
* Set the start limit
* @private
* @param {Number} start the value to start rendering the items from
* @returns the value
*/
this
.
setStart
=
function
setStart
(
start
)
{
_start
=
parseInt
(
start
,
10
);
return
_start
;
};
/**
* Get the start value
* @private
* @returns the start value
*/
this
.
getStart
=
function
getStart
()
{
return
_start
;
};
/**
* Set the number of item to display
* @private
* @param {Number/String} nb the number of item to display or "*" for all
* @returns the value
*/
this
.
setNb
=
function
setNb
(
nb
)
{
_nb
=
nb
==
"
*
"
?
nb
:
parseInt
(
nb
,
10
);
return
_nb
;
};
/**
* Get the number of item to display
* @private
* @returns the value
*/
this
.
getNb
=
function
getNb
()
{
return
_nb
;
};
/**
* Adds a new item and adds it in the items list
* @private
* @param {Number} id the id of the item
* @returns
*/
this
.
addItem
=
function
addItem
(
id
)
{
var
node
,
next
;
if
(
typeof
id
==
"
number
"
&&
!
this
.
items
[
id
])
{
next
=
this
.
getNextItem
(
id
);
node
=
this
.
create
(
id
);
if
(
node
)
{
// IE (until 9) apparently fails to appendChild when insertBefore's second argument is null, hence this.
if
(
next
)
{
_rootNode
.
insertBefore
(
node
,
next
);
}
else
{
_rootNode
.
appendChild
(
node
);
}
return
true
;
}
else
{
return
false
;
}
}
else
{
return
false
;
}
};
/**
* Get the next item in the item store given an id.
* @private
* @param {Number} id the id to start from
* @returns
*/
this
.
getNextItem
=
function
getNextItem
(
id
)
{
var
keys
=
Object
.
keys
(
this
.
items
).
map
(
function
(
string
)
{
return
Number
(
string
);
}),
closest
=
Tools
.
closestGreater
(
id
,
keys
),
closestId
=
keys
[
closest
];
// Only return if different
if
(
closestId
!=
id
)
{
return
this
.
items
[
closestId
];
}
else
{
return
;
}
};
/**
* Remove an item from the dom and the items list
* @private
* @param {Number} id the id of the item to remove
* @returns
*/
this
.
removeItem
=
function
removeItem
(
id
)
{
var
item
=
this
.
items
[
id
];
if
(
item
)
{
_rootNode
.
removeChild
(
item
);
delete
this
.
items
[
id
];
_removeObserversForId
(
id
);
return
true
;
}
else
{
return
false
;
}
};
/**
* create a new node. Actually makes a clone of the initial one
* and adds pluginname_id to each node, then calls plugins.apply to apply all plugins
* @private
* @param id
* @param pluginName
* @returns the associated node
*/
this
.
create
=
function
create
(
id
)
{
if
(
_model
.
has
(
id
))
{
var
newNode
=
_node
.
cloneNode
(
true
),
nodes
=
DomUtils
.
getNodes
(
newNode
);
Tools
.
toArray
(
nodes
).
forEach
(
function
(
child
)
{
child
.
setAttribute
(
"
data-
"
+
_plugins
.
name
+
"
_id
"
,
id
);
});
this
.
items
[
id
]
=
newNode
;
_plugins
.
apply
(
newNode
);
return
newNode
;
}
};
/**
* Renders the dom tree, adds nodes that are in the boundaries
* and removes the others
* @private
* @returns true boundaries are set
*/
this
.
render
=
function
render
()
{
// If the number of items to render is all (*)
// Then get the number of items
var
_tmpNb
=
_nb
==
"
*
"
?
_model
.
getNbItems
()
:
_nb
;
// This will store the items to remove
var
marked
=
[];
// Render only if boundaries have been set
if
(
_nb
!==
null
&&
_start
!==
null
)
{
// Loop through the existing items
Tools
.
loop
(
this
.
items
,
function
(
value
,
idx
)
{
// If an item is out of the boundary
idx
=
Number
(
idx
);
if
(
idx
<
_start
||
idx
>=
(
_start
+
_tmpNb
)
||
!
_model
.
has
(
idx
))
{
// Mark it
marked
.
push
(
idx
);
}
},
this
);
// Remove the marked item from the highest id to the lowest
// Doing this will avoid the id change during removal
// (removing id 2 will make id 3 becoming 2)
marked
.
sort
(
Tools
.
compareNumbers
).
reverse
().
forEach
(
this
.
removeItem
,
this
);
// Now that we have removed the old nodes
// Add the missing one
for
(
var
i
=
_start
,
l
=
_tmpNb
+
_start
;
i
<
l
;
i
++
)
{
this
.
addItem
(
i
);
}
return
true
;
}
else
{
return
false
;
}
};
this
.
setPlugins
(
$plugins
);
this
.
setRootNode
(
$rootNode
);
};
/**
* Save an itemRenderer according to its id
* @private
* @param {String} id the id of the itemRenderer
* @param {ItemRenderer} itemRenderer an itemRenderer object
*/
this
.
setItemRenderer
=
function
setItemRenderer
(
id
,
itemRenderer
)
{
id
=
id
||
"
default
"
;
_itemRenderers
[
id
]
=
itemRenderer
;
};
/**
* Get an itemRenderer
* @private
* @param {String} id the name of the itemRenderer
* @returns the itemRenderer
*/
this
.
getItemRenderer
=
function
getItemRenderer
(
id
)
{
return
_itemRenderers
[
id
];
};
/**
* Expands the inner dom nodes of a given dom node, filling it with model's values
* @param {HTMLElement|SVGElement} node the dom node to apply foreach to
*/
this
.
foreach
=
function
foreach
(
node
,
idItemRenderer
,
start
,
nb
)
{
var
itemRenderer
=
new
this
.
ItemRenderer
(
this
.
plugins
,
node
);
itemRenderer
.
setStart
(
start
||
0
);
itemRenderer
.
setNb
(
nb
||
"
*
"
);
itemRenderer
.
render
();
// Add the newly created item
_model
.
watch
(
"
added
"
,
itemRenderer
.
render
,
itemRenderer
);
// If an item is deleted
// If an item is deleted
_model
.
watch
(
"
deleted
"
,
function
(
idx
)
{
itemRenderer
.
render
();
itemRenderer
.
render
();
// Also remove all observers
this
.
observers
[
idx
]
&&
this
.
observers
[
idx
].
forEach
(
function
(
handler
)
{
_model
.
unwatchValue
(
handler
);
},
this
);
delete
this
.
observers
[
idx
];
_removeObserversForId
(
idx
);
},
this
);
this
.
setItemRenderer
(
idItemRenderer
,
itemRenderer
);
...
...
@@ -394,13 +425,13 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
* @returns true if the foreach exists
*/
this
.
updateStart
=
function
updateStart
(
id
,
start
)
{
var
itemRenderer
=
this
.
getItemRenderer
(
id
);
if
(
itemRenderer
)
{
itemRenderer
.
setStart
(
start
);
return
true
;
}
else
{
return
false
;
}
var
itemRenderer
=
this
.
getItemRenderer
(
id
);
if
(
itemRenderer
)
{
itemRenderer
.
setStart
(
start
);
return
true
;
}
else
{
return
false
;
}
};
/**
...
...
@@ -410,13 +441,13 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
* @returns true if the foreach exists
*/
this
.
updateNb
=
function
updateNb
(
id
,
nb
)
{
var
itemRenderer
=
this
.
getItemRenderer
(
id
);
if
(
itemRenderer
)
{
itemRenderer
.
setNb
(
nb
);
return
true
;
}
else
{
return
false
;
}
var
itemRenderer
=
this
.
getItemRenderer
(
id
);
if
(
itemRenderer
)
{
itemRenderer
.
setNb
(
nb
);
return
true
;
}
else
{
return
false
;
}
};
/**
...
...
@@ -425,201 +456,201 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
* @returns true if the foreach exists
*/
this
.
refresh
=
function
refresh
(
id
)
{
var
itemRenderer
=
this
.
getItemRenderer
(
id
);
if
(
itemRenderer
)
{
itemRenderer
.
render
();
return
true
;
}
else
{
return
false
;
}
var
itemRenderer
=
this
.
getItemRenderer
(
id
);
if
(
itemRenderer
)
{
itemRenderer
.
render
();
return
true
;
}
else
{
return
false
;
}
};
/**
* Both ways binding between a dom node attributes and the model
* @param {HTMLElement|SVGElement} node the dom node to apply the plugin to
* @param {String} name the name of the property to look for in the model's value
* @returns
*/
this
.
bind
=
function
bind
(
node
,
property
,
name
)
{
// Name can be unset if the value of a row is plain text
name
=
name
||
""
;
// In case of an array-like model the id is the index of the model's item to look for.
// The _id is added by the foreach function
var
id
=
node
.
getAttribute
(
"
data-
"
+
this
.
plugins
.
name
+
"
_id
"
),
// Else, it is the first element of the following
split
=
name
.
split
(
"
.
"
),
// So the index of the model is either id or the first element of split
modelIdx
=
id
||
split
.
shift
(),
// And the name of the property to look for in the value is
prop
=
id
?
name
:
split
.
join
(
"
.
"
),
// Get the model's value
get
=
Tools
.
getNestedProperty
(
_model
.
get
(
modelIdx
),
prop
),
// When calling bind like bind:newBinding,param1, param2... we need to get them
extraParam
=
Tools
.
toArray
(
arguments
).
slice
(
3
);
// 0 and false are acceptable falsy values
if
(
get
||
get
===
0
||
get
===
false
)
{
// If the binding hasn't been overriden
if
(
!
this
.
execBinding
.
apply
(
this
,
[
node
,
property
,
get
]
// Extra params are passed to the new binding too
.
concat
(
extraParam
)))
{
// Execute the default one which is a simple assignation
//node[property] = get;
DomUtils
.
setAttribute
(
node
,
property
,
get
);
}
}
// Only watch for changes (double way data binding) if the binding
// has not been redefined
if
(
!
this
.
hasBinding
(
property
))
{
node
.
addEventListener
(
"
change
"
,
function
(
event
)
{
if
(
_model
.
has
(
modelIdx
))
{
if
(
prop
)
{
_model
.
update
(
modelIdx
,
name
,
node
[
property
]);
}
else
{
_model
.
set
(
modelIdx
,
node
[
property
]);
}
}
},
true
);
}
// Watch for changes
this
.
observers
[
modelIdx
]
=
this
.
observers
[
modelIdx
]
||
[];
this
.
observers
[
modelIdx
].
push
(
_model
.
watchValue
(
modelIdx
,
function
(
value
)
{
if
(
!
this
.
execBinding
.
apply
(
this
,
[
node
,
property
,
Tools
.
getNestedProperty
(
value
,
prop
)]
// passing extra params too
.
concat
(
extraParam
)))
{
//node[property] = Tools.getNestedProperty(value, prop);
DomUtils
.
setAttribute
(
node
,
property
,
Tools
.
getNestedProperty
(
value
,
prop
));
}
},
this
));
};
/**
* Set the node's value into the model, the name is the model's property
* @private
* @param {HTMLElement|SVGElement} node
* @returns true if the property is added
*/
this
.
set
=
function
set
(
node
)
{
if
(
DomUtils
.
isAcceptedType
(
node
)
&&
node
.
name
)
{
_model
.
set
(
node
.
name
,
node
.
value
);
return
true
;
}
else
{
return
false
;
}
};
this
.
getItemIndex
=
function
getElementId
(
dom
)
{
var
dataset
=
DomUtils
.
getDataset
(
dom
);
if
(
dataset
&&
typeof
dataset
[
this
.
plugins
.
name
+
"
_id
"
]
!=
"
undefined
"
)
{
return
+
dataset
[
this
.
plugins
.
name
+
"
_id
"
];
}
else
{
return
false
;
}
};
/**
* Prevents the submit and set the model with all form's inputs
* @param {HTMLFormElement} for
m
* @returns true if valid form
*/
this
.
form
=
function
form
(
form
)
{
if
(
form
&&
form
.
nodeName
==
"
FORM
"
)
{
var
that
=
this
;
form
.
addEventListener
(
"
submit
"
,
function
(
event
)
{
Tools
.
toArray
(
form
.
querySelectorAll
(
"
[name]
"
)).
forEach
(
that
.
set
,
that
);
event
.
preventDefault
();
},
true
);
return
true
;
}
else
{
return
false
;
}
};
/**
* Add a new way to handle a binding
* @param {String} name of the binding
* @param {Function} binding the function to handle the binding
* @returns
*/
this
.
addBinding
=
function
addBinding
(
name
,
binding
)
{
if
(
name
&&
typeof
name
==
"
string
"
&&
typeof
binding
==
"
function
"
)
{
_bindings
[
name
]
=
binding
;
return
true
;
}
else
{
return
false
;
}
};
/**
* Execute a binding
* Only used by the plugin
* @private
* @param {HTMLElement} node the dom node on which to execute the binding
* @param {String} name the name of the binding
* @param {Any type} value the value to pass to the function
* @returns
*/
this
.
execBinding
=
function
execBinding
(
node
,
name
)
{
if
(
this
.
hasBinding
(
name
))
{
_bindings
[
name
].
apply
(
node
,
Array
.
prototype
.
slice
.
call
(
arguments
,
2
));
return
true
;
}
else
{
return
false
;
}
};
/**
* Check if the binding exists
* @private
* @param {String} name the name of the binding
* @returns
*/
this
.
hasBinding
=
function
hasBinding
(
name
)
{
return
_bindings
.
hasOwnProperty
(
name
);
};
/**
* Get a binding
* For debugging only
* @private
* @param {String} name the name of the binding
* @returns
*/
this
.
getBinding
=
function
getBinding
(
name
)
{
return
_bindings
[
name
];
};
/**
* Add multiple binding at once
* @param {Object} list the list of bindings to add
* @returns
*/
this
.
addBindings
=
function
addBindings
(
list
)
{
return
Tools
.
loop
(
list
,
function
(
binding
,
name
)
{
this
.
addBinding
(
name
,
binding
);
},
this
);
};
// Inits the model
this
.
setModel
(
$model
);
// Inits bindings
this
.
addBindings
(
$bindings
);
};
/**
* Both ways binding between a dom node attributes and the model
* @param {HTMLElement|SVGElement} node the dom node to apply the plugin to
* @param {String} name the name of the property to look for in the model's value
* @returns
*/
this
.
bind
=
function
bind
(
node
,
property
,
name
)
{
// Name can be unset if the value of a row is plain text
name
=
name
||
""
;
// In case of an array-like model the id is the index of the model's item to look for.
// The _id is added by the foreach function
var
id
=
node
.
getAttribute
(
"
data-
"
+
this
.
plugins
.
name
+
"
_id
"
),
// Else, it is the first element of the following
split
=
name
.
split
(
"
.
"
),
// So the index of the model is either id or the first element of split
modelIdx
=
id
||
split
.
shift
(),
// And the name of the property to look for in the value is
prop
=
id
?
name
:
split
.
join
(
"
.
"
),
// Get the model's value
get
=
Tools
.
getNestedProperty
(
_model
.
get
(
modelIdx
),
prop
),
// When calling bind like bind:newBinding,param1, param2... we need to get them
extraParam
=
Tools
.
toArray
(
arguments
).
slice
(
3
);
// 0 and false are acceptable falsy values
if
(
get
||
get
===
0
||
get
===
false
)
{
// If the binding hasn't been overriden
if
(
!
this
.
execBinding
.
apply
(
this
,
[
node
,
property
,
get
]
// Extra params are passed to the new binding too
.
concat
(
extraParam
)))
{
// Execute the default one which is a simple assignation
//node[property] = get;
DomUtils
.
setAttribute
(
node
,
property
,
get
);
}
}
// Only watch for changes (double way data binding) if the binding
// has not been redefined
if
(
!
this
.
hasBinding
(
property
))
{
node
.
addEventListener
(
"
change
"
,
function
(
event
)
{
if
(
_model
.
has
(
modelIdx
))
{
if
(
prop
)
{
_model
.
update
(
modelIdx
,
name
,
node
[
property
]);
}
else
{
_model
.
set
(
modelIdx
,
node
[
property
]);
}
}
},
true
);
}
// Watch for changes
this
.
observers
[
modelIdx
]
=
this
.
observers
[
modelIdx
]
||
[];
this
.
observers
[
modelIdx
].
push
(
_model
.
watchValue
(
modelIdx
,
function
(
value
)
{
if
(
!
this
.
execBinding
.
apply
(
this
,
[
node
,
property
,
Tools
.
getNestedProperty
(
value
,
prop
)]
// passing extra params too
.
concat
(
extraParam
)))
{
//node[property] = Tools.getNestedProperty(value, prop);
DomUtils
.
setAttribute
(
node
,
property
,
Tools
.
getNestedProperty
(
value
,
prop
));
}
},
this
));
};
/**
* Set the node's value into the model, the name is the model's property
* @private
* @param {HTMLElement|SVGElement} node
* @returns true if the property is added
*/
this
.
set
=
function
set
(
node
)
{
if
(
DomUtils
.
isAcceptedType
(
node
)
&&
node
.
name
)
{
_model
.
set
(
node
.
name
,
node
.
value
);
return
true
;
}
else
{
return
false
;
}
};
this
.
getItemIndex
=
function
getElementId
(
dom
)
{
var
dataset
=
DomUtils
.
getDataset
(
dom
);
if
(
dataset
&&
typeof
dataset
[
this
.
plugins
.
name
+
"
_id
"
]
!=
"
undefined
"
)
{
return
+
dataset
[
this
.
plugins
.
name
+
"
_id
"
];
}
else
{
return
false
;
}
};
/**
* Prevents the submit and set the model with all form's inputs
* @param {HTMLFormElement} DOMfro
m
* @returns true if valid form
*/
this
.
form
=
function
form
(
DOM
form
)
{
if
(
DOMform
&&
DOM
form
.
nodeName
==
"
FORM
"
)
{
var
that
=
this
;
DOM
form
.
addEventListener
(
"
submit
"
,
function
(
event
)
{
Tools
.
toArray
(
DOM
form
.
querySelectorAll
(
"
[name]
"
)).
forEach
(
that
.
set
,
that
);
event
.
preventDefault
();
},
true
);
return
true
;
}
else
{
return
false
;
}
};
/**
* Add a new way to handle a binding
* @param {String} name of the binding
* @param {Function} binding the function to handle the binding
* @returns
*/
this
.
addBinding
=
function
addBinding
(
name
,
binding
)
{
if
(
name
&&
typeof
name
==
"
string
"
&&
typeof
binding
==
"
function
"
)
{
_bindings
[
name
]
=
binding
;
return
true
;
}
else
{
return
false
;
}
};
/**
* Execute a binding
* Only used by the plugin
* @private
* @param {HTMLElement} node the dom node on which to execute the binding
* @param {String} name the name of the binding
* @param {Any type} value the value to pass to the function
* @returns
*/
this
.
execBinding
=
function
execBinding
(
node
,
name
)
{
if
(
this
.
hasBinding
(
name
))
{
_bindings
[
name
].
apply
(
node
,
Array
.
prototype
.
slice
.
call
(
arguments
,
2
));
return
true
;
}
else
{
return
false
;
}
};
/**
* Check if the binding exists
* @private
* @param {String} name the name of the binding
* @returns
*/
this
.
hasBinding
=
function
hasBinding
(
name
)
{
return
_bindings
.
hasOwnProperty
(
name
);
};
/**
* Get a binding
* For debugging only
* @private
* @param {String} name the name of the binding
* @returns
*/
this
.
getBinding
=
function
getBinding
(
name
)
{
return
_bindings
[
name
];
};
/**
* Add multiple binding at once
* @param {Object} list the list of bindings to add
* @returns
*/
this
.
addBindings
=
function
addBindings
(
list
)
{
return
Tools
.
loop
(
list
,
function
(
binding
,
name
)
{
this
.
addBinding
(
name
,
binding
);
},
this
);
};
// Inits the model
this
.
setModel
(
$model
);
// Inits bindings
this
.
addBindings
(
$bindings
);
};
});
examples/olives/bower_components/olives/src/DomUtils.js
View file @
9df98e41
...
...
@@ -6,6 +6,8 @@
define
([
"
Tools
"
],
function
(
Tools
)
{
"
use strict
"
;
return
{
/**
* Returns a NodeList including the given dom node,
...
...
examples/olives/bower_components/olives/src/Event.plugin.js
View file @
9df98e41
...
...
@@ -14,121 +14,123 @@ define(["DomUtils"],
*/
function
EventPlugin
(
Utils
)
{
/**
* The event plugin constructor.
* ex: new EventPlugin({method: function(){} ...}, false);
* @param {Object} the object that has the event handling methods
* @param {Boolean} $isMobile if the event handler has to map with touch events
*/
return
function
EventPluginConstructor
(
$parent
,
$isMobile
)
{
/**
* The parent callback
* @private
*/
var
_parent
=
null
,
/**
* The mapping object.
* @private
*/
_map
=
{
"
mousedown
"
:
"
touchstart
"
,
"
mouseup
"
:
"
touchend
"
,
"
mousemove
"
:
"
touchmove
"
},
/**
* Is touch device.
* @private
*/
_isMobile
=
!!
$isMobile
;
/**
* Add mapped event listener (for testing purpose).
* @private
*/
this
.
addEventListener
=
function
addEventListener
(
node
,
event
,
callback
,
useCapture
)
{
node
.
addEventListener
(
this
.
map
(
event
),
callback
,
!!
useCapture
);
};
/**
* Listen to DOM events.
* @param {Object} node DOM node
* @param {String} name event's name
* @param {String} listener callback's name
* @param {String} useCapture string
*/
this
.
listen
=
function
listen
(
node
,
name
,
listener
,
useCapture
)
{
this
.
addEventListener
(
node
,
name
,
function
(
e
){
_parent
[
listener
].
call
(
_parent
,
e
,
node
);
},
!!
useCapture
);
};
/**
* Delegate the event handling to a parent DOM element
* @param {Object} node DOM node
* @param {String} selector CSS3 selector to the element that listens to the event
* @param {String} name event's name
* @param {String} listener callback's name
* @param {String} useCapture string
*/
this
.
delegate
=
function
delegate
(
node
,
selector
,
name
,
listener
,
useCapture
)
{
this
.
addEventListener
(
node
,
name
,
function
(
event
){
if
(
Utils
.
matches
(
node
,
selector
,
event
.
target
))
{
_parent
[
listener
].
call
(
_parent
,
event
,
node
);
}
},
!!
useCapture
);
};
/**
* Get the parent object.
* @return {Object} the parent object
*/
this
.
getParent
=
function
getParent
()
{
return
_parent
;
};
/**
* Set the parent object.
* The parent object is an object which the functions are called by node listeners.
* @param {Object} the parent object
* @return true if object has been set
*/
this
.
setParent
=
function
setParent
(
parent
)
{
if
(
parent
instanceof
Object
){
_parent
=
parent
;
return
true
;
}
return
false
;
};
/**
* Get event mapping.
* @param {String} event's name
* @return the mapped event's name
*/
this
.
map
=
function
map
(
name
)
{
return
_isMobile
?
(
_map
[
name
]
||
name
)
:
name
;
};
/**
* Set event mapping.
* @param {String} event's name
* @param {String} event's value
* @return true if mapped
*/
this
.
setMap
=
function
setMap
(
name
,
value
)
{
if
(
typeof
name
==
"
string
"
&&
typeof
value
==
"
string
"
)
{
_map
[
name
]
=
value
;
return
true
;
}
return
false
;
};
//init
this
.
setParent
(
$parent
);
};
"
use strict
"
;
/**
* The event plugin constructor.
* ex: new EventPlugin({method: function(){} ...}, false);
* @param {Object} the object that has the event handling methods
* @param {Boolean} $isMobile if the event handler has to map with touch events
*/
return
function
EventPluginConstructor
(
$parent
,
$isMobile
)
{
/**
* The parent callback
* @private
*/
var
_parent
=
null
,
/**
* The mapping object.
* @private
*/
_map
=
{
"
mousedown
"
:
"
touchstart
"
,
"
mouseup
"
:
"
touchend
"
,
"
mousemove
"
:
"
touchmove
"
},
/**
* Is touch device.
* @private
*/
_isMobile
=
!!
$isMobile
;
/**
* Add mapped event listener (for testing purpose).
* @private
*/
this
.
addEventListener
=
function
addEventListener
(
node
,
event
,
callback
,
useCapture
)
{
node
.
addEventListener
(
this
.
map
(
event
),
callback
,
!!
useCapture
);
};
/**
* Listen to DOM events.
* @param {Object} node DOM node
* @param {String} name event's name
* @param {String} listener callback's name
* @param {String} useCapture string
*/
this
.
listen
=
function
listen
(
node
,
name
,
listener
,
useCapture
)
{
this
.
addEventListener
(
node
,
name
,
function
(
e
){
_parent
[
listener
].
call
(
_parent
,
e
,
node
);
},
!!
useCapture
);
};
/**
* Delegate the event handling to a parent DOM element
* @param {Object} node DOM node
* @param {String} selector CSS3 selector to the element that listens to the event
* @param {String} name event's name
* @param {String} listener callback's name
* @param {String} useCapture string
*/
this
.
delegate
=
function
delegate
(
node
,
selector
,
name
,
listener
,
useCapture
)
{
this
.
addEventListener
(
node
,
name
,
function
(
event
){
if
(
Utils
.
matches
(
node
,
selector
,
event
.
target
))
{
_parent
[
listener
].
call
(
_parent
,
event
,
node
);
}
},
!!
useCapture
);
};
/**
* Get the parent object.
* @return {Object} the parent object
*/
this
.
getParent
=
function
getParent
()
{
return
_parent
;
};
/**
* Set the parent object.
* The parent object is an object which the functions are called by node listeners.
* @param {Object} the parent object
* @return true if object has been set
*/
this
.
setParent
=
function
setParent
(
parent
)
{
if
(
parent
instanceof
Object
){
_parent
=
parent
;
return
true
;
}
return
false
;
};
/**
* Get event mapping.
* @param {String} event's name
* @return the mapped event's name
*/
this
.
map
=
function
map
(
name
)
{
return
_isMobile
?
(
_map
[
name
]
||
name
)
:
name
;
};
/**
* Set event mapping.
* @param {String} event's name
* @param {String} event's value
* @return true if mapped
*/
this
.
setMap
=
function
setMap
(
name
,
value
)
{
if
(
typeof
name
==
"
string
"
&&
typeof
value
==
"
string
"
)
{
_map
[
name
]
=
value
;
return
true
;
}
return
false
;
};
//init
this
.
setParent
(
$parent
);
};
});
examples/olives/bower_components/olives/src/LocalStore.js
View file @
9df98e41
...
...
@@ -15,6 +15,8 @@ define(["Store", "Tools"],
*/
function
LocalStore
(
Store
,
Tools
)
{
"
use strict
"
;
function
LocalStoreConstructor
()
{
/**
...
...
@@ -96,7 +98,7 @@ function LocalStore(Store, Tools) {
return
function
LocalStoreFactory
(
init
)
{
LocalStoreConstructor
.
prototype
=
new
Store
(
init
);
return
new
LocalStoreConstructor
;
return
new
LocalStoreConstructor
()
;
};
});
examples/olives/bower_components/olives/src/OObject.js
View file @
9df98e41
...
...
@@ -13,179 +13,185 @@ define(["StateMachine", "Store", "Plugins", "DomUtils", "Tools"],
*/
function
OObject
(
StateMachine
,
Store
,
Plugins
,
DomUtils
,
Tools
)
{
return
function
OObjectConstructor
(
otherStore
)
{
/**
* This function creates the dom of the UI from its template
* It then queries the dom for data- attributes
* It can't be executed if the template is not set
* @private
*/
var
render
=
function
render
(
UI
)
{
// The place where the template will be created
// is either the currentPlace where the node is placed
// or a temporary div
var
baseNode
=
_currentPlace
||
document
.
createElement
(
"
div
"
);
// If the template is set
if
(
UI
.
template
)
{
// In this function, the thisObject is the UI's prototype
// UI is the UI that has OObject as prototype
if
(
typeof
UI
.
template
==
"
string
"
)
{
// Let the browser do the parsing, can't be faster & easier.
baseNode
.
innerHTML
=
UI
.
template
.
trim
();
}
else
if
(
DomUtils
.
isAcceptedType
(
UI
.
template
))
{
// If it's already an HTML element
baseNode
.
appendChild
(
UI
.
template
);
}
// The UI must be placed in a unique dom node
// If not, there can't be multiple UIs placed in the same parentNode
// as it wouldn't be possible to know which node would belong to which UI
// This is probably a DOM limitation.
if
(
baseNode
.
childNodes
.
length
>
1
)
{
throw
Error
(
"
UI.template should have only one parent node
"
);
}
else
{
UI
.
dom
=
baseNode
.
childNodes
[
0
];
}
UI
.
plugins
.
apply
(
UI
.
dom
);
}
else
{
// An explicit message I hope
throw
Error
(
"
UI.template must be set prior to render
"
);
}
},
/**
* This function appends the dom tree to the given dom node.
* This dom node should be somewhere in the dom of the application
* @private
*/
place
=
function
place
(
UI
,
place
,
beforeNode
)
{
if
(
place
)
{
// IE (until 9) apparently fails to appendChild when insertBefore's second argument is null, hence this.
beforeNode
?
place
.
insertBefore
(
UI
.
dom
,
beforeNode
)
:
place
.
appendChild
(
UI
.
dom
);
// Also save the new place, so next renderings
// will be made inside it
_currentPlace
=
place
;
}
},
/**
* Does rendering & placing in one function
* @private
*/
renderNPlace
=
function
renderNPlace
(
UI
,
dom
)
{
render
(
UI
);
place
.
apply
(
null
,
Tools
.
toArray
(
arguments
));
},
/**
* This stores the current place
* If this is set, this is the place where new templates
* will be appended
* @private
*/
_currentPlace
=
null
,
/**
* The UI's stateMachine.
* Much better than if(stuff) do(stuff) else if (!stuff and stuff but not stouff) do (otherstuff)
* Please open an issue if you want to propose a better one
* @private
*/
_stateMachine
=
new
StateMachine
(
"
Init
"
,
{
"
Init
"
:
[[
"
render
"
,
render
,
this
,
"
Rendered
"
],
[
"
place
"
,
renderNPlace
,
this
,
"
Rendered
"
]],
"
Rendered
"
:
[[
"
place
"
,
place
,
this
],
[
"
render
"
,
render
,
this
]]
});
/**
* The UI's Store
* It has set/get/del/has/watch/unwatch methods
* @see Emily's doc for more info on how it works.
*/
this
.
model
=
otherStore
instanceof
Store
?
otherStore
:
new
Store
;
/**
* The module that will manage the plugins for this UI
* @see Olives/Plugins' doc for more info on how it works.
*/
this
.
plugins
=
new
Plugins
();
/**
* Describes the template, can either be like "<p></p>" or HTMLElements
* @type string or HTMLElement|SVGElement
*/
this
.
template
=
null
;
/**
* This will hold the dom nodes built from the template.
*/
this
.
dom
=
null
;
/**
* Place the UI in a given dom node
* @param node the node on which to append the UI
* @param beforeNode the dom before which to append the UI
*/
this
.
place
=
function
place
(
node
,
beforeNode
)
{
_stateMachine
.
event
(
"
place
"
,
this
,
node
,
beforeNode
);
};
/**
* Renders the template to dom nodes and applies the plugins on it
* It requires the template to be set first
*/
this
.
render
=
function
render
()
{
_stateMachine
.
event
(
"
render
"
,
this
);
};
/**
* Set the UI's template from a DOM element
* @param {HTMLElement|SVGElement} dom the dom element that'll become the template of the UI
* @returns true if dom is an HTMLElement|SVGElement
*/
this
.
setTemplateFromDom
=
function
setTemplateFromDom
(
dom
)
{
if
(
DomUtils
.
isAcceptedType
(
dom
))
{
this
.
template
=
dom
;
return
true
;
}
else
{
return
false
;
}
};
/**
* Transforms dom nodes into a UI.
* It basically does a setTemplateFromDOM, then a place
* It's a helper function
* @param {HTMLElement|SVGElement} node the dom to transform to a UI
* @returns true if dom is an HTMLElement|SVGElement
*/
this
.
alive
=
function
alive
(
dom
)
{
if
(
DomUtils
.
isAcceptedType
(
dom
))
{
this
.
setTemplateFromDom
(
dom
);
this
.
place
(
dom
.
parentNode
,
dom
.
nextElementSibling
);
return
true
;
}
else
{
return
false
;
}
};
/**
* Get the current dom node where the UI is placed.
* for debugging purpose
* @private
* @return {HTMLElement} node the dom where the UI is placed.
*/
this
.
getCurrentPlace
=
function
(){
return
_currentPlace
;
};
};
"
use strict
"
;
return
function
OObjectConstructor
(
otherStore
)
{
/**
* This function creates the dom of the UI from its template
* It then queries the dom for data- attributes
* It can't be executed if the template is not set
* @private
*/
var
render
=
function
render
(
UI
)
{
// The place where the template will be created
// is either the currentPlace where the node is placed
// or a temporary div
var
baseNode
=
_currentPlace
||
document
.
createElement
(
"
div
"
);
// If the template is set
if
(
UI
.
template
)
{
// In this function, the thisObject is the UI's prototype
// UI is the UI that has OObject as prototype
if
(
typeof
UI
.
template
==
"
string
"
)
{
// Let the browser do the parsing, can't be faster & easier.
baseNode
.
innerHTML
=
UI
.
template
.
trim
();
}
else
if
(
DomUtils
.
isAcceptedType
(
UI
.
template
))
{
// If it's already an HTML element
baseNode
.
appendChild
(
UI
.
template
);
}
// The UI must be placed in a unique dom node
// If not, there can't be multiple UIs placed in the same parentNode
// as it wouldn't be possible to know which node would belong to which UI
// This is probably a DOM limitation.
if
(
baseNode
.
childNodes
.
length
>
1
)
{
throw
new
Error
(
"
UI.template should have only one parent node
"
);
}
else
{
UI
.
dom
=
baseNode
.
childNodes
[
0
];
}
UI
.
plugins
.
apply
(
UI
.
dom
);
}
else
{
// An explicit message I hope
throw
new
Error
(
"
UI.template must be set prior to render
"
);
}
},
/**
* This function appends the dom tree to the given dom node.
* This dom node should be somewhere in the dom of the application
* @private
*/
place
=
function
place
(
UI
,
DOMplace
,
beforeNode
)
{
if
(
DOMplace
)
{
// IE (until 9) apparently fails to appendChild when insertBefore's second argument is null, hence this.
if
(
beforeNode
)
{
DOMplace
.
insertBefore
(
UI
.
dom
,
beforeNode
);
}
else
{
DOMplace
.
appendChild
(
UI
.
dom
);
}
// Also save the new place, so next renderings
// will be made inside it
_currentPlace
=
DOMplace
;
}
},
/**
* Does rendering & placing in one function
* @private
*/
renderNPlace
=
function
renderNPlace
(
UI
,
dom
)
{
render
(
UI
);
place
.
apply
(
null
,
Tools
.
toArray
(
arguments
));
},
/**
* This stores the current place
* If this is set, this is the place where new templates
* will be appended
* @private
*/
_currentPlace
=
null
,
/**
* The UI's stateMachine.
* Much better than if(stuff) do(stuff) else if (!stuff and stuff but not stouff) do (otherstuff)
* Please open an issue if you want to propose a better one
* @private
*/
_stateMachine
=
new
StateMachine
(
"
Init
"
,
{
"
Init
"
:
[[
"
render
"
,
render
,
this
,
"
Rendered
"
],
[
"
place
"
,
renderNPlace
,
this
,
"
Rendered
"
]],
"
Rendered
"
:
[[
"
place
"
,
place
,
this
],
[
"
render
"
,
render
,
this
]]
});
/**
* The UI's Store
* It has set/get/del/has/watch/unwatch methods
* @see Emily's doc for more info on how it works.
*/
this
.
model
=
otherStore
instanceof
Store
?
otherStore
:
new
Store
();
/**
* The module that will manage the plugins for this UI
* @see Olives/Plugins' doc for more info on how it works.
*/
this
.
plugins
=
new
Plugins
();
/**
* Describes the template, can either be like "<p></p>" or HTMLElements
* @type string or HTMLElement|SVGElement
*/
this
.
template
=
null
;
/**
* This will hold the dom nodes built from the template.
*/
this
.
dom
=
null
;
/**
* Place the UI in a given dom node
* @param node the node on which to append the UI
* @param beforeNode the dom before which to append the UI
*/
this
.
place
=
function
place
(
node
,
beforeNode
)
{
_stateMachine
.
event
(
"
place
"
,
this
,
node
,
beforeNode
);
};
/**
* Renders the template to dom nodes and applies the plugins on it
* It requires the template to be set first
*/
this
.
render
=
function
render
()
{
_stateMachine
.
event
(
"
render
"
,
this
);
};
/**
* Set the UI's template from a DOM element
* @param {HTMLElement|SVGElement} dom the dom element that'll become the template of the UI
* @returns true if dom is an HTMLElement|SVGElement
*/
this
.
setTemplateFromDom
=
function
setTemplateFromDom
(
dom
)
{
if
(
DomUtils
.
isAcceptedType
(
dom
))
{
this
.
template
=
dom
;
return
true
;
}
else
{
return
false
;
}
};
/**
* Transforms dom nodes into a UI.
* It basically does a setTemplateFromDOM, then a place
* It's a helper function
* @param {HTMLElement|SVGElement} node the dom to transform to a UI
* @returns true if dom is an HTMLElement|SVGElement
*/
this
.
alive
=
function
alive
(
dom
)
{
if
(
DomUtils
.
isAcceptedType
(
dom
))
{
this
.
setTemplateFromDom
(
dom
);
this
.
place
(
dom
.
parentNode
,
dom
.
nextElementSibling
);
return
true
;
}
else
{
return
false
;
}
};
/**
* Get the current dom node where the UI is placed.
* for debugging purpose
* @private
* @return {HTMLElement} node the dom where the UI is placed.
*/
this
.
getCurrentPlace
=
function
(){
return
_currentPlace
;
};
};
});
examples/olives/bower_components/olives/src/Place.plugin.js
View file @
9df98e41
...
...
@@ -12,77 +12,79 @@ define(["OObject", "Tools"],
*/
function
PlacePlugin
(
OObject
,
Tools
)
{
/**
* Intilialize a Place.plugin with a list of OObjects
* @param {Object} $uis a list of OObjects such as:
* {
* "header": new OObject(),
* "list": new OObject()
* }
* @Constructor
*/
return
function
PlacePluginConstructor
(
$uis
)
{
"
use strict
"
;
/**
* The list of uis currently set in this place plugin
* @private
*/
var
_uis
=
{};
/**
* Intilialize a Place.plugin with a list of OObjects
* @param {Object} $uis a list of OObjects such as:
* {
* "header": new OObject(),
* "list": new OObject()
* }
* @Constructor
*/
return
function
PlacePluginConstructor
(
$uis
)
{
/**
* Attach an OObject to this DOM element
* @param {HTML|SVGElement} node the dom node where to attach the OObject
* @param {String} the name of the OObject to attach
* @throws {NoSuchOObject} an error if there's no OObject for the given name
*/
this
.
place
=
function
place
(
node
,
name
)
{
if
(
_uis
[
name
]
instanceof
OObject
)
{
_uis
[
name
].
place
(
node
);
}
else
{
throw
new
Error
(
name
+
"
is not an OObject UI in place:
"
+
name
);
}
};
/**
* The list of uis currently set in this place plugin
* @private
*/
var
_uis
=
{};
/**
* Add an OObject that can be attached to a dom element
* @param {String} the name of the OObject to add to the list
* @param {OObject} ui the OObject to add the list
* @returns {Boolean} true if the OObject was added
*/
this
.
set
=
function
set
(
name
,
ui
)
{
if
(
typeof
name
==
"
string
"
&&
ui
instanceof
OObject
)
{
_uis
[
name
]
=
ui
;
return
true
;
}
else
{
return
false
;
}
};
/**
* Attach an OObject to this DOM element
* @param {HTML|SVGElement} node the dom node where to attach the OObject
* @param {String} the name of the OObject to attach
* @throws {NoSuchOObject} an error if there's no OObject for the given name
*/
this
.
place
=
function
place
(
node
,
name
)
{
if
(
_uis
[
name
]
instanceof
OObject
)
{
_uis
[
name
].
place
(
node
);
}
else
{
throw
new
Error
(
name
+
"
is not an OObject UI in place:
"
+
name
);
}
};
/**
* Add multiple dom elements at once
* @param {Object} $uis a list of OObjects such as:
* {
* "header": new OObject(),
* "list": new OObject()
* }
*/
this
.
setAll
=
function
setAll
(
uis
)
{
Tools
.
loop
(
uis
,
function
(
ui
,
name
)
{
this
.
set
(
name
,
ui
);
},
this
);
};
/**
* Add an OObject that can be attached to a dom element
* @param {String} the name of the OObject to add to the list
* @param {OObject} ui the OObject to add the list
* @returns {Boolean} true if the OObject was added
*/
this
.
set
=
function
set
(
name
,
ui
)
{
if
(
typeof
name
==
"
string
"
&&
ui
instanceof
OObject
)
{
_uis
[
name
]
=
ui
;
return
true
;
}
else
{
return
false
;
}
};
/**
* Returns an OObject from the list given its name
* @param {String} the name of the OObject to get
* @returns {OObject} OObject for the given name
*/
this
.
get
=
function
get
(
name
)
{
return
_uis
[
name
];
};
/**
* Add multiple dom elements at once
* @param {Object} $uis a list of OObjects such as:
* {
* "header": new OObject(),
* "list": new OObject()
* }
*/
this
.
setAll
=
function
setAll
(
uis
)
{
Tools
.
loop
(
uis
,
function
(
ui
,
name
)
{
this
.
set
(
name
,
ui
);
},
this
);
};
this
.
setAll
(
$uis
);
/**
* Returns an OObject from the list given its name
* @param {String} the name of the OObject to get
* @returns {OObject} OObject for the given name
*/
this
.
get
=
function
get
(
name
)
{
return
_uis
[
name
];
};
};
this
.
setAll
(
$uis
);
};
});
examples/olives/bower_components/olives/src/Plugins.js
View file @
9df98e41
...
...
@@ -17,6 +17,8 @@ define(["Tools", "DomUtils"],
*/
function
Plugins
(
Tools
,
DomUtils
)
{
"
use strict
"
;
return
function
PluginsConstructor
(
$plugins
)
{
/**
...
...
examples/olives/bower_components/olives/src/SocketIOTransport.js
View file @
9df98e41
...
...
@@ -12,6 +12,8 @@ define(["Observable", "Tools"],
*/
function
SocketIOTransport
(
Observable
,
Tools
)
{
"
use strict
"
;
/**
* Defines the SocketIOTransport
* @private
...
...
@@ -47,7 +49,7 @@ function SocketIOTransport(Observable, Tools) {
*/
this
.
getSocket
=
function
getSocket
()
{
return
_socket
;
}
,
}
;
/**
* Subscribe to a socket event
...
...
@@ -56,7 +58,7 @@ function SocketIOTransport(Observable, Tools) {
*/
this
.
on
=
function
on
(
event
,
func
)
{
return
_socket
.
on
(
event
,
func
);
}
,
}
;
/**
* Subscribe to a socket event but disconnect as soon as it fires.
...
...
@@ -95,15 +97,17 @@ function SocketIOTransport(Observable, Tools) {
* @param {Object} scope the scope in which to execute the callback
*/
this
.
request
=
function
request
(
channel
,
data
,
func
,
scope
)
{
if
(
typeof
channel
==
"
string
"
&&
typeof
data
!=
"
undefined
"
)
{
if
(
typeof
channel
==
"
string
"
&&
typeof
data
!=
"
undefined
"
)
{
var
reqData
=
{
eventId
:
Date
.
now
()
+
Math
.
floor
(
Math
.
random
()
*
1
e6
),
data
:
data
},
boundCallback
=
function
()
{
func
&&
func
.
apply
(
scope
||
null
,
arguments
);
if
(
func
)
{
func
.
apply
(
scope
||
null
,
arguments
);
}
};
this
.
once
(
reqData
.
eventId
,
boundCallback
);
...
...
@@ -125,9 +129,9 @@ function SocketIOTransport(Observable, Tools) {
* @returns
*/
this
.
listen
=
function
listen
(
channel
,
data
,
func
,
scope
)
{
if
(
typeof
channel
==
"
string
"
&&
typeof
data
!=
"
undefined
"
&&
typeof
func
==
"
function
"
)
{
if
(
typeof
channel
==
"
string
"
&&
typeof
data
!=
"
undefined
"
&&
typeof
func
==
"
function
"
)
{
var
reqData
=
{
eventId
:
Date
.
now
()
+
Math
.
floor
(
Math
.
random
()
*
1
e6
),
...
...
@@ -135,7 +139,9 @@ function SocketIOTransport(Observable, Tools) {
keepAlive
:
true
},
boundCallback
=
function
()
{
func
&&
func
.
apply
(
scope
||
null
,
arguments
);
if
(
func
)
{
func
.
apply
(
scope
||
null
,
arguments
);
}
},
that
=
this
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment