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
Eugene Shen
todomvc
Commits
c5e53bc8
Commit
c5e53bc8
authored
Apr 26, 2014
by
Pascal Hartig
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Upgrade Knockout to v3.1.0
parent
0a50f32a
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
951 additions
and
287 deletions
+951
-287
architecture-examples/knockoutjs/bower.json
architecture-examples/knockoutjs/bower.json
+1
-1
architecture-examples/knockoutjs/bower_components/component-knockout-passy/knockout.js
...tjs/bower_components/component-knockout-passy/knockout.js
+930
-273
architecture-examples/knockoutjs/bower_components/director/build/director.js
...es/knockoutjs/bower_components/director/build/director.js
+19
-12
architecture-examples/knockoutjs/index.html
architecture-examples/knockoutjs/index.html
+1
-1
No files found.
architecture-examples/knockoutjs/bower.json
View file @
c5e53bc8
...
@@ -3,7 +3,7 @@
...
@@ -3,7 +3,7 @@
"version"
:
"0.0.0"
,
"version"
:
"0.0.0"
,
"dependencies"
:
{
"dependencies"
:
{
"todomvc-common"
:
"~0.1.4"
,
"todomvc-common"
:
"~0.1.4"
,
"
knockout.js"
:
"~3.0
.0"
,
"
component-knockout-passy"
:
"~3.1
.0"
,
"director"
:
"~1.2.0"
"director"
:
"~1.2.0"
}
}
}
}
architecture-examples/knockoutjs/bower_components/
knockout.js/knockout.debug
.js
→
architecture-examples/knockoutjs/bower_components/
component-knockout-passy/knockout
.js
View file @
c5e53bc8
// Knockout JavaScript library v3.0.0
/*!
// (c) Steven Sanderson - http://knockoutjs.com/
* Knockout JavaScript library v3.1.0
// License: MIT (http://www.opensource.org/licenses/mit-license.php)
* (c) Steven Sanderson - http://knockoutjs.com/
* License: MIT (http://www.opensource.org/licenses/mit-license.php)
*/
(
function
(){
(
function
(){
var
DEBUG
=
true
;
var
DEBUG
=
true
;
...
@@ -17,15 +19,15 @@ var DEBUG=true;
...
@@ -17,15 +19,15 @@ var DEBUG=true;
if
(
typeof
require
===
'
function
'
&&
typeof
exports
===
'
object
'
&&
typeof
module
===
'
object
'
)
{
if
(
typeof
require
===
'
function
'
&&
typeof
exports
===
'
object
'
&&
typeof
module
===
'
object
'
)
{
// [1] CommonJS/Node.js
// [1] CommonJS/Node.js
var
target
=
module
[
'
exports
'
]
||
exports
;
// module.exports is for Node.js
var
target
=
module
[
'
exports
'
]
||
exports
;
// module.exports is for Node.js
factory
(
target
);
factory
(
target
,
require
);
}
else
if
(
typeof
define
===
'
function
'
&&
define
[
'
amd
'
])
{
}
else
if
(
typeof
define
===
'
function
'
&&
define
[
'
amd
'
])
{
// [2] AMD anonymous module
// [2] AMD anonymous module
define
([
'
exports
'
],
factory
);
define
([
'
exports
'
,
'
require
'
],
factory
);
}
else
{
}
else
{
// [3] No module loader (plain <script> tag) - put directly in global namespace
// [3] No module loader (plain <script> tag) - put directly in global namespace
factory
(
window
[
'
ko
'
]
=
{});
factory
(
window
[
'
ko
'
]
=
{});
}
}
}(
function
(
koExports
){
}(
function
(
koExports
,
require
){
// Internally, all KO objects are attached to koExports (even the non-exported ones whose names will be minified by the closure compiler).
// Internally, all KO objects are attached to koExports (even the non-exported ones whose names will be minified by the closure compiler).
// In the future, the following "ko" variable may be made distinct from "koExports" so that private objects are not externally reachable.
// In the future, the following "ko" variable may be made distinct from "koExports" so that private objects are not externally reachable.
var
ko
=
typeof
koExports
!==
'
undefined
'
?
koExports
:
{};
var
ko
=
typeof
koExports
!==
'
undefined
'
?
koExports
:
{};
...
@@ -44,17 +46,35 @@ ko.exportSymbol = function(koPath, object) {
...
@@ -44,17 +46,35 @@ ko.exportSymbol = function(koPath, object) {
ko
.
exportProperty
=
function
(
owner
,
publicName
,
object
)
{
ko
.
exportProperty
=
function
(
owner
,
publicName
,
object
)
{
owner
[
publicName
]
=
object
;
owner
[
publicName
]
=
object
;
};
};
ko
.
version
=
"
3.
0
.0
"
;
ko
.
version
=
"
3.
1
.0
"
;
ko
.
exportSymbol
(
'
version
'
,
ko
.
version
);
ko
.
exportSymbol
(
'
version
'
,
ko
.
version
);
ko
.
utils
=
(
function
()
{
ko
.
utils
=
(
function
()
{
var
objectForEach
=
function
(
obj
,
action
)
{
function
objectForEach
(
obj
,
action
)
{
for
(
var
prop
in
obj
)
{
for
(
var
prop
in
obj
)
{
if
(
obj
.
hasOwnProperty
(
prop
))
{
if
(
obj
.
hasOwnProperty
(
prop
))
{
action
(
prop
,
obj
[
prop
]);
action
(
prop
,
obj
[
prop
]);
}
}
}
}
};
}
function
extend
(
target
,
source
)
{
if
(
source
)
{
for
(
var
prop
in
source
)
{
if
(
source
.
hasOwnProperty
(
prop
))
{
target
[
prop
]
=
source
[
prop
];
}
}
}
return
target
;
}
function
setPrototypeOf
(
obj
,
proto
)
{
obj
.
__proto__
=
proto
;
return
obj
;
}
var
canSetPrototype
=
({
__proto__
:
[]
}
instanceof
Array
);
// Represent the known event types in a compact way, then at runtime transform it into a hash with event name as key (for fast lookup)
// Represent the known event types in a compact way, then at runtime transform it into a hash with event name as key (for fast lookup)
var
knownEvents
=
{},
knownEventTypesByEventName
=
{};
var
knownEvents
=
{},
knownEventTypesByEventName
=
{};
...
@@ -98,7 +118,7 @@ ko.utils = (function () {
...
@@ -98,7 +118,7 @@ ko.utils = (function () {
arrayForEach
:
function
(
array
,
action
)
{
arrayForEach
:
function
(
array
,
action
)
{
for
(
var
i
=
0
,
j
=
array
.
length
;
i
<
j
;
i
++
)
for
(
var
i
=
0
,
j
=
array
.
length
;
i
<
j
;
i
++
)
action
(
array
[
i
]);
action
(
array
[
i
]
,
i
);
},
},
arrayIndexOf
:
function
(
array
,
item
)
{
arrayIndexOf
:
function
(
array
,
item
)
{
...
@@ -112,15 +132,19 @@ ko.utils = (function () {
...
@@ -112,15 +132,19 @@ ko.utils = (function () {
arrayFirst
:
function
(
array
,
predicate
,
predicateOwner
)
{
arrayFirst
:
function
(
array
,
predicate
,
predicateOwner
)
{
for
(
var
i
=
0
,
j
=
array
.
length
;
i
<
j
;
i
++
)
for
(
var
i
=
0
,
j
=
array
.
length
;
i
<
j
;
i
++
)
if
(
predicate
.
call
(
predicateOwner
,
array
[
i
]))
if
(
predicate
.
call
(
predicateOwner
,
array
[
i
]
,
i
))
return
array
[
i
];
return
array
[
i
];
return
null
;
return
null
;
},
},
arrayRemoveItem
:
function
(
array
,
itemToRemove
)
{
arrayRemoveItem
:
function
(
array
,
itemToRemove
)
{
var
index
=
ko
.
utils
.
arrayIndexOf
(
array
,
itemToRemove
);
var
index
=
ko
.
utils
.
arrayIndexOf
(
array
,
itemToRemove
);
if
(
index
>
=
0
)
if
(
index
>
0
)
{
array
.
splice
(
index
,
1
);
array
.
splice
(
index
,
1
);
}
else
if
(
index
===
0
)
{
array
.
shift
();
}
},
},
arrayGetDistinctValues
:
function
(
array
)
{
arrayGetDistinctValues
:
function
(
array
)
{
...
@@ -137,7 +161,7 @@ ko.utils = (function () {
...
@@ -137,7 +161,7 @@ ko.utils = (function () {
array
=
array
||
[];
array
=
array
||
[];
var
result
=
[];
var
result
=
[];
for
(
var
i
=
0
,
j
=
array
.
length
;
i
<
j
;
i
++
)
for
(
var
i
=
0
,
j
=
array
.
length
;
i
<
j
;
i
++
)
result
.
push
(
mapping
(
array
[
i
]));
result
.
push
(
mapping
(
array
[
i
]
,
i
));
return
result
;
return
result
;
},
},
...
@@ -145,7 +169,7 @@ ko.utils = (function () {
...
@@ -145,7 +169,7 @@ ko.utils = (function () {
array
=
array
||
[];
array
=
array
||
[];
var
result
=
[];
var
result
=
[];
for
(
var
i
=
0
,
j
=
array
.
length
;
i
<
j
;
i
++
)
for
(
var
i
=
0
,
j
=
array
.
length
;
i
<
j
;
i
++
)
if
(
predicate
(
array
[
i
]))
if
(
predicate
(
array
[
i
]
,
i
))
result
.
push
(
array
[
i
]);
result
.
push
(
array
[
i
]);
return
result
;
return
result
;
},
},
...
@@ -170,16 +194,13 @@ ko.utils = (function () {
...
@@ -170,16 +194,13 @@ ko.utils = (function () {
}
}
},
},
extend
:
function
(
target
,
source
)
{
canSetPrototype
:
canSetPrototype
,
if
(
source
)
{
for
(
var
prop
in
source
)
{
extend
:
extend
,
if
(
source
.
hasOwnProperty
(
prop
))
{
target
[
prop
]
=
source
[
prop
];
setPrototypeOf
:
setPrototypeOf
,
}
}
setPrototypeOfOrExtend
:
canSetPrototype
?
setPrototypeOf
:
extend
,
}
return
target
;
},
objectForEach
:
objectForEach
,
objectForEach
:
objectForEach
,
...
@@ -262,7 +283,7 @@ ko.utils = (function () {
...
@@ -262,7 +283,7 @@ ko.utils = (function () {
// Rule [A]
// Rule [A]
while
(
continuousNodeArray
.
length
&&
continuousNodeArray
[
0
].
parentNode
!==
parentNode
)
while
(
continuousNodeArray
.
length
&&
continuousNodeArray
[
0
].
parentNode
!==
parentNode
)
continuousNodeArray
.
s
plice
(
0
,
1
);
continuousNodeArray
.
s
hift
(
);
// Rule [B]
// Rule [B]
if
(
continuousNodeArray
.
length
>
1
)
{
if
(
continuousNodeArray
.
length
>
1
)
{
...
@@ -346,21 +367,7 @@ ko.utils = (function () {
...
@@ -346,21 +367,7 @@ ko.utils = (function () {
registerEventHandler
:
function
(
element
,
eventType
,
handler
)
{
registerEventHandler
:
function
(
element
,
eventType
,
handler
)
{
var
mustUseAttachEvent
=
ieVersion
&&
eventsThatMustBeRegisteredUsingAttachEvent
[
eventType
];
var
mustUseAttachEvent
=
ieVersion
&&
eventsThatMustBeRegisteredUsingAttachEvent
[
eventType
];
if
(
!
mustUseAttachEvent
&&
typeof
jQuery
!=
"
undefined
"
)
{
if
(
!
mustUseAttachEvent
&&
jQuery
)
{
if
(
isClickOnCheckableElement
(
element
,
eventType
))
{
// For click events on checkboxes, jQuery interferes with the event handling in an awkward way:
// it toggles the element checked state *after* the click event handlers run, whereas native
// click events toggle the checked state *before* the event handler.
// Fix this by intecepting the handler and applying the correct checkedness before it runs.
var
originalHandler
=
handler
;
handler
=
function
(
event
,
eventData
)
{
var
jQuerySuppliedCheckedState
=
this
.
checked
;
if
(
eventData
)
this
.
checked
=
eventData
.
checkedStateBeforeEvent
!==
true
;
originalHandler
.
call
(
this
,
event
);
this
.
checked
=
jQuerySuppliedCheckedState
;
// Restore the state jQuery applied
};
}
jQuery
(
element
)[
'
bind
'
](
eventType
,
handler
);
jQuery
(
element
)[
'
bind
'
](
eventType
,
handler
);
}
else
if
(
!
mustUseAttachEvent
&&
typeof
element
.
addEventListener
==
"
function
"
)
}
else
if
(
!
mustUseAttachEvent
&&
typeof
element
.
addEventListener
==
"
function
"
)
element
.
addEventListener
(
eventType
,
handler
,
false
);
element
.
addEventListener
(
eventType
,
handler
,
false
);
...
@@ -382,13 +389,14 @@ ko.utils = (function () {
...
@@ -382,13 +389,14 @@ ko.utils = (function () {
if
(
!
(
element
&&
element
.
nodeType
))
if
(
!
(
element
&&
element
.
nodeType
))
throw
new
Error
(
"
element must be a DOM node when calling triggerEvent
"
);
throw
new
Error
(
"
element must be a DOM node when calling triggerEvent
"
);
if
(
typeof
jQuery
!=
"
undefined
"
)
{
// For click events on checkboxes and radio buttons, jQuery toggles the element checked state *after* the
var
eventData
=
[];
// event handler runs instead of *before*. (This was fixed in 1.9 for checkboxes but not for radio buttons.)
if
(
isClickOnCheckableElement
(
element
,
eventType
))
{
// IE doesn't change the checked state when you trigger the click event using "fireEvent".
// Work around the jQuery "click events on checkboxes" issue described above by storing the original checked state before triggering the handler
// In both cases, we'll use the click method instead.
eventData
.
push
({
checkedStateBeforeEvent
:
element
.
checked
});
var
useClickWorkaround
=
isClickOnCheckableElement
(
element
,
eventType
);
}
jQuery
(
element
)[
'
trigger
'
](
eventType
,
eventData
);
if
(
jQuery
&&
!
useClickWorkaround
)
{
jQuery
(
element
)[
'
trigger
'
](
eventType
);
}
else
if
(
typeof
document
.
createEvent
==
"
function
"
)
{
}
else
if
(
typeof
document
.
createEvent
==
"
function
"
)
{
if
(
typeof
element
.
dispatchEvent
==
"
function
"
)
{
if
(
typeof
element
.
dispatchEvent
==
"
function
"
)
{
var
eventCategory
=
knownEventTypesByEventName
[
eventType
]
||
"
HTMLEvents
"
;
var
eventCategory
=
knownEventTypesByEventName
[
eventType
]
||
"
HTMLEvents
"
;
...
@@ -398,15 +406,13 @@ ko.utils = (function () {
...
@@ -398,15 +406,13 @@ ko.utils = (function () {
}
}
else
else
throw
new
Error
(
"
The supplied element doesn't support dispatchEvent
"
);
throw
new
Error
(
"
The supplied element doesn't support dispatchEvent
"
);
}
else
if
(
useClickWorkaround
&&
element
.
click
)
{
element
.
click
();
}
else
if
(
typeof
element
.
fireEvent
!=
"
undefined
"
)
{
}
else
if
(
typeof
element
.
fireEvent
!=
"
undefined
"
)
{
// Unlike other browsers, IE doesn't change the checked state of checkboxes/radiobuttons when you trigger their "click" event
// so to make it consistent, we'll do it manually here
if
(
isClickOnCheckableElement
(
element
,
eventType
))
element
.
checked
=
element
.
checked
!==
true
;
element
.
fireEvent
(
"
on
"
+
eventType
);
element
.
fireEvent
(
"
on
"
+
eventType
);
}
}
else
{
else
throw
new
Error
(
"
Browser doesn't support triggering events
"
);
throw
new
Error
(
"
Browser doesn't support triggering events
"
);
}
},
},
unwrapObservable
:
function
(
value
)
{
unwrapObservable
:
function
(
value
)
{
...
@@ -438,7 +444,7 @@ ko.utils = (function () {
...
@@ -438,7 +444,7 @@ ko.utils = (function () {
// we'll clear everything and create a single text node.
// we'll clear everything and create a single text node.
var
innerTextNode
=
ko
.
virtualElements
.
firstChild
(
element
);
var
innerTextNode
=
ko
.
virtualElements
.
firstChild
(
element
);
if
(
!
innerTextNode
||
innerTextNode
.
nodeType
!=
3
||
ko
.
virtualElements
.
nextSibling
(
innerTextNode
))
{
if
(
!
innerTextNode
||
innerTextNode
.
nodeType
!=
3
||
ko
.
virtualElements
.
nextSibling
(
innerTextNode
))
{
ko
.
virtualElements
.
setDomNodeChildren
(
element
,
[
d
ocument
.
createTextNode
(
value
)]);
ko
.
virtualElements
.
setDomNodeChildren
(
element
,
[
element
.
ownerD
ocument
.
createTextNode
(
value
)]);
}
else
{
}
else
{
innerTextNode
.
data
=
value
;
innerTextNode
.
data
=
value
;
}
}
...
@@ -687,16 +693,13 @@ ko.utils.domNodeDisposal = new (function () {
...
@@ -687,16 +693,13 @@ ko.utils.domNodeDisposal = new (function () {
callbacks
[
i
](
node
);
callbacks
[
i
](
node
);
}
}
//
Also e
rase the DOM data
//
E
rase the DOM data
ko
.
utils
.
domData
.
clear
(
node
);
ko
.
utils
.
domData
.
clear
(
node
);
// Special support for jQuery here because it's so commonly used.
// Perform cleanup needed by external libraries (currently only jQuery, but can be extended)
// Many jQuery plugins (including jquery.tmpl) store data using jQuery's equivalent of domData
ko
.
utils
.
domNodeDisposal
[
"
cleanExternalData
"
](
node
);
// so notify it to tear down any resources associated with the node & descendants here.
if
((
typeof
jQuery
==
"
function
"
)
&&
(
typeof
jQuery
[
'
cleanData
'
]
==
"
function
"
))
jQuery
[
'
cleanData
'
]([
node
]);
//
Also c
lear any immediate-child comment nodes, as these wouldn't have been found by
//
C
lear any immediate-child comment nodes, as these wouldn't have been found by
// node.getElementsByTagName("*") in cleanNode() (comment nodes aren't elements)
// node.getElementsByTagName("*") in cleanNode() (comment nodes aren't elements)
if
(
cleanableNodeTypesWithDescendants
[
node
.
nodeType
])
if
(
cleanableNodeTypesWithDescendants
[
node
.
nodeType
])
cleanImmediateCommentTypeChildren
(
node
);
cleanImmediateCommentTypeChildren
(
node
);
...
@@ -748,6 +751,14 @@ ko.utils.domNodeDisposal = new (function () {
...
@@ -748,6 +751,14 @@ ko.utils.domNodeDisposal = new (function () {
ko
.
cleanNode
(
node
);
ko
.
cleanNode
(
node
);
if
(
node
.
parentNode
)
if
(
node
.
parentNode
)
node
.
parentNode
.
removeChild
(
node
);
node
.
parentNode
.
removeChild
(
node
);
},
"
cleanExternalData
"
:
function
(
node
)
{
// Special support for jQuery here because it's so commonly used.
// Many jQuery plugins (including jquery.tmpl) store data using jQuery's equivalent of domData
// so notify it to tear down any resources associated with the node & descendants here.
if
(
jQuery
&&
(
typeof
jQuery
[
'
cleanData
'
]
==
"
function
"
))
jQuery
[
'
cleanData
'
]([
node
]);
}
}
}
}
})();
})();
...
@@ -821,8 +832,8 @@ ko.exportSymbol('utils.domNodeDisposal.removeDisposeCallback', ko.utils.domNodeD
...
@@ -821,8 +832,8 @@ ko.exportSymbol('utils.domNodeDisposal.removeDisposeCallback', ko.utils.domNodeD
}
}
ko
.
utils
.
parseHtmlFragment
=
function
(
html
)
{
ko
.
utils
.
parseHtmlFragment
=
function
(
html
)
{
return
typeof
jQuery
!=
'
undefined
'
?
jQueryHtmlParse
(
html
)
// As below, benefit from jQuery's optimisations where possible
return
jQuery
?
jQueryHtmlParse
(
html
)
// As below, benefit from jQuery's optimisations where possible
:
simpleHtmlParse
(
html
);
// ... otherwise, this simple logic will do in most common cases.
:
simpleHtmlParse
(
html
);
// ... otherwise, this simple logic will do in most common cases.
};
};
ko
.
utils
.
setHtml
=
function
(
node
,
html
)
{
ko
.
utils
.
setHtml
=
function
(
node
,
html
)
{
...
@@ -838,7 +849,7 @@ ko.exportSymbol('utils.domNodeDisposal.removeDisposeCallback', ko.utils.domNodeD
...
@@ -838,7 +849,7 @@ ko.exportSymbol('utils.domNodeDisposal.removeDisposeCallback', ko.utils.domNodeD
// jQuery contains a lot of sophisticated code to parse arbitrary HTML fragments,
// jQuery contains a lot of sophisticated code to parse arbitrary HTML fragments,
// for example <tr> elements which are not normally allowed to exist on their own.
// for example <tr> elements which are not normally allowed to exist on their own.
// If you've referenced jQuery we'll use that rather than duplicating its code.
// If you've referenced jQuery we'll use that rather than duplicating its code.
if
(
typeof
jQuery
!=
'
undefined
'
)
{
if
(
jQuery
)
{
jQuery
(
node
)[
'
html
'
](
html
);
jQuery
(
node
)[
'
html
'
](
html
);
}
else
{
}
else
{
// ... otherwise, use KO's own parsing logic.
// ... otherwise, use KO's own parsing logic.
...
@@ -944,6 +955,22 @@ ko.extenders = {
...
@@ -944,6 +955,22 @@ ko.extenders = {
});
});
},
},
'
rateLimit
'
:
function
(
target
,
options
)
{
var
timeout
,
method
,
limitFunction
;
if
(
typeof
options
==
'
number
'
)
{
timeout
=
options
;
}
else
{
timeout
=
options
[
'
timeout
'
];
method
=
options
[
'
method
'
];
}
limitFunction
=
method
==
'
notifyWhenChangesStop
'
?
debounce
:
throttle
;
target
.
limit
(
function
(
callback
)
{
return
limitFunction
(
callback
,
timeout
);
});
},
'
notify
'
:
function
(
target
,
notifyWhen
)
{
'
notify
'
:
function
(
target
,
notifyWhen
)
{
target
[
"
equalityComparer
"
]
=
notifyWhen
==
"
always
"
?
target
[
"
equalityComparer
"
]
=
notifyWhen
==
"
always
"
?
null
:
// null equalityComparer means to always notify
null
:
// null equalityComparer means to always notify
...
@@ -957,6 +984,26 @@ function valuesArePrimitiveAndEqual(a, b) {
...
@@ -957,6 +984,26 @@ function valuesArePrimitiveAndEqual(a, b) {
return
oldValueIsPrimitive
?
(
a
===
b
)
:
false
;
return
oldValueIsPrimitive
?
(
a
===
b
)
:
false
;
}
}
function
throttle
(
callback
,
timeout
)
{
var
timeoutInstance
;
return
function
()
{
if
(
!
timeoutInstance
)
{
timeoutInstance
=
setTimeout
(
function
()
{
timeoutInstance
=
undefined
;
callback
();
},
timeout
);
}
};
}
function
debounce
(
callback
,
timeout
)
{
var
timeoutInstance
;
return
function
()
{
clearTimeout
(
timeoutInstance
);
timeoutInstance
=
setTimeout
(
callback
,
timeout
);
};
}
function
applyExtenders
(
requestedExtenders
)
{
function
applyExtenders
(
requestedExtenders
)
{
var
target
=
this
;
var
target
=
this
;
if
(
requestedExtenders
)
{
if
(
requestedExtenders
)
{
...
@@ -976,6 +1023,7 @@ ko.subscription = function (target, callback, disposeCallback) {
...
@@ -976,6 +1023,7 @@ ko.subscription = function (target, callback, disposeCallback) {
this
.
target
=
target
;
this
.
target
=
target
;
this
.
callback
=
callback
;
this
.
callback
=
callback
;
this
.
disposeCallback
=
disposeCallback
;
this
.
disposeCallback
=
disposeCallback
;
this
.
isDisposed
=
false
;
ko
.
exportProperty
(
this
,
'
dispose
'
,
this
.
dispose
);
ko
.
exportProperty
(
this
,
'
dispose
'
,
this
.
dispose
);
};
};
ko
.
subscription
.
prototype
.
dispose
=
function
()
{
ko
.
subscription
.
prototype
.
dispose
=
function
()
{
...
@@ -984,28 +1032,32 @@ ko.subscription.prototype.dispose = function () {
...
@@ -984,28 +1032,32 @@ ko.subscription.prototype.dispose = function () {
};
};
ko
.
subscribable
=
function
()
{
ko
.
subscribable
=
function
()
{
ko
.
utils
.
setPrototypeOfOrExtend
(
this
,
ko
.
subscribable
[
'
fn
'
]);
this
.
_subscriptions
=
{};
this
.
_subscriptions
=
{};
ko
.
utils
.
extend
(
this
,
ko
.
subscribable
[
'
fn
'
]);
ko
.
exportProperty
(
this
,
'
subscribe
'
,
this
.
subscribe
);
ko
.
exportProperty
(
this
,
'
extend
'
,
this
.
extend
);
ko
.
exportProperty
(
this
,
'
getSubscriptionsCount
'
,
this
.
getSubscriptionsCount
);
}
}
var
defaultEvent
=
"
change
"
;
var
defaultEvent
=
"
change
"
;
ko
.
subscribable
[
'
fn
'
]
=
{
var
ko_subscribable_fn
=
{
subscribe
:
function
(
callback
,
callbackTarget
,
event
)
{
subscribe
:
function
(
callback
,
callbackTarget
,
event
)
{
var
self
=
this
;
event
=
event
||
defaultEvent
;
event
=
event
||
defaultEvent
;
var
boundCallback
=
callbackTarget
?
callback
.
bind
(
callbackTarget
)
:
callback
;
var
boundCallback
=
callbackTarget
?
callback
.
bind
(
callbackTarget
)
:
callback
;
var
subscription
=
new
ko
.
subscription
(
this
,
boundCallback
,
function
()
{
var
subscription
=
new
ko
.
subscription
(
self
,
boundCallback
,
function
()
{
ko
.
utils
.
arrayRemoveItem
(
this
.
_subscriptions
[
event
],
subscription
);
ko
.
utils
.
arrayRemoveItem
(
self
.
_subscriptions
[
event
],
subscription
);
}
.
bind
(
this
)
);
});
if
(
!
this
.
_subscriptions
[
event
])
// This will force a computed with deferEvaluation to evaluate before any subscriptions
this
.
_subscriptions
[
event
]
=
[];
// are registered.
this
.
_subscriptions
[
event
].
push
(
subscription
);
if
(
self
.
peek
)
{
self
.
peek
();
}
if
(
!
self
.
_subscriptions
[
event
])
self
.
_subscriptions
[
event
]
=
[];
self
.
_subscriptions
[
event
].
push
(
subscription
);
return
subscription
;
return
subscription
;
},
},
...
@@ -1013,19 +1065,61 @@ ko.subscribable['fn'] = {
...
@@ -1013,19 +1065,61 @@ ko.subscribable['fn'] = {
event
=
event
||
defaultEvent
;
event
=
event
||
defaultEvent
;
if
(
this
.
hasSubscriptionsForEvent
(
event
))
{
if
(
this
.
hasSubscriptionsForEvent
(
event
))
{
try
{
try
{
ko
.
dependencyDetection
.
begin
();
ko
.
dependencyDetection
.
begin
();
// Begin suppressing dependency detection (by setting the top frame to undefined)
for
(
var
a
=
this
.
_subscriptions
[
event
].
slice
(
0
),
i
=
0
,
subscription
;
subscription
=
a
[
i
];
++
i
)
{
for
(
var
a
=
this
.
_subscriptions
[
event
].
slice
(
0
),
i
=
0
,
subscription
;
subscription
=
a
[
i
];
++
i
)
{
// In case a subscription was disposed during the arrayForEach cycle, check
// In case a subscription was disposed during the arrayForEach cycle, check
// for isDisposed on each subscription before invoking its callback
// for isDisposed on each subscription before invoking its callback
if
(
subscription
&&
(
subscription
.
isDisposed
!==
true
)
)
if
(
!
subscription
.
isDisposed
)
subscription
.
callback
(
valueToNotify
);
subscription
.
callback
(
valueToNotify
);
}
}
}
finally
{
}
finally
{
ko
.
dependencyDetection
.
end
();
ko
.
dependencyDetection
.
end
();
// End suppressing dependency detection
}
}
}
}
},
},
limit
:
function
(
limitFunction
)
{
var
self
=
this
,
selfIsObservable
=
ko
.
isObservable
(
self
),
isPending
,
previousValue
,
pendingValue
,
beforeChange
=
'
beforeChange
'
;
if
(
!
self
.
_origNotifySubscribers
)
{
self
.
_origNotifySubscribers
=
self
[
"
notifySubscribers
"
];
self
[
"
notifySubscribers
"
]
=
function
(
value
,
event
)
{
if
(
!
event
||
event
===
defaultEvent
)
{
self
.
_rateLimitedChange
(
value
);
}
else
if
(
event
===
beforeChange
)
{
self
.
_rateLimitedBeforeChange
(
value
);
}
else
{
self
.
_origNotifySubscribers
(
value
,
event
);
}
};
}
var
finish
=
limitFunction
(
function
()
{
// If an observable provided a reference to itself, access it to get the latest value.
// This allows computed observables to delay calculating their value until needed.
if
(
selfIsObservable
&&
pendingValue
===
self
)
{
pendingValue
=
self
();
}
isPending
=
false
;
if
(
self
.
isDifferent
(
previousValue
,
pendingValue
))
{
self
.
_origNotifySubscribers
(
previousValue
=
pendingValue
);
}
});
self
.
_rateLimitedChange
=
function
(
value
)
{
isPending
=
true
;
pendingValue
=
value
;
finish
();
};
self
.
_rateLimitedBeforeChange
=
function
(
value
)
{
if
(
!
isPending
)
{
previousValue
=
value
;
self
.
_origNotifySubscribers
(
value
,
beforeChange
);
}
};
},
hasSubscriptionsForEvent
:
function
(
event
)
{
hasSubscriptionsForEvent
:
function
(
event
)
{
return
this
.
_subscriptions
[
event
]
&&
this
.
_subscriptions
[
event
].
length
;
return
this
.
_subscriptions
[
event
]
&&
this
.
_subscriptions
[
event
].
length
;
},
},
...
@@ -1038,9 +1132,26 @@ ko.subscribable['fn'] = {
...
@@ -1038,9 +1132,26 @@ ko.subscribable['fn'] = {
return
total
;
return
total
;
},
},
isDifferent
:
function
(
oldValue
,
newValue
)
{
return
!
this
[
'
equalityComparer
'
]
||
!
this
[
'
equalityComparer
'
](
oldValue
,
newValue
);
},
extend
:
applyExtenders
extend
:
applyExtenders
};
};
ko
.
exportProperty
(
ko_subscribable_fn
,
'
subscribe
'
,
ko_subscribable_fn
.
subscribe
);
ko
.
exportProperty
(
ko_subscribable_fn
,
'
extend
'
,
ko_subscribable_fn
.
extend
);
ko
.
exportProperty
(
ko_subscribable_fn
,
'
getSubscriptionsCount
'
,
ko_subscribable_fn
.
getSubscriptionsCount
);
// For browsers that support proto assignment, we overwrite the prototype of each
// observable instance. Since observables are functions, we need Function.prototype
// to still be in the prototype chain.
if
(
ko
.
utils
.
canSetPrototype
)
{
ko
.
utils
.
setPrototypeOf
(
ko_subscribable_fn
,
Function
.
prototype
);
}
ko
.
subscribable
[
'
fn
'
]
=
ko_subscribable_fn
;
ko
.
isSubscribable
=
function
(
instance
)
{
ko
.
isSubscribable
=
function
(
instance
)
{
return
instance
!=
null
&&
typeof
instance
.
subscribe
==
"
function
"
&&
typeof
instance
[
"
notifySubscribers
"
]
==
"
function
"
;
return
instance
!=
null
&&
typeof
instance
.
subscribe
==
"
function
"
&&
typeof
instance
[
"
notifySubscribers
"
]
==
"
function
"
;
...
@@ -1049,40 +1160,67 @@ ko.isSubscribable = function (instance) {
...
@@ -1049,40 +1160,67 @@ ko.isSubscribable = function (instance) {
ko
.
exportSymbol
(
'
subscribable
'
,
ko
.
subscribable
);
ko
.
exportSymbol
(
'
subscribable
'
,
ko
.
subscribable
);
ko
.
exportSymbol
(
'
isSubscribable
'
,
ko
.
isSubscribable
);
ko
.
exportSymbol
(
'
isSubscribable
'
,
ko
.
isSubscribable
);
ko
.
dependencyDetection
=
(
function
()
{
ko
.
computedContext
=
ko
.
dependencyDetection
=
(
function
()
{
var
_frames
=
[];
var
outerFrames
=
[],
currentFrame
,
lastId
=
0
;
// Return a unique ID that can be assigned to an observable for dependency tracking.
// Theoretically, you could eventually overflow the number storage size, resulting
// in duplicate IDs. But in JavaScript, the largest exact integral value is 2^53
// or 9,007,199,254,740,992. If you created 1,000,000 IDs per second, it would
// take over 285 years to reach that number.
// Reference http://blog.vjeux.com/2010/javascript/javascript-max_int-number-limits.html
function
getId
()
{
return
++
lastId
;
}
function
begin
(
options
)
{
outerFrames
.
push
(
currentFrame
);
currentFrame
=
options
;
}
function
end
()
{
currentFrame
=
outerFrames
.
pop
();
}
return
{
return
{
begin
:
function
(
callback
)
{
begin
:
begin
,
_frames
.
push
(
callback
&&
{
callback
:
callback
,
distinctDependencies
:[]
});
},
end
:
function
()
{
end
:
end
,
_frames
.
pop
();
},
registerDependency
:
function
(
subscribable
)
{
registerDependency
:
function
(
subscribable
)
{
if
(
!
ko
.
isSubscribable
(
subscribable
))
if
(
currentFrame
)
{
throw
new
Error
(
"
Only subscribable things can act as dependencies
"
);
if
(
!
ko
.
isSubscribable
(
subscribable
))
if
(
_frames
.
length
>
0
)
{
throw
new
Error
(
"
Only subscribable things can act as dependencies
"
);
var
topFrame
=
_frames
[
_frames
.
length
-
1
];
currentFrame
.
callback
(
subscribable
,
subscribable
.
_id
||
(
subscribable
.
_id
=
getId
()));
if
(
!
topFrame
||
ko
.
utils
.
arrayIndexOf
(
topFrame
.
distinctDependencies
,
subscribable
)
>=
0
)
return
;
topFrame
.
distinctDependencies
.
push
(
subscribable
);
topFrame
.
callback
(
subscribable
);
}
}
},
},
ignore
:
function
(
callback
,
callbackTarget
,
callbackArgs
)
{
ignore
:
function
(
callback
,
callbackTarget
,
callbackArgs
)
{
try
{
try
{
_frames
.
push
(
null
);
begin
(
);
return
callback
.
apply
(
callbackTarget
,
callbackArgs
||
[]);
return
callback
.
apply
(
callbackTarget
,
callbackArgs
||
[]);
}
finally
{
}
finally
{
_frames
.
pop
();
end
();
}
}
},
getDependenciesCount
:
function
()
{
if
(
currentFrame
)
return
currentFrame
.
computed
.
getDependenciesCount
();
},
isInitial
:
function
()
{
if
(
currentFrame
)
return
currentFrame
.
isInitial
;
}
}
};
};
})();
})();
ko
.
exportSymbol
(
'
computedContext
'
,
ko
.
computedContext
);
ko
.
exportSymbol
(
'
computedContext.getDependenciesCount
'
,
ko
.
computedContext
.
getDependenciesCount
);
ko
.
exportSymbol
(
'
computedContext.isInitial
'
,
ko
.
computedContext
.
isInitial
);
ko
.
observable
=
function
(
initialValue
)
{
ko
.
observable
=
function
(
initialValue
)
{
var
_latestValue
=
initialValue
;
var
_latestValue
=
initialValue
;
...
@@ -1091,7 +1229,7 @@ ko.observable = function (initialValue) {
...
@@ -1091,7 +1229,7 @@ ko.observable = function (initialValue) {
// Write
// Write
// Ignore writes if the value hasn't changed
// Ignore writes if the value hasn't changed
if
(
!
observable
[
'
equalityComparer
'
]
||
!
observable
[
'
equalityComparer
'
]
(
_latestValue
,
arguments
[
0
]))
{
if
(
observable
.
isDifferent
(
_latestValue
,
arguments
[
0
]))
{
observable
.
valueWillMutate
();
observable
.
valueWillMutate
();
_latestValue
=
arguments
[
0
];
_latestValue
=
arguments
[
0
];
if
(
DEBUG
)
observable
.
_latestValue
=
_latestValue
;
if
(
DEBUG
)
observable
.
_latestValue
=
_latestValue
;
...
@@ -1105,12 +1243,13 @@ ko.observable = function (initialValue) {
...
@@ -1105,12 +1243,13 @@ ko.observable = function (initialValue) {
return
_latestValue
;
return
_latestValue
;
}
}
}
}
if
(
DEBUG
)
observable
.
_latestValue
=
_latestValue
;
ko
.
subscribable
.
call
(
observable
);
ko
.
subscribable
.
call
(
observable
);
ko
.
utils
.
setPrototypeOfOrExtend
(
observable
,
ko
.
observable
[
'
fn
'
]);
if
(
DEBUG
)
observable
.
_latestValue
=
_latestValue
;
observable
.
peek
=
function
()
{
return
_latestValue
};
observable
.
peek
=
function
()
{
return
_latestValue
};
observable
.
valueHasMutated
=
function
()
{
observable
[
"
notifySubscribers
"
](
_latestValue
);
}
observable
.
valueHasMutated
=
function
()
{
observable
[
"
notifySubscribers
"
](
_latestValue
);
}
observable
.
valueWillMutate
=
function
()
{
observable
[
"
notifySubscribers
"
](
_latestValue
,
"
beforeChange
"
);
}
observable
.
valueWillMutate
=
function
()
{
observable
[
"
notifySubscribers
"
](
_latestValue
,
"
beforeChange
"
);
}
ko
.
utils
.
extend
(
observable
,
ko
.
observable
[
'
fn
'
]);
ko
.
exportProperty
(
observable
,
'
peek
'
,
observable
.
peek
);
ko
.
exportProperty
(
observable
,
'
peek
'
,
observable
.
peek
);
ko
.
exportProperty
(
observable
,
"
valueHasMutated
"
,
observable
.
valueHasMutated
);
ko
.
exportProperty
(
observable
,
"
valueHasMutated
"
,
observable
.
valueHasMutated
);
...
@@ -1126,6 +1265,12 @@ ko.observable['fn'] = {
...
@@ -1126,6 +1265,12 @@ ko.observable['fn'] = {
var
protoProperty
=
ko
.
observable
.
protoProperty
=
"
__ko_proto__
"
;
var
protoProperty
=
ko
.
observable
.
protoProperty
=
"
__ko_proto__
"
;
ko
.
observable
[
'
fn
'
][
protoProperty
]
=
ko
.
observable
;
ko
.
observable
[
'
fn
'
][
protoProperty
]
=
ko
.
observable
;
// Note that for browsers that don't support proto assignment, the
// inheritance chain is created manually in the ko.observable constructor
if
(
ko
.
utils
.
canSetPrototype
)
{
ko
.
utils
.
setPrototypeOf
(
ko
.
observable
[
'
fn
'
],
ko
.
subscribable
[
'
fn
'
]);
}
ko
.
hasPrototype
=
function
(
instance
,
prototype
)
{
ko
.
hasPrototype
=
function
(
instance
,
prototype
)
{
if
((
instance
===
null
)
||
(
instance
===
undefined
)
||
(
instance
[
protoProperty
]
===
undefined
))
return
false
;
if
((
instance
===
null
)
||
(
instance
===
undefined
)
||
(
instance
[
protoProperty
]
===
undefined
))
return
false
;
if
(
instance
[
protoProperty
]
===
prototype
)
return
true
;
if
(
instance
[
protoProperty
]
===
prototype
)
return
true
;
...
@@ -1157,7 +1302,7 @@ ko.observableArray = function (initialValues) {
...
@@ -1157,7 +1302,7 @@ ko.observableArray = function (initialValues) {
throw
new
Error
(
"
The argument passed when initializing an observable array must be an array, or null, or undefined.
"
);
throw
new
Error
(
"
The argument passed when initializing an observable array must be an array, or null, or undefined.
"
);
var
result
=
ko
.
observable
(
initialValues
);
var
result
=
ko
.
observable
(
initialValues
);
ko
.
utils
.
e
xtend
(
result
,
ko
.
observableArray
[
'
fn
'
]);
ko
.
utils
.
setPrototypeOfOrE
xtend
(
result
,
ko
.
observableArray
[
'
fn
'
]);
return
result
.
extend
({
'
trackArrayChanges
'
:
true
});
return
result
.
extend
({
'
trackArrayChanges
'
:
true
});
};
};
...
@@ -1265,6 +1410,12 @@ ko.utils.arrayForEach(["slice"], function (methodName) {
...
@@ -1265,6 +1410,12 @@ ko.utils.arrayForEach(["slice"], function (methodName) {
};
};
});
});
// Note that for browsers that don't support proto assignment, the
// inheritance chain is created manually in the ko.observableArray constructor
if
(
ko
.
utils
.
canSetPrototype
)
{
ko
.
utils
.
setPrototypeOf
(
ko
.
observableArray
[
'
fn
'
],
ko
.
observable
[
'
fn
'
]);
}
ko
.
exportSymbol
(
'
observableArray
'
,
ko
.
observableArray
);
ko
.
exportSymbol
(
'
observableArray
'
,
ko
.
observableArray
);
var
arrayChangeEventName
=
'
arrayChange
'
;
var
arrayChangeEventName
=
'
arrayChange
'
;
ko
.
extenders
[
'
trackArrayChanges
'
]
=
function
(
target
)
{
ko
.
extenders
[
'
trackArrayChanges
'
]
=
function
(
target
)
{
...
@@ -1327,9 +1478,9 @@ ko.extenders['trackArrayChanges'] = function(target) {
...
@@ -1327,9 +1478,9 @@ ko.extenders['trackArrayChanges'] = function(target) {
function
getChanges
(
previousContents
,
currentContents
)
{
function
getChanges
(
previousContents
,
currentContents
)
{
// We try to re-use cached diffs.
// We try to re-use cached diffs.
// The
only scenario where pendingNotifications > 1 is when using the KO 'deferred updates' plugin,
// The
scenarios where pendingNotifications > 1 are when using rate-limiting or the Deferred Updates
//
which without this check would not be compatible with arrayChange notifications. Without that
//
plugin, which without this check would not be compatible with arrayChange notifications. Normally,
//
plugin, notifications are always
issued immediately so we wouldn't be queueing up more than one.
//
notifications are
issued immediately so we wouldn't be queueing up more than one.
if
(
!
cachedDiff
||
pendingNotifications
>
1
)
{
if
(
!
cachedDiff
||
pendingNotifications
>
1
)
{
cachedDiff
=
ko
.
utils
.
compareArrays
(
previousContents
,
currentContents
,
{
'
sparse
'
:
true
});
cachedDiff
=
ko
.
utils
.
compareArrays
(
previousContents
,
currentContents
,
{
'
sparse
'
:
true
});
}
}
...
@@ -1349,7 +1500,7 @@ ko.extenders['trackArrayChanges'] = function(target) {
...
@@ -1349,7 +1500,7 @@ ko.extenders['trackArrayChanges'] = function(target) {
offset
=
0
;
offset
=
0
;
function
pushDiff
(
status
,
value
,
index
)
{
function
pushDiff
(
status
,
value
,
index
)
{
diff
.
push
({
'
status
'
:
status
,
'
value
'
:
value
,
'
index
'
:
index
})
;
return
diff
[
diff
.
length
]
=
{
'
status
'
:
status
,
'
value
'
:
value
,
'
index
'
:
index
}
;
}
}
switch
(
operationName
)
{
switch
(
operationName
)
{
case
'
push
'
:
case
'
push
'
:
...
@@ -1374,13 +1525,15 @@ ko.extenders['trackArrayChanges'] = function(target) {
...
@@ -1374,13 +1525,15 @@ ko.extenders['trackArrayChanges'] = function(target) {
var
startIndex
=
Math
.
min
(
Math
.
max
(
0
,
args
[
0
]
<
0
?
arrayLength
+
args
[
0
]
:
args
[
0
]),
arrayLength
),
var
startIndex
=
Math
.
min
(
Math
.
max
(
0
,
args
[
0
]
<
0
?
arrayLength
+
args
[
0
]
:
args
[
0
]),
arrayLength
),
endDeleteIndex
=
argsLength
===
1
?
arrayLength
:
Math
.
min
(
startIndex
+
(
args
[
1
]
||
0
),
arrayLength
),
endDeleteIndex
=
argsLength
===
1
?
arrayLength
:
Math
.
min
(
startIndex
+
(
args
[
1
]
||
0
),
arrayLength
),
endAddIndex
=
startIndex
+
argsLength
-
2
,
endAddIndex
=
startIndex
+
argsLength
-
2
,
endIndex
=
Math
.
max
(
endDeleteIndex
,
endAddIndex
);
endIndex
=
Math
.
max
(
endDeleteIndex
,
endAddIndex
),
additions
=
[],
deletions
=
[];
for
(
var
index
=
startIndex
,
argsIndex
=
2
;
index
<
endIndex
;
++
index
,
++
argsIndex
)
{
for
(
var
index
=
startIndex
,
argsIndex
=
2
;
index
<
endIndex
;
++
index
,
++
argsIndex
)
{
if
(
index
<
endDeleteIndex
)
if
(
index
<
endDeleteIndex
)
pushDiff
(
'
deleted
'
,
rawArray
[
index
],
index
);
deletions
.
push
(
pushDiff
(
'
deleted
'
,
rawArray
[
index
],
index
)
);
if
(
index
<
endAddIndex
)
if
(
index
<
endAddIndex
)
pushDiff
(
'
added
'
,
args
[
argsIndex
],
index
);
additions
.
push
(
pushDiff
(
'
added
'
,
args
[
argsIndex
],
index
)
);
}
}
ko
.
utils
.
findMovesInArrayComparison
(
deletions
,
additions
);
break
;
break
;
default
:
default
:
...
@@ -1389,11 +1542,12 @@ ko.extenders['trackArrayChanges'] = function(target) {
...
@@ -1389,11 +1542,12 @@ ko.extenders['trackArrayChanges'] = function(target) {
cachedDiff
=
diff
;
cachedDiff
=
diff
;
};
};
};
};
ko
.
dependentObservable
=
function
(
evaluatorFunctionOrOptions
,
evaluatorFunctionTarget
,
options
)
{
ko
.
computed
=
ko
.
dependentObservable
=
function
(
evaluatorFunctionOrOptions
,
evaluatorFunctionTarget
,
options
)
{
var
_latestValue
,
var
_latestValue
,
_
hasBeenEvaluated
=
fals
e
,
_
needsEvaluation
=
tru
e
,
_isBeingEvaluated
=
false
,
_isBeingEvaluated
=
false
,
_suppressDisposalUntilDisposeWhenReturnsFalse
=
false
,
_suppressDisposalUntilDisposeWhenReturnsFalse
=
false
,
_isDisposed
=
false
,
readFunction
=
evaluatorFunctionOrOptions
;
readFunction
=
evaluatorFunctionOrOptions
;
if
(
readFunction
&&
typeof
readFunction
==
"
object
"
)
{
if
(
readFunction
&&
typeof
readFunction
==
"
object
"
)
{
...
@@ -1409,15 +1563,21 @@ ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunction
...
@@ -1409,15 +1563,21 @@ ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunction
if
(
typeof
readFunction
!=
"
function
"
)
if
(
typeof
readFunction
!=
"
function
"
)
throw
new
Error
(
"
Pass a function that returns the value of the ko.computed
"
);
throw
new
Error
(
"
Pass a function that returns the value of the ko.computed
"
);
function
addSubscriptionToDependency
(
subscribable
)
{
function
addSubscriptionToDependency
(
subscribable
,
id
)
{
_subscriptionsToDependencies
.
push
(
subscribable
.
subscribe
(
evaluatePossiblyAsync
));
if
(
!
_subscriptionsToDependencies
[
id
])
{
_subscriptionsToDependencies
[
id
]
=
subscribable
.
subscribe
(
evaluatePossiblyAsync
);
++
_dependenciesCount
;
}
}
}
function
disposeAllSubscriptionsToDependencies
()
{
function
disposeAllSubscriptionsToDependencies
()
{
ko
.
utils
.
arrayForEach
(
_subscriptionsToDependencies
,
function
(
subscription
)
{
_isDisposed
=
true
;
ko
.
utils
.
objectForEach
(
_subscriptionsToDependencies
,
function
(
id
,
subscription
)
{
subscription
.
dispose
();
subscription
.
dispose
();
});
});
_subscriptionsToDependencies
=
[];
_subscriptionsToDependencies
=
{};
_dependenciesCount
=
0
;
_needsEvaluation
=
false
;
}
}
function
evaluatePossiblyAsync
()
{
function
evaluatePossiblyAsync
()
{
...
@@ -1425,8 +1585,11 @@ ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunction
...
@@ -1425,8 +1585,11 @@ ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunction
if
(
throttleEvaluationTimeout
&&
throttleEvaluationTimeout
>=
0
)
{
if
(
throttleEvaluationTimeout
&&
throttleEvaluationTimeout
>=
0
)
{
clearTimeout
(
evaluationTimeoutInstance
);
clearTimeout
(
evaluationTimeoutInstance
);
evaluationTimeoutInstance
=
setTimeout
(
evaluateImmediate
,
throttleEvaluationTimeout
);
evaluationTimeoutInstance
=
setTimeout
(
evaluateImmediate
,
throttleEvaluationTimeout
);
}
else
}
else
if
(
dependentObservable
.
_evalRateLimited
)
{
dependentObservable
.
_evalRateLimited
();
}
else
{
evaluateImmediate
();
evaluateImmediate
();
}
}
}
function
evaluateImmediate
()
{
function
evaluateImmediate
()
{
...
@@ -1438,11 +1601,15 @@ ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunction
...
@@ -1438,11 +1601,15 @@ ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunction
return
;
return
;
}
}
// Do not evaluate (and possibly capture new dependencies) if disposed
if
(
_isDisposed
)
{
return
;
}
if
(
disposeWhen
&&
disposeWhen
())
{
if
(
disposeWhen
&&
disposeWhen
())
{
// See comment below about _suppressDisposalUntilDisposeWhenReturnsFalse
// See comment below about _suppressDisposalUntilDisposeWhenReturnsFalse
if
(
!
_suppressDisposalUntilDisposeWhenReturnsFalse
)
{
if
(
!
_suppressDisposalUntilDisposeWhenReturnsFalse
)
{
dispose
();
dispose
();
_hasBeenEvaluated
=
true
;
return
;
return
;
}
}
}
else
{
}
else
{
...
@@ -1454,38 +1621,63 @@ ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunction
...
@@ -1454,38 +1621,63 @@ ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunction
try
{
try
{
// Initially, we assume that none of the subscriptions are still being used (i.e., all are candidates for disposal).
// Initially, we assume that none of the subscriptions are still being used (i.e., all are candidates for disposal).
// Then, during evaluation, we cross off any that are in fact still being used.
// Then, during evaluation, we cross off any that are in fact still being used.
var
disposalCandidates
=
ko
.
utils
.
arrayMap
(
_subscriptionsToDependencies
,
function
(
item
)
{
return
item
.
target
;});
var
disposalCandidates
=
_subscriptionsToDependencies
,
disposalCount
=
_dependenciesCount
;
ko
.
dependencyDetection
.
begin
({
ko
.
dependencyDetection
.
begin
(
function
(
subscribable
)
{
callback
:
function
(
subscribable
,
id
)
{
var
inOld
;
if
(
!
_isDisposed
)
{
if
((
inOld
=
ko
.
utils
.
arrayIndexOf
(
disposalCandidates
,
subscribable
))
>=
0
)
if
(
disposalCount
&&
disposalCandidates
[
id
])
{
disposalCandidates
[
inOld
]
=
undefined
;
// Don't want to dispose this subscription, as it's still being used
// Don't want to dispose this subscription, as it's still being used
else
_subscriptionsToDependencies
[
id
]
=
disposalCandidates
[
id
];
addSubscriptionToDependency
(
subscribable
);
// Brand new subscription - add it
++
_dependenciesCount
;
delete
disposalCandidates
[
id
];
--
disposalCount
;
}
else
{
// Brand new subscription - add it
addSubscriptionToDependency
(
subscribable
,
id
);
}
}
},
computed
:
dependentObservable
,
isInitial
:
!
_dependenciesCount
// If we're evaluating when there are no previous dependencies, it must be the first time
});
});
var
newValue
=
evaluatorFunctionTarget
?
readFunction
.
call
(
evaluatorFunctionTarget
)
:
readFunction
();
_subscriptionsToDependencies
=
{};
_dependenciesCount
=
0
;
// For each subscription no longer being used, remove it from the active subscriptions list and dispose it
try
{
for
(
var
i
=
disposalCandidates
.
length
-
1
;
i
>=
0
;
i
--
)
{
var
newValue
=
evaluatorFunctionTarget
?
readFunction
.
call
(
evaluatorFunctionTarget
)
:
readFunction
();
if
(
disposalCandidates
[
i
])
_subscriptionsToDependencies
.
splice
(
i
,
1
)[
0
].
dispose
();
}
finally
{
ko
.
dependencyDetection
.
end
();
// For each subscription no longer being used, remove it from the active subscriptions list and dispose it
if
(
disposalCount
)
{
ko
.
utils
.
objectForEach
(
disposalCandidates
,
function
(
id
,
toDispose
)
{
toDispose
.
dispose
();
});
}
_needsEvaluation
=
false
;
}
}
_hasBeenEvaluated
=
true
;
if
(
!
dependentObservable
[
'
equalityComparer
'
]
||
!
dependentObservable
[
'
equalityComparer
'
]
(
_latestValue
,
newValue
))
{
if
(
dependentObservable
.
isDifferent
(
_latestValue
,
newValue
))
{
dependentObservable
[
"
notifySubscribers
"
](
_latestValue
,
"
beforeChange
"
);
dependentObservable
[
"
notifySubscribers
"
](
_latestValue
,
"
beforeChange
"
);
_latestValue
=
newValue
;
_latestValue
=
newValue
;
if
(
DEBUG
)
dependentObservable
.
_latestValue
=
_latestValue
;
if
(
DEBUG
)
dependentObservable
.
_latestValue
=
_latestValue
;
dependentObservable
[
"
notifySubscribers
"
](
_latestValue
);
// If rate-limited, the notification will happen within the limit function. Otherwise,
// notify as soon as the value changes. Check specifically for the throttle setting since
// it overrides rateLimit.
if
(
!
dependentObservable
.
_evalRateLimited
||
dependentObservable
[
'
throttleEvaluation
'
])
{
dependentObservable
[
"
notifySubscribers
"
](
_latestValue
);
}
}
}
}
finally
{
}
finally
{
ko
.
dependencyDetection
.
end
();
_isBeingEvaluated
=
false
;
_isBeingEvaluated
=
false
;
}
}
if
(
!
_
subscriptionsToDependencies
.
length
)
if
(
!
_
dependenciesCount
)
dispose
();
dispose
();
}
}
...
@@ -1500,7 +1692,7 @@ ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunction
...
@@ -1500,7 +1692,7 @@ ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunction
return
this
;
// Permits chained assignments
return
this
;
// Permits chained assignments
}
else
{
}
else
{
// Reading the value
// Reading the value
if
(
!
_hasBeenEvaluated
)
if
(
_needsEvaluation
)
evaluateImmediate
();
evaluateImmediate
();
ko
.
dependencyDetection
.
registerDependency
(
dependentObservable
);
ko
.
dependencyDetection
.
registerDependency
(
dependentObservable
);
return
_latestValue
;
return
_latestValue
;
...
@@ -1508,13 +1700,15 @@ ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunction
...
@@ -1508,13 +1700,15 @@ ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunction
}
}
function
peek
()
{
function
peek
()
{
if
(
!
_hasBeenEvaluated
)
// Peek won't re-evaluate, except to get the initial value when "deferEvaluation" is set.
// That's the only time that both of these conditions will be satisfied.
if
(
_needsEvaluation
&&
!
_dependenciesCount
)
evaluateImmediate
();
evaluateImmediate
();
return
_latestValue
;
return
_latestValue
;
}
}
function
isActive
()
{
function
isActive
()
{
return
!
_hasBeenEvaluated
||
_subscriptionsToDependencies
.
length
>
0
;
return
_needsEvaluation
||
_dependenciesCount
>
0
;
}
}
// By here, "options" is always non-null
// By here, "options" is always non-null
...
@@ -1523,20 +1717,36 @@ ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunction
...
@@ -1523,20 +1717,36 @@ ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunction
disposeWhenOption
=
options
[
"
disposeWhen
"
]
||
options
.
disposeWhen
,
disposeWhenOption
=
options
[
"
disposeWhen
"
]
||
options
.
disposeWhen
,
disposeWhen
=
disposeWhenOption
,
disposeWhen
=
disposeWhenOption
,
dispose
=
disposeAllSubscriptionsToDependencies
,
dispose
=
disposeAllSubscriptionsToDependencies
,
_subscriptionsToDependencies
=
[],
_subscriptionsToDependencies
=
{},
_dependenciesCount
=
0
,
evaluationTimeoutInstance
=
null
;
evaluationTimeoutInstance
=
null
;
if
(
!
evaluatorFunctionTarget
)
if
(
!
evaluatorFunctionTarget
)
evaluatorFunctionTarget
=
options
[
"
owner
"
];
evaluatorFunctionTarget
=
options
[
"
owner
"
];
ko
.
subscribable
.
call
(
dependentObservable
);
ko
.
utils
.
setPrototypeOfOrExtend
(
dependentObservable
,
ko
.
dependentObservable
[
'
fn
'
]);
dependentObservable
.
peek
=
peek
;
dependentObservable
.
peek
=
peek
;
dependentObservable
.
getDependenciesCount
=
function
()
{
return
_
subscriptionsToDependencies
.
length
;
};
dependentObservable
.
getDependenciesCount
=
function
()
{
return
_
dependenciesCount
;
};
dependentObservable
.
hasWriteFunction
=
typeof
options
[
"
write
"
]
===
"
function
"
;
dependentObservable
.
hasWriteFunction
=
typeof
options
[
"
write
"
]
===
"
function
"
;
dependentObservable
.
dispose
=
function
()
{
dispose
();
};
dependentObservable
.
dispose
=
function
()
{
dispose
();
};
dependentObservable
.
isActive
=
isActive
;
dependentObservable
.
isActive
=
isActive
;
ko
.
subscribable
.
call
(
dependentObservable
);
// Replace the limit function with one that delays evaluation as well.
ko
.
utils
.
extend
(
dependentObservable
,
ko
.
dependentObservable
[
'
fn
'
]);
var
originalLimit
=
dependentObservable
.
limit
;
dependentObservable
.
limit
=
function
(
limitFunction
)
{
originalLimit
.
call
(
dependentObservable
,
limitFunction
);
dependentObservable
.
_evalRateLimited
=
function
()
{
dependentObservable
.
_rateLimitedBeforeChange
(
_latestValue
);
_needsEvaluation
=
true
;
// Mark as dirty
// Pass the observable to the rate-limit code, which will access it when
// it's time to do the notification.
dependentObservable
.
_rateLimitedChange
(
dependentObservable
);
}
};
ko
.
exportProperty
(
dependentObservable
,
'
peek
'
,
dependentObservable
.
peek
);
ko
.
exportProperty
(
dependentObservable
,
'
peek
'
,
dependentObservable
.
peek
);
ko
.
exportProperty
(
dependentObservable
,
'
dispose
'
,
dependentObservable
.
dispose
);
ko
.
exportProperty
(
dependentObservable
,
'
dispose
'
,
dependentObservable
.
dispose
);
...
@@ -1568,7 +1778,7 @@ ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunction
...
@@ -1568,7 +1778,7 @@ ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunction
// Attach a DOM node disposal callback so that the computed will be proactively disposed as soon as the node is
// Attach a DOM node disposal callback so that the computed will be proactively disposed as soon as the node is
// removed using ko.removeNode. But skip if isActive is false (there will never be any dependencies to dispose).
// removed using ko.removeNode. But skip if isActive is false (there will never be any dependencies to dispose).
if
(
disposeWhenNodeIsRemoved
&&
isActive
())
{
if
(
disposeWhenNodeIsRemoved
&&
isActive
()
&&
disposeWhenNodeIsRemoved
.
nodeType
)
{
dispose
=
function
()
{
dispose
=
function
()
{
ko
.
utils
.
domNodeDisposal
.
removeDisposeCallback
(
disposeWhenNodeIsRemoved
,
dispose
);
ko
.
utils
.
domNodeDisposal
.
removeDisposeCallback
(
disposeWhenNodeIsRemoved
,
dispose
);
disposeAllSubscriptionsToDependencies
();
disposeAllSubscriptionsToDependencies
();
...
@@ -1591,6 +1801,12 @@ ko.dependentObservable['fn'] = {
...
@@ -1591,6 +1801,12 @@ ko.dependentObservable['fn'] = {
};
};
ko
.
dependentObservable
[
'
fn
'
][
protoProp
]
=
ko
.
dependentObservable
;
ko
.
dependentObservable
[
'
fn
'
][
protoProp
]
=
ko
.
dependentObservable
;
// Note that for browsers that don't support proto assignment, the
// inheritance chain is created manually in the ko.dependentObservable constructor
if
(
ko
.
utils
.
canSetPrototype
)
{
ko
.
utils
.
setPrototypeOf
(
ko
.
dependentObservable
[
'
fn
'
],
ko
.
subscribable
[
'
fn
'
]);
}
ko
.
exportSymbol
(
'
dependentObservable
'
,
ko
.
dependentObservable
);
ko
.
exportSymbol
(
'
dependentObservable
'
,
ko
.
dependentObservable
);
ko
.
exportSymbol
(
'
computed
'
,
ko
.
dependentObservable
);
// Make "ko.computed" an alias for "ko.dependentObservable"
ko
.
exportSymbol
(
'
computed
'
,
ko
.
dependentObservable
);
// Make "ko.computed" an alias for "ko.dependentObservable"
ko
.
exportSymbol
(
'
isComputed
'
,
ko
.
isComputed
);
ko
.
exportSymbol
(
'
isComputed
'
,
ko
.
isComputed
);
...
@@ -1712,7 +1928,7 @@ ko.exportSymbol('toJSON', ko.toJSON);
...
@@ -1712,7 +1928,7 @@ ko.exportSymbol('toJSON', ko.toJSON);
}
}
},
},
writeValue
:
function
(
element
,
value
)
{
writeValue
:
function
(
element
,
value
,
allowUnset
)
{
switch
(
ko
.
utils
.
tagNameLower
(
element
))
{
switch
(
ko
.
utils
.
tagNameLower
(
element
))
{
case
'
option
'
:
case
'
option
'
:
switch
(
typeof
value
)
{
switch
(
typeof
value
)
{
...
@@ -1734,19 +1950,19 @@ ko.exportSymbol('toJSON', ko.toJSON);
...
@@ -1734,19 +1950,19 @@ ko.exportSymbol('toJSON', ko.toJSON);
}
}
break
;
break
;
case
'
select
'
:
case
'
select
'
:
if
(
value
===
""
)
if
(
value
===
""
||
value
===
null
)
// A blank string or null value will select the caption
value
=
undefined
;
value
=
undefined
;
if
(
value
===
null
||
value
===
undefined
)
var
selection
=
-
1
;
element
.
selectedIndex
=
-
1
;
for
(
var
i
=
0
,
n
=
element
.
options
.
length
,
optionValue
;
i
<
n
;
++
i
)
{
for
(
var
i
=
element
.
options
.
length
-
1
;
i
>=
0
;
i
--
)
{
optionValue
=
ko
.
selectExtensions
.
readValue
(
element
.
options
[
i
]);
if
(
ko
.
selectExtensions
.
readValue
(
element
.
options
[
i
])
==
value
)
{
// Include special check to handle selecting a caption with a blank string value
element
.
selectedIndex
=
i
;
if
(
optionValue
==
value
||
(
optionValue
==
""
&&
value
===
undefined
))
{
selection
=
i
;
break
;
break
;
}
}
}
}
// for drop-down select, ensure first is selected
if
(
allowUnset
||
selection
>=
0
||
(
value
===
undefined
&&
element
.
size
>
1
))
{
if
(
!
(
element
.
size
>
1
)
&&
element
.
selectedIndex
===
-
1
)
{
element
.
selectedIndex
=
selection
;
element
.
selectedIndex
=
0
;
}
}
break
;
break
;
default
:
default
:
...
@@ -1902,7 +2118,7 @@ ko.expressionRewriting = (function () {
...
@@ -1902,7 +2118,7 @@ ko.expressionRewriting = (function () {
});
});
if
(
propertyAccessorResultStrings
.
length
)
if
(
propertyAccessorResultStrings
.
length
)
processKeyValue
(
'
_ko_property_writers
'
,
"
{
"
+
propertyAccessorResultStrings
.
join
(
"
,
"
)
+
"
}
"
);
processKeyValue
(
'
_ko_property_writers
'
,
"
{
"
+
propertyAccessorResultStrings
.
join
(
"
,
"
)
+
"
}
"
);
return
resultStrings
.
join
(
"
,
"
);
return
resultStrings
.
join
(
"
,
"
);
}
}
...
@@ -2157,6 +2373,336 @@ ko.exportSymbol('virtualElements.insertAfter', ko.virtualElements.insertAfter);
...
@@ -2157,6 +2373,336 @@ ko.exportSymbol('virtualElements.insertAfter', ko.virtualElements.insertAfter);
//ko.exportSymbol('virtualElements.nextSibling', ko.virtualElements.nextSibling); // nextSibling is not minified
//ko.exportSymbol('virtualElements.nextSibling', ko.virtualElements.nextSibling); // nextSibling is not minified
ko
.
exportSymbol
(
'
virtualElements.prepend
'
,
ko
.
virtualElements
.
prepend
);
ko
.
exportSymbol
(
'
virtualElements.prepend
'
,
ko
.
virtualElements
.
prepend
);
ko
.
exportSymbol
(
'
virtualElements.setDomNodeChildren
'
,
ko
.
virtualElements
.
setDomNodeChildren
);
ko
.
exportSymbol
(
'
virtualElements.setDomNodeChildren
'
,
ko
.
virtualElements
.
setDomNodeChildren
);
(
function
(
undefined
)
{
var
loadingSubscribablesCache
=
{},
// Tracks component loads that are currently in flight
loadedDefinitionsCache
=
{};
// Tracks component loads that have already completed
ko
.
components
=
{
get
:
function
(
componentName
,
callback
)
{
var
cachedDefinition
=
getObjectOwnProperty
(
loadedDefinitionsCache
,
componentName
);
if
(
cachedDefinition
)
{
// It's already loaded and cached. Reuse the same definition object.
// Note that for API consistency, even cache hits complete asynchronously.
setTimeout
(
function
()
{
callback
(
cachedDefinition
)
},
0
);
}
else
{
// Join the loading process that is already underway, or start a new one.
loadComponentAndNotify
(
componentName
,
callback
);
}
},
clearCachedDefinition
:
function
(
componentName
)
{
delete
loadedDefinitionsCache
[
componentName
];
},
_getFirstResultFromLoaders
:
getFirstResultFromLoaders
};
function
getObjectOwnProperty
(
obj
,
propName
)
{
return
obj
.
hasOwnProperty
(
propName
)
?
obj
[
propName
]
:
undefined
;
}
function
loadComponentAndNotify
(
componentName
,
callback
)
{
var
subscribable
=
getObjectOwnProperty
(
loadingSubscribablesCache
,
componentName
),
completedAsync
;
if
(
!
subscribable
)
{
// It's not started loading yet. Start loading, and when it's done, move it to loadedDefinitionsCache.
subscribable
=
loadingSubscribablesCache
[
componentName
]
=
new
ko
.
subscribable
();
beginLoadingComponent
(
componentName
,
function
(
definition
)
{
loadedDefinitionsCache
[
componentName
]
=
definition
;
delete
loadingSubscribablesCache
[
componentName
];
// For API consistency, all loads complete asynchronously. However we want to avoid
// adding an extra setTimeout if it's unnecessary (i.e., the completion is already
// async) since setTimeout(..., 0) still takes about 16ms or more on most browsers.
if
(
completedAsync
)
{
subscribable
[
'
notifySubscribers
'
](
definition
);
}
else
{
setTimeout
(
function
()
{
subscribable
[
'
notifySubscribers
'
](
definition
);
},
0
);
}
});
completedAsync
=
true
;
}
subscribable
.
subscribe
(
callback
);
}
function
beginLoadingComponent
(
componentName
,
callback
)
{
getFirstResultFromLoaders
(
'
getConfig
'
,
[
componentName
],
function
(
config
)
{
if
(
config
)
{
// We have a config, so now load its definition
getFirstResultFromLoaders
(
'
loadComponent
'
,
[
componentName
,
config
],
function
(
definition
)
{
callback
(
definition
);
});
}
else
{
// The component has no config - it's unknown to all the loaders.
// Note that this is not an error (e.g., a module loading error) - that would abort the
// process and this callback would not run. For this callback to run, all loaders must
// have confirmed they don't know about this component.
callback
(
null
);
}
});
}
function
getFirstResultFromLoaders
(
methodName
,
argsExceptCallback
,
callback
,
candidateLoaders
)
{
// On the first call in the stack, start with the full set of loaders
if
(
!
candidateLoaders
)
{
candidateLoaders
=
ko
.
components
[
'
loaders
'
].
slice
(
0
);
// Use a copy, because we'll be mutating this array
}
// Try the next candidate
var
currentCandidateLoader
=
candidateLoaders
.
shift
();
if
(
currentCandidateLoader
)
{
var
methodInstance
=
currentCandidateLoader
[
methodName
];
if
(
methodInstance
)
{
var
wasAborted
=
false
,
synchronousReturnValue
=
methodInstance
.
apply
(
currentCandidateLoader
,
argsExceptCallback
.
concat
(
function
(
result
)
{
if
(
wasAborted
)
{
callback
(
null
);
}
else
if
(
result
!==
null
)
{
// This candidate returned a value. Use it.
callback
(
result
);
}
else
{
// Try the next candidate
getFirstResultFromLoaders
(
methodName
,
argsExceptCallback
,
callback
,
candidateLoaders
);
}
}));
// Currently, loaders may not return anything synchronously. This leaves open the possibility
// that we'll extend the API to support synchronous return values in the future. It won't be
// a breaking change, because currently no loader is allowed to return anything except undefined.
if
(
synchronousReturnValue
!==
undefined
)
{
wasAborted
=
true
;
// Method to suppress exceptions will remain undocumented. This is only to keep
// KO's specs running tidily, since we can observe the loading got aborted without
// having exceptions cluttering up the console too.
if
(
!
currentCandidateLoader
[
'
suppressLoaderExceptions
'
])
{
throw
new
Error
(
'
Component loaders must supply values by invoking the callback, not by returning values synchronously.
'
);
}
}
}
else
{
// This candidate doesn't have the relevant handler. Synchronously move on to the next one.
getFirstResultFromLoaders
(
methodName
,
argsExceptCallback
,
callback
,
candidateLoaders
);
}
}
else
{
// No candidates returned a value
callback
(
null
);
}
}
// Reference the loaders via string name so it's possible for developers
// to replace the whole array by assigning to ko.components.loaders
ko
.
components
[
'
loaders
'
]
=
[];
ko
.
exportSymbol
(
'
components
'
,
ko
.
components
);
ko
.
exportSymbol
(
'
components.get
'
,
ko
.
components
.
get
);
ko
.
exportSymbol
(
'
components.clearCachedDefinition
'
,
ko
.
components
.
clearCachedDefinition
);
})();
(
function
(
undefined
)
{
// The default loader is responsible for two things:
// 1. Maintaining the default in-memory registry of component configuration objects
// (i.e., the thing you're writing to when you call ko.components.register(someName, ...))
// 2. Answering requests for components by fetching configuration objects
// from that default in-memory registry and resolving them into standard
// component definition objects (of the form { createViewModel: ..., template: ... })
// Custom loaders may override either of these facilities, i.e.,
// 1. To supply configuration objects from some other source (e.g., conventions)
// 2. Or, to resolve configuration objects by loading viewmodels/templates via arbitrary logic.
var
defaultConfigRegistry
=
{};
ko
.
components
.
register
=
function
(
componentName
,
config
)
{
if
(
!
config
)
{
throw
new
Error
(
'
Invalid configuration for
'
+
componentName
);
}
if
(
ko
.
components
.
isRegistered
(
componentName
))
{
throw
new
Error
(
'
Component
'
+
componentName
+
'
is already registered
'
);
}
defaultConfigRegistry
[
componentName
]
=
config
;
}
ko
.
components
.
isRegistered
=
function
(
componentName
)
{
return
componentName
in
defaultConfigRegistry
;
}
ko
.
components
.
unregister
=
function
(
componentName
)
{
delete
defaultConfigRegistry
[
componentName
];
ko
.
components
.
clearCachedDefinition
(
componentName
);
}
ko
.
components
.
defaultLoader
=
{
'
getConfig
'
:
function
(
componentName
,
callback
)
{
var
result
=
defaultConfigRegistry
.
hasOwnProperty
(
componentName
)
?
defaultConfigRegistry
[
componentName
]
:
null
;
callback
(
result
);
},
'
loadComponent
'
:
function
(
componentName
,
config
,
callback
)
{
var
errorCallback
=
makeErrorCallback
(
componentName
);
possiblyGetConfigFromAmd
(
errorCallback
,
config
,
function
(
loadedConfig
)
{
resolveConfig
(
componentName
,
errorCallback
,
loadedConfig
,
callback
);
});
},
'
loadTemplate
'
:
function
(
componentName
,
templateConfig
,
callback
)
{
resolveTemplate
(
makeErrorCallback
(
componentName
),
templateConfig
,
callback
);
},
'
loadViewModel
'
:
function
(
componentName
,
viewModelConfig
,
callback
)
{
resolveViewModel
(
makeErrorCallback
(
componentName
),
viewModelConfig
,
callback
);
}
};
var
createViewModelKey
=
'
createViewModel
'
;
// Takes a config object of the form { template: ..., viewModel: ... }, and asynchronously convert it
// into the standard component definition format:
// { template: <ArrayOfDomNodes>, createViewModel: function(params, componentInfo) { ... } }.
// Since both template and viewModel may need to be resolved asynchronously, both tasks are performed
// in parallel, and the results joined when both are ready. We don't depend on any promises infrastructure,
// so this is implemented manually below.
function
resolveConfig
(
componentName
,
errorCallback
,
config
,
callback
)
{
var
result
=
{},
makeCallBackWhenZero
=
2
,
tryIssueCallback
=
function
()
{
if
(
--
makeCallBackWhenZero
===
0
)
{
callback
(
result
);
}
},
templateConfig
=
config
[
'
template
'
],
viewModelConfig
=
config
[
'
viewModel
'
];
if
(
templateConfig
)
{
possiblyGetConfigFromAmd
(
errorCallback
,
templateConfig
,
function
(
loadedConfig
)
{
ko
.
components
.
_getFirstResultFromLoaders
(
'
loadTemplate
'
,
[
componentName
,
loadedConfig
],
function
(
resolvedTemplate
)
{
result
[
'
template
'
]
=
resolvedTemplate
;
tryIssueCallback
();
});
});
}
else
{
tryIssueCallback
();
}
if
(
viewModelConfig
)
{
possiblyGetConfigFromAmd
(
errorCallback
,
viewModelConfig
,
function
(
loadedConfig
)
{
ko
.
components
.
_getFirstResultFromLoaders
(
'
loadViewModel
'
,
[
componentName
,
loadedConfig
],
function
(
resolvedViewModel
)
{
result
[
createViewModelKey
]
=
resolvedViewModel
;
tryIssueCallback
();
});
});
}
else
{
tryIssueCallback
();
}
}
function
resolveTemplate
(
errorCallback
,
templateConfig
,
callback
)
{
if
(
typeof
templateConfig
===
'
string
'
)
{
// Markup - parse it
callback
(
ko
.
utils
.
parseHtmlFragment
(
templateConfig
));
}
else
if
(
templateConfig
instanceof
Array
)
{
// Assume already an array of DOM nodes - pass through unchanged
callback
(
templateConfig
);
}
else
if
(
isDocumentFragment
(
templateConfig
))
{
// Document fragment - use its child nodes
callback
(
ko
.
utils
.
makeArray
(
templateConfig
.
childNodes
));
}
else
if
(
templateConfig
[
'
element
'
])
{
var
element
=
templateConfig
[
'
element
'
];
if
(
isDomElement
(
element
))
{
// Element instance - copy its child nodes
callback
(
ko
.
utils
.
cloneNodes
(
element
.
childNodes
));
}
else
if
(
typeof
element
===
'
string
'
)
{
// Element ID - find it, then copy its child nodes
var
elemInstance
=
document
.
getElementById
(
element
);
if
(
elemInstance
)
{
callback
(
ko
.
utils
.
cloneNodes
(
elemInstance
.
childNodes
));
}
else
{
errorCallback
(
'
Cannot find element with ID
'
+
element
);
}
}
else
{
errorCallback
(
'
Unknown element type:
'
+
element
);
}
}
else
{
errorCallback
(
'
Unknown template value:
'
+
templateConfig
);
}
}
function
resolveViewModel
(
errorCallback
,
viewModelConfig
,
callback
)
{
if
(
typeof
viewModelConfig
===
'
function
'
)
{
// Constructor - convert to standard factory function format
// By design, this does *not* supply componentInfo to the constructor, as the intent is that
// componentInfo contains non-viewmodel data (e.g., the component's element) that should only
// be used in factory functions, not viewmodel constructors.
callback
(
function
(
params
/*, componentInfo */
)
{
return
new
viewModelConfig
(
params
);
});
}
else
if
(
typeof
viewModelConfig
[
createViewModelKey
]
===
'
function
'
)
{
// Already a factory function - use it as-is
callback
(
viewModelConfig
[
createViewModelKey
]);
}
else
if
(
'
instance
'
in
viewModelConfig
)
{
// Fixed object instance - promote to createViewModel format for API consistency
var
fixedInstance
=
viewModelConfig
[
'
instance
'
];
callback
(
function
(
params
,
componentInfo
)
{
return
fixedInstance
;
});
}
else
if
(
'
viewModel
'
in
viewModelConfig
)
{
// Resolved AMD module whose value is of the form { viewModel: ... }
resolveViewModel
(
errorCallback
,
viewModelConfig
[
'
viewModel
'
],
callback
);
}
else
{
errorCallback
(
'
Unknown viewModel value:
'
+
viewModelConfig
);
}
}
function
isDomElement
(
obj
)
{
if
(
window
[
'
HTMLElement
'
])
{
return
obj
instanceof
HTMLElement
;
}
else
{
return
obj
&&
obj
.
tagName
&&
obj
.
nodeType
===
1
;
}
}
function
isDocumentFragment
(
obj
)
{
if
(
window
[
'
DocumentFragment
'
])
{
return
obj
instanceof
DocumentFragment
;
}
else
{
return
obj
&&
obj
.
nodeType
===
11
;
}
}
function
possiblyGetConfigFromAmd
(
errorCallback
,
config
,
callback
)
{
if
(
typeof
config
[
'
require
'
]
===
'
string
'
)
{
// The config is the value of an AMD module
if
(
require
||
window
[
'
require
'
])
{
(
require
||
window
[
'
require
'
])([
config
[
'
require
'
]],
callback
);
}
else
{
errorCallback
(
'
Uses require, but no AMD loader is present
'
);
}
}
else
{
callback
(
config
);
}
}
function
makeErrorCallback
(
componentName
)
{
return
function
(
message
)
{
throw
new
Error
(
'
Component
\'
'
+
componentName
+
'
\'
:
'
+
message
);
};
}
ko
.
exportSymbol
(
'
components.register
'
,
ko
.
components
.
register
);
ko
.
exportSymbol
(
'
components.isRegistered
'
,
ko
.
components
.
isRegistered
);
ko
.
exportSymbol
(
'
components.unregister
'
,
ko
.
components
.
unregister
);
// Expose the default loader so that developers can directly ask it for configuration
// or to resolve configuration
ko
.
exportSymbol
(
'
components.defaultLoader
'
,
ko
.
components
.
defaultLoader
);
// By default, the default loader is the only registered component loader
ko
.
components
[
'
loaders
'
].
push
(
ko
.
components
.
defaultLoader
);
})();
(
function
()
{
(
function
()
{
var
defaultBindingAttributeName
=
"
data-bind
"
;
var
defaultBindingAttributeName
=
"
data-bind
"
;
...
@@ -2251,10 +2797,11 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
...
@@ -2251,10 +2797,11 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
// any child contexts, must be updated when the view model is changed.
// any child contexts, must be updated when the view model is changed.
function
updateContext
()
{
function
updateContext
()
{
// Most of the time, the context will directly get a view model object, but if a function is given,
// Most of the time, the context will directly get a view model object, but if a function is given,
// we call the function to retrieve the view model. If the function accesses any obsevables
(or i
s
// we call the function to retrieve the view model. If the function accesses any obsevables
or return
s
//
itself an observable)
, the dependency is tracked, and those observables can later cause the binding
//
an observable
, the dependency is tracked, and those observables can later cause the binding
// context to be updated.
// context to be updated.
var
dataItem
=
isFunc
?
dataItemOrAccessor
()
:
dataItemOrAccessor
;
var
dataItemOrObservable
=
isFunc
?
dataItemOrAccessor
()
:
dataItemOrAccessor
,
dataItem
=
ko
.
utils
.
unwrapObservable
(
dataItemOrObservable
);
if
(
parentContext
)
{
if
(
parentContext
)
{
// When a "parent" context is given, register a dependency on the parent context. Thus whenever the
// When a "parent" context is given, register a dependency on the parent context. Thus whenever the
...
@@ -2279,7 +2826,7 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
...
@@ -2279,7 +2826,7 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
// See https://github.com/SteveSanderson/knockout/issues/490
// See https://github.com/SteveSanderson/knockout/issues/490
self
[
'
ko
'
]
=
ko
;
self
[
'
ko
'
]
=
ko
;
}
}
self
[
'
$rawData
'
]
=
dataItemOr
Accessor
;
self
[
'
$rawData
'
]
=
dataItemOr
Observable
;
self
[
'
$data
'
]
=
dataItem
;
self
[
'
$data
'
]
=
dataItem
;
if
(
dataItemAlias
)
if
(
dataItemAlias
)
self
[
dataItemAlias
]
=
dataItem
;
self
[
dataItemAlias
]
=
dataItem
;
...
@@ -2297,7 +2844,7 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
...
@@ -2297,7 +2844,7 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
}
}
var
self
=
this
,
var
self
=
this
,
isFunc
=
typeof
(
dataItemOrAccessor
)
==
"
function
"
,
isFunc
=
typeof
(
dataItemOrAccessor
)
==
"
function
"
&&
!
ko
.
isObservable
(
dataItemOrAccessor
)
,
nodes
,
nodes
,
subscribable
=
ko
.
dependentObservable
(
updateContext
,
null
,
{
disposeWhen
:
disposeWhen
,
disposeWhenNodeIsRemoved
:
true
});
subscribable
=
ko
.
dependentObservable
(
updateContext
,
null
,
{
disposeWhen
:
disposeWhen
,
disposeWhenNodeIsRemoved
:
true
});
...
@@ -2352,7 +2899,12 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
...
@@ -2352,7 +2899,12 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
// Similarly to "child" contexts, provide a function here to make sure that the correct values are set
// Similarly to "child" contexts, provide a function here to make sure that the correct values are set
// when an observable view model is updated.
// when an observable view model is updated.
ko
.
bindingContext
.
prototype
[
'
extend
'
]
=
function
(
properties
)
{
ko
.
bindingContext
.
prototype
[
'
extend
'
]
=
function
(
properties
)
{
return
new
ko
.
bindingContext
(
this
[
'
$rawData
'
],
this
,
null
,
function
(
self
)
{
// If the parent context references an observable view model, "_subscribable" will always be the
// latest view model object. If not, "_subscribable" isn't set, and we can use the static "$data" value.
return
new
ko
.
bindingContext
(
this
.
_subscribable
||
this
[
'
$data
'
],
this
,
null
,
function
(
self
,
parentContext
)
{
// This "child" context doesn't directly track a parent observable view model,
// so we need to manually set the $rawData value to match the parent.
self
[
'
$rawData
'
]
=
parentContext
[
'
$rawData
'
];
ko
.
utils
.
extend
(
self
,
typeof
(
properties
)
==
"
function
"
?
properties
()
:
properties
);
ko
.
utils
.
extend
(
self
,
typeof
(
properties
)
==
"
function
"
?
properties
()
:
properties
);
});
});
};
};
...
@@ -2480,7 +3032,7 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
...
@@ -2480,7 +3032,7 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
}
}
}
}
});
});
cyclicDependencyStack
.
pop
()
;
cyclicDependencyStack
.
length
--
;
}
}
// Next add the current binding
// Next add the current binding
result
.
push
({
key
:
bindingKey
,
handler
:
binding
});
result
.
push
({
key
:
bindingKey
,
handler
:
binding
});
...
@@ -2516,26 +3068,21 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
...
@@ -2516,26 +3068,21 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
var
provider
=
ko
.
bindingProvider
[
'
instance
'
],
var
provider
=
ko
.
bindingProvider
[
'
instance
'
],
getBindings
=
provider
[
'
getBindingAccessors
'
]
||
getBindingsAndMakeAccessors
;
getBindings
=
provider
[
'
getBindingAccessors
'
]
||
getBindingsAndMakeAccessors
;
if
(
sourceBindings
||
bindingContext
.
_subscribable
)
{
// Get the binding from the provider within a computed observable so that we can update the bindings whenever
// When an obsevable view model is used, the binding context will expose an observable _subscribable value.
// the binding context is updated or if the binding provider accesses observables.
// Get the binding from the provider within a computed observable so that we can update the bindings whenever
var
bindingsUpdater
=
ko
.
dependentObservable
(
// the binding context is updated.
function
()
{
var
bindingsUpdater
=
ko
.
dependentObservable
(
bindings
=
sourceBindings
?
sourceBindings
(
bindingContext
,
node
)
:
getBindings
.
call
(
provider
,
node
,
bindingContext
);
function
()
{
// Register a dependency on the binding context to support obsevable view models.
bindings
=
sourceBindings
?
sourceBindings
(
bindingContext
,
node
)
:
getBindings
.
call
(
provider
,
node
,
bindingContext
);
if
(
bindings
&&
bindingContext
.
_subscribable
)
// Register a dependency on the binding context
bindingContext
.
_subscribable
();
if
(
bindings
&&
bindingContext
.
_subscribable
)
return
bindings
;
bindingContext
.
_subscribable
();
},
return
bindings
;
null
,
{
disposeWhenNodeIsRemoved
:
node
}
},
);
null
,
{
disposeWhenNodeIsRemoved
:
node
}
);
if
(
!
bindings
||
!
bindingsUpdater
.
isActive
())
bindingsUpdater
=
null
;
if
(
!
bindings
||
!
bindingsUpdater
.
isActive
())
bindingsUpdater
=
null
;
}
else
{
bindings
=
ko
.
dependencyDetection
.
ignore
(
getBindings
,
provider
,
[
node
,
bindingContext
]);
}
}
}
var
bindingHandlerThatControlsDescendantBindings
;
var
bindingHandlerThatControlsDescendantBindings
;
...
@@ -2650,6 +3197,11 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
...
@@ -2650,6 +3197,11 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
};
};
ko
.
applyBindings
=
function
(
viewModelOrBindingContext
,
rootNode
)
{
ko
.
applyBindings
=
function
(
viewModelOrBindingContext
,
rootNode
)
{
// If jQuery is loaded after Knockout, we won't initially have access to it. So save it here.
if
(
!
jQuery
&&
window
[
'
jQuery
'
])
{
jQuery
=
window
[
'
jQuery
'
];
}
if
(
rootNode
&&
(
rootNode
.
nodeType
!==
1
)
&&
(
rootNode
.
nodeType
!==
8
))
if
(
rootNode
&&
(
rootNode
.
nodeType
!==
1
)
&&
(
rootNode
.
nodeType
!==
8
))
throw
new
Error
(
"
ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node
"
);
throw
new
Error
(
"
ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node
"
);
rootNode
=
rootNode
||
window
.
document
.
body
;
// Make "rootNode" parameter optional
rootNode
=
rootNode
||
window
.
document
.
body
;
// Make "rootNode" parameter optional
...
@@ -2683,6 +3235,87 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
...
@@ -2683,6 +3235,87 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
ko
.
exportSymbol
(
'
contextFor
'
,
ko
.
contextFor
);
ko
.
exportSymbol
(
'
contextFor
'
,
ko
.
contextFor
);
ko
.
exportSymbol
(
'
dataFor
'
,
ko
.
dataFor
);
ko
.
exportSymbol
(
'
dataFor
'
,
ko
.
dataFor
);
})();
})();
(
function
(
undefined
)
{
var
componentLoadingOperationUniqueId
=
0
;
ko
.
bindingHandlers
[
'
component
'
]
=
{
'
init
'
:
function
(
element
,
valueAccessor
,
ignored1
,
ignored2
,
bindingContext
)
{
var
currentViewModel
,
currentLoadingOperationId
,
disposeAssociatedComponentViewModel
=
function
()
{
var
currentViewModelDispose
=
currentViewModel
&&
currentViewModel
[
'
dispose
'
];
if
(
typeof
currentViewModelDispose
===
'
function
'
)
{
currentViewModelDispose
.
call
(
currentViewModel
);
}
// Any in-flight loading operation is no longer relevant, so make sure we ignore its completion
currentLoadingOperationId
=
null
;
};
ko
.
utils
.
domNodeDisposal
.
addDisposeCallback
(
element
,
disposeAssociatedComponentViewModel
);
ko
.
computed
(
function
()
{
var
value
=
ko
.
utils
.
unwrapObservable
(
valueAccessor
()),
componentName
,
componentParams
;
if
(
typeof
value
===
'
string
'
)
{
componentName
=
value
;
}
else
{
componentName
=
ko
.
utils
.
unwrapObservable
(
value
[
'
name
'
]);
componentParams
=
ko
.
utils
.
unwrapObservable
(
value
[
'
params
'
]);
}
if
(
!
componentName
)
{
throw
new
Error
(
'
No component name specified
'
);
}
var
loadingOperationId
=
currentLoadingOperationId
=
++
componentLoadingOperationUniqueId
;
ko
.
components
.
get
(
componentName
,
function
(
componentDefinition
)
{
// If this is not the current load operation for this element, ignore it.
if
(
currentLoadingOperationId
!==
loadingOperationId
)
{
return
;
}
// Clean up previous state
disposeAssociatedComponentViewModel
();
// Instantiate and bind new component. Implicitly this cleans any old DOM nodes.
if
(
!
componentDefinition
)
{
throw
new
Error
(
'
Unknown component
\'
'
+
componentName
+
'
\'
'
);
}
cloneTemplateIntoElement
(
componentName
,
componentDefinition
,
element
);
var
componentViewModel
=
createViewModel
(
componentDefinition
,
element
,
componentParams
),
childBindingContext
=
bindingContext
[
'
createChildContext
'
](
componentViewModel
);
currentViewModel
=
componentViewModel
;
ko
.
applyBindingsToDescendants
(
childBindingContext
,
element
);
});
},
null
,
{
disposeWhenNodeIsRemoved
:
element
});
return
{
'
controlsDescendantBindings
'
:
true
};
}
};
ko
.
virtualElements
.
allowedBindings
[
'
component
'
]
=
true
;
function
cloneTemplateIntoElement
(
componentName
,
componentDefinition
,
element
)
{
var
template
=
componentDefinition
[
'
template
'
];
if
(
!
template
)
{
throw
new
Error
(
'
Component
\'
'
+
componentName
+
'
\'
has no template
'
);
}
var
clonedNodesArray
=
ko
.
utils
.
cloneNodes
(
template
);
ko
.
virtualElements
.
setDomNodeChildren
(
element
,
clonedNodesArray
);
}
function
createViewModel
(
componentDefinition
,
element
,
componentParams
)
{
var
componentViewModelFactory
=
componentDefinition
[
'
createViewModel
'
];
return
componentViewModelFactory
?
componentViewModelFactory
.
call
(
componentDefinition
,
componentParams
,
{
element
:
element
})
:
componentParams
;
// Template-only component
}
})();
var
attrHtmlToJavascriptMap
=
{
'
class
'
:
'
className
'
,
'
for
'
:
'
htmlFor
'
};
var
attrHtmlToJavascriptMap
=
{
'
class
'
:
'
className
'
,
'
for
'
:
'
htmlFor
'
};
ko
.
bindingHandlers
[
'
attr
'
]
=
{
ko
.
bindingHandlers
[
'
attr
'
]
=
{
'
update
'
:
function
(
element
,
valueAccessor
,
allBindings
)
{
'
update
'
:
function
(
element
,
valueAccessor
,
allBindings
)
{
...
@@ -2739,7 +3372,7 @@ ko.bindingHandlers['checked'] = {
...
@@ -2739,7 +3372,7 @@ ko.bindingHandlers['checked'] = {
elemValue
=
useCheckedValue
?
checkedValue
()
:
isChecked
;
elemValue
=
useCheckedValue
?
checkedValue
()
:
isChecked
;
// When we're first setting up this computed, don't change any model state.
// When we're first setting up this computed, don't change any model state.
if
(
!
shouldSet
)
{
if
(
ko
.
computedContext
.
isInitial
()
)
{
return
;
return
;
}
}
...
@@ -2798,8 +3431,7 @@ ko.bindingHandlers['checked'] = {
...
@@ -2798,8 +3431,7 @@ ko.bindingHandlers['checked'] = {
var
isValueArray
=
isCheckbox
&&
(
ko
.
utils
.
unwrapObservable
(
valueAccessor
())
instanceof
Array
),
var
isValueArray
=
isCheckbox
&&
(
ko
.
utils
.
unwrapObservable
(
valueAccessor
())
instanceof
Array
),
oldElemValue
=
isValueArray
?
checkedValue
()
:
undefined
,
oldElemValue
=
isValueArray
?
checkedValue
()
:
undefined
,
useCheckedValue
=
isRadio
||
isValueArray
,
useCheckedValue
=
isRadio
||
isValueArray
;
shouldSet
=
false
;
// IE 6 won't allow radio buttons to be selected unless they have a name
// IE 6 won't allow radio buttons to be selected unless they have a name
if
(
isRadio
&&
!
element
.
name
)
if
(
isRadio
&&
!
element
.
name
)
...
@@ -2808,13 +3440,11 @@ ko.bindingHandlers['checked'] = {
...
@@ -2808,13 +3440,11 @@ ko.bindingHandlers['checked'] = {
// Set up two computeds to update the binding:
// Set up two computeds to update the binding:
// The first responds to changes in the checkedValue value and to element clicks
// The first responds to changes in the checkedValue value and to element clicks
ko
.
dependentObservable
(
updateModel
,
null
,
{
disposeWhenNodeIsRemoved
:
element
});
ko
.
computed
(
updateModel
,
null
,
{
disposeWhenNodeIsRemoved
:
element
});
ko
.
utils
.
registerEventHandler
(
element
,
"
click
"
,
updateModel
);
ko
.
utils
.
registerEventHandler
(
element
,
"
click
"
,
updateModel
);
// The second responds to changes in the model value (the one associated with the checked binding)
// The second responds to changes in the model value (the one associated with the checked binding)
ko
.
dependentObservable
(
updateView
,
null
,
{
disposeWhenNodeIsRemoved
:
element
});
ko
.
computed
(
updateView
,
null
,
{
disposeWhenNodeIsRemoved
:
element
});
shouldSet
=
true
;
}
}
};
};
ko
.
expressionRewriting
.
twoWayBindings
[
'
checked
'
]
=
true
;
ko
.
expressionRewriting
.
twoWayBindings
[
'
checked
'
]
=
true
;
...
@@ -3007,37 +3637,37 @@ ko.bindingHandlers['html'] = {
...
@@ -3007,37 +3637,37 @@ ko.bindingHandlers['html'] = {
ko
.
utils
.
setHtml
(
element
,
valueAccessor
());
ko
.
utils
.
setHtml
(
element
,
valueAccessor
());
}
}
};
};
var
withIfDomDataKey
=
ko
.
utils
.
domData
.
nextKey
();
// Makes a binding like with or if
// Makes a binding like with or if
function
makeWithIfBinding
(
bindingKey
,
isWith
,
isNot
,
makeContextCallback
)
{
function
makeWithIfBinding
(
bindingKey
,
isWith
,
isNot
,
makeContextCallback
)
{
ko
.
bindingHandlers
[
bindingKey
]
=
{
ko
.
bindingHandlers
[
bindingKey
]
=
{
'
init
'
:
function
(
element
)
{
'
init
'
:
function
(
element
,
valueAccessor
,
allBindings
,
viewModel
,
bindingContext
)
{
ko
.
utils
.
domData
.
set
(
element
,
withIfDomDataKey
,
{});
var
didDisplayOnLastUpdate
,
return
{
'
controlsDescendantBindings
'
:
true
};
savedNodes
;
},
ko
.
computed
(
function
()
{
'
update
'
:
function
(
element
,
valueAccessor
,
allBindings
,
viewModel
,
bindingContext
)
{
var
dataValue
=
ko
.
utils
.
unwrapObservable
(
valueAccessor
()),
var
withIfData
=
ko
.
utils
.
domData
.
get
(
element
,
withIfDomDataKey
),
shouldDisplay
=
!
isNot
!==
!
dataValue
,
// equivalent to isNot ? !dataValue : !!dataValue
dataValue
=
ko
.
utils
.
unwrapObservable
(
valueAccessor
()),
isFirstRender
=
!
savedNodes
,
shouldDisplay
=
!
isNot
!==
!
dataValue
,
// equivalent to isNot ? !dataValue : !!dataValue
needsRefresh
=
isFirstRender
||
isWith
||
(
shouldDisplay
!==
didDisplayOnLastUpdate
);
isFirstRender
=
!
withIfData
.
savedNodes
,
needsRefresh
=
isFirstRender
||
isWith
||
(
shouldDisplay
!==
withIfData
.
didDisplayOnLastUpdate
);
if
(
needsRefresh
)
{
// Save a copy of the inner nodes on the initial update, but only if we have dependencies.
if
(
needsRefresh
)
{
if
(
isFirstRender
&&
ko
.
computedContext
.
getDependenciesCount
())
{
if
(
isFirstRender
)
{
savedNodes
=
ko
.
utils
.
cloneNodes
(
ko
.
virtualElements
.
childNodes
(
element
),
true
/* shouldCleanNodes */
);
withIfData
.
savedNodes
=
ko
.
utils
.
cloneNodes
(
ko
.
virtualElements
.
childNodes
(
element
),
true
/* shouldCleanNodes */
);
}
}
if
(
shouldDisplay
)
{
if
(
shouldDisplay
)
{
if
(
!
isFirstRender
)
{
if
(
!
isFirstRender
)
{
ko
.
virtualElements
.
setDomNodeChildren
(
element
,
ko
.
utils
.
cloneNodes
(
withIfData
.
savedNodes
));
ko
.
virtualElements
.
setDomNodeChildren
(
element
,
ko
.
utils
.
cloneNodes
(
savedNodes
));
}
ko
.
applyBindingsToDescendants
(
makeContextCallback
?
makeContextCallback
(
bindingContext
,
dataValue
)
:
bindingContext
,
element
);
}
else
{
ko
.
virtualElements
.
emptyNode
(
element
);
}
}
ko
.
applyBindingsToDescendants
(
makeContextCallback
?
makeContextCallback
(
bindingContext
,
dataValue
)
:
bindingContext
,
element
);
}
else
{
ko
.
virtualElements
.
emptyNode
(
element
);
}
withIfData
.
didDisplayOnLastUpdate
=
shouldDisplay
;
didDisplayOnLastUpdate
=
shouldDisplay
;
}
}
},
null
,
{
disposeWhenNodeIsRemoved
:
element
});
return
{
'
controlsDescendantBindings
'
:
true
};
}
}
};
};
ko
.
expressionRewriting
.
bindingRewriteValidators
[
bindingKey
]
=
false
;
// Can't rewrite control flow bindings
ko
.
expressionRewriting
.
bindingRewriteValidators
[
bindingKey
]
=
false
;
// Can't rewrite control flow bindings
...
@@ -3052,6 +3682,7 @@ makeWithIfBinding('with', true /* isWith */, false /* isNot */,
...
@@ -3052,6 +3682,7 @@ makeWithIfBinding('with', true /* isWith */, false /* isNot */,
return
bindingContext
[
'
createChildContext
'
](
dataValue
);
return
bindingContext
[
'
createChildContext
'
](
dataValue
);
}
}
);
);
var
captionPlaceholder
=
{};
ko
.
bindingHandlers
[
'
options
'
]
=
{
ko
.
bindingHandlers
[
'
options
'
]
=
{
'
init
'
:
function
(
element
)
{
'
init
'
:
function
(
element
)
{
if
(
ko
.
utils
.
tagNameLower
(
element
)
!==
"
select
"
)
if
(
ko
.
utils
.
tagNameLower
(
element
)
!==
"
select
"
)
...
@@ -3072,12 +3703,13 @@ ko.bindingHandlers['options'] = {
...
@@ -3072,12 +3703,13 @@ ko.bindingHandlers['options'] = {
var
selectWasPreviouslyEmpty
=
element
.
length
==
0
;
var
selectWasPreviouslyEmpty
=
element
.
length
==
0
;
var
previousScrollTop
=
(
!
selectWasPreviouslyEmpty
&&
element
.
multiple
)
?
element
.
scrollTop
:
null
;
var
previousScrollTop
=
(
!
selectWasPreviouslyEmpty
&&
element
.
multiple
)
?
element
.
scrollTop
:
null
;
var
unwrappedArray
=
ko
.
utils
.
unwrapObservable
(
valueAccessor
());
var
unwrappedArray
=
ko
.
utils
.
unwrapObservable
(
valueAccessor
());
var
includeDestroyed
=
allBindings
.
get
(
'
optionsIncludeDestroyed
'
);
var
includeDestroyed
=
allBindings
.
get
(
'
optionsIncludeDestroyed
'
);
var
captionPlaceholder
=
{};
var
arrayToDomNodeChildrenOptions
=
{};
var
captionValue
;
var
captionValue
;
var
filteredArray
;
var
previousSelectedValues
;
var
previousSelectedValues
;
if
(
element
.
multiple
)
{
if
(
element
.
multiple
)
{
previousSelectedValues
=
ko
.
utils
.
arrayMap
(
selectedOptions
(),
ko
.
selectExtensions
.
readValue
);
previousSelectedValues
=
ko
.
utils
.
arrayMap
(
selectedOptions
(),
ko
.
selectExtensions
.
readValue
);
}
else
{
}
else
{
...
@@ -3089,7 +3721,7 @@ ko.bindingHandlers['options'] = {
...
@@ -3089,7 +3721,7 @@ ko.bindingHandlers['options'] = {
unwrappedArray
=
[
unwrappedArray
];
unwrappedArray
=
[
unwrappedArray
];
// Filter out any entries marked as destroyed
// Filter out any entries marked as destroyed
var
filteredArray
=
ko
.
utils
.
arrayFilter
(
unwrappedArray
,
function
(
item
)
{
filteredArray
=
ko
.
utils
.
arrayFilter
(
unwrappedArray
,
function
(
item
)
{
return
includeDestroyed
||
item
===
undefined
||
item
===
null
||
!
ko
.
utils
.
unwrapObservable
(
item
[
'
_destroy
'
]);
return
includeDestroyed
||
item
===
undefined
||
item
===
null
||
!
ko
.
utils
.
unwrapObservable
(
item
[
'
_destroy
'
]);
});
});
...
@@ -3103,7 +3735,6 @@ ko.bindingHandlers['options'] = {
...
@@ -3103,7 +3735,6 @@ ko.bindingHandlers['options'] = {
}
}
}
else
{
}
else
{
// If a falsy value is provided (e.g. null), we'll simply empty the select element
// If a falsy value is provided (e.g. null), we'll simply empty the select element
unwrappedArray
=
[];
}
}
function
applyToObject
(
object
,
predicate
,
defaultValue
)
{
function
applyToObject
(
object
,
predicate
,
defaultValue
)
{
...
@@ -3126,7 +3757,7 @@ ko.bindingHandlers['options'] = {
...
@@ -3126,7 +3757,7 @@ ko.bindingHandlers['options'] = {
previousSelectedValues
=
oldOptions
[
0
].
selected
?
[
ko
.
selectExtensions
.
readValue
(
oldOptions
[
0
])
]
:
[];
previousSelectedValues
=
oldOptions
[
0
].
selected
?
[
ko
.
selectExtensions
.
readValue
(
oldOptions
[
0
])
]
:
[];
itemUpdate
=
true
;
itemUpdate
=
true
;
}
}
var
option
=
d
ocument
.
createElement
(
"
option
"
);
var
option
=
element
.
ownerD
ocument
.
createElement
(
"
option
"
);
if
(
arrayEntry
===
captionPlaceholder
)
{
if
(
arrayEntry
===
captionPlaceholder
)
{
ko
.
utils
.
setTextContent
(
option
,
allBindings
.
get
(
'
optionsCaption
'
));
ko
.
utils
.
setTextContent
(
option
,
allBindings
.
get
(
'
optionsCaption
'
));
ko
.
selectExtensions
.
writeValue
(
option
,
undefined
);
ko
.
selectExtensions
.
writeValue
(
option
,
undefined
);
...
@@ -3142,6 +3773,13 @@ ko.bindingHandlers['options'] = {
...
@@ -3142,6 +3773,13 @@ ko.bindingHandlers['options'] = {
return
[
option
];
return
[
option
];
}
}
// By using a beforeRemove callback, we delay the removal until after new items are added. This fixes a selection
// problem in IE<=8 and Firefox. See https://github.com/knockout/knockout/issues/1208
arrayToDomNodeChildrenOptions
[
'
beforeRemove
'
]
=
function
(
option
)
{
element
.
removeChild
(
option
);
};
function
setSelectionCallback
(
arrayEntry
,
newOptions
)
{
function
setSelectionCallback
(
arrayEntry
,
newOptions
)
{
// IE6 doesn't like us to assign selection to OPTION nodes before they're added to the document.
// IE6 doesn't like us to assign selection to OPTION nodes before they're added to the document.
// That's why we first added them without selection. Now it's time to set the selection.
// That's why we first added them without selection. Now it's time to set the selection.
...
@@ -3163,27 +3801,35 @@ ko.bindingHandlers['options'] = {
...
@@ -3163,27 +3801,35 @@ ko.bindingHandlers['options'] = {
}
}
}
}
ko
.
utils
.
setDomNodeChildrenFromArrayMapping
(
element
,
filteredArray
,
optionForArrayItem
,
null
,
callback
);
ko
.
utils
.
setDomNodeChildrenFromArrayMapping
(
element
,
filteredArray
,
optionForArrayItem
,
arrayToDomNodeChildrenOptions
,
callback
);
// Determine if the selection has changed as a result of updating the options list
ko
.
dependencyDetection
.
ignore
(
function
()
{
var
selectionChanged
;
if
(
allBindings
.
get
(
'
valueAllowUnset
'
)
&&
allBindings
[
'
has
'
](
'
value
'
))
{
if
(
element
.
multiple
)
{
// The model value is authoritative, so make sure its value is the one selected
// For a multiple-select box, compare the new selection count to the previous one
ko
.
selectExtensions
.
writeValue
(
element
,
ko
.
utils
.
unwrapObservable
(
allBindings
.
get
(
'
value
'
)),
true
/* allowUnset */
);
// But if nothing was selected before, the selection can't have changed
}
else
{
selectionChanged
=
previousSelectedValues
.
length
&&
selectedOptions
().
length
<
previousSelectedValues
.
length
;
// Determine if the selection has changed as a result of updating the options list
}
else
{
var
selectionChanged
;
// For a single-select box, compare the current value to the previous value
if
(
element
.
multiple
)
{
// But if nothing was selected before or nothing is selected now, just look for a change in selection
// For a multiple-select box, compare the new selection count to the previous one
selectionChanged
=
(
previousSelectedValues
.
length
&&
element
.
selectedIndex
>=
0
)
// But if nothing was selected before, the selection can't have changed
?
(
ko
.
selectExtensions
.
readValue
(
element
.
options
[
element
.
selectedIndex
])
!==
previousSelectedValues
[
0
])
selectionChanged
=
previousSelectedValues
.
length
&&
selectedOptions
().
length
<
previousSelectedValues
.
length
;
:
(
previousSelectedValues
.
length
||
element
.
selectedIndex
>=
0
);
}
else
{
}
// For a single-select box, compare the current value to the previous value
// But if nothing was selected before or nothing is selected now, just look for a change in selection
selectionChanged
=
(
previousSelectedValues
.
length
&&
element
.
selectedIndex
>=
0
)
?
(
ko
.
selectExtensions
.
readValue
(
element
.
options
[
element
.
selectedIndex
])
!==
previousSelectedValues
[
0
])
:
(
previousSelectedValues
.
length
||
element
.
selectedIndex
>=
0
);
}
// Ensure consistency between model value and selected option.
// Ensure consistency between model value and selected option.
// If the dropdown was changed so that selection is no longer the same,
// If the dropdown was changed so that selection is no longer the same,
// notify the value or selectedOptions binding.
// notify the value or selectedOptions binding.
if
(
selectionChanged
)
if
(
selectionChanged
)
{
ko
.
dependencyDetection
.
ignore
(
ko
.
utils
.
triggerEvent
,
null
,
[
element
,
"
change
"
]);
ko
.
utils
.
triggerEvent
(
element
,
"
change
"
);
}
}
});
// Workaround for IE bug
// Workaround for IE bug
ko
.
utils
.
ensureSelectElementIsRenderedCorrectly
(
element
);
ko
.
utils
.
ensureSelectElementIsRenderedCorrectly
(
element
);
...
@@ -3294,6 +3940,7 @@ ko.bindingHandlers['value'] = {
...
@@ -3294,6 +3940,7 @@ ko.bindingHandlers['value'] = {
&&
element
.
autocomplete
!=
"
off
"
&&
(
!
element
.
form
||
element
.
form
.
autocomplete
!=
"
off
"
);
&&
element
.
autocomplete
!=
"
off
"
&&
(
!
element
.
form
||
element
.
form
.
autocomplete
!=
"
off
"
);
if
(
ieAutoCompleteHackNeeded
&&
ko
.
utils
.
arrayIndexOf
(
eventsToCatch
,
"
propertychange
"
)
==
-
1
)
{
if
(
ieAutoCompleteHackNeeded
&&
ko
.
utils
.
arrayIndexOf
(
eventsToCatch
,
"
propertychange
"
)
==
-
1
)
{
ko
.
utils
.
registerEventHandler
(
element
,
"
propertychange
"
,
function
()
{
propertyChangedFired
=
true
});
ko
.
utils
.
registerEventHandler
(
element
,
"
propertychange
"
,
function
()
{
propertyChangedFired
=
true
});
ko
.
utils
.
registerEventHandler
(
element
,
"
focus
"
,
function
()
{
propertyChangedFired
=
false
});
ko
.
utils
.
registerEventHandler
(
element
,
"
blur
"
,
function
()
{
ko
.
utils
.
registerEventHandler
(
element
,
"
blur
"
,
function
()
{
if
(
propertyChangedFired
)
{
if
(
propertyChangedFired
)
{
valueUpdateHandler
();
valueUpdateHandler
();
...
@@ -3313,18 +3960,20 @@ ko.bindingHandlers['value'] = {
...
@@ -3313,18 +3960,20 @@ ko.bindingHandlers['value'] = {
ko
.
utils
.
registerEventHandler
(
element
,
eventName
,
handler
);
ko
.
utils
.
registerEventHandler
(
element
,
eventName
,
handler
);
});
});
},
},
'
update
'
:
function
(
element
,
valueAccessor
)
{
'
update
'
:
function
(
element
,
valueAccessor
,
allBindings
)
{
var
valueIsSelectOption
=
ko
.
utils
.
tagNameLower
(
element
)
===
"
select
"
;
var
newValue
=
ko
.
utils
.
unwrapObservable
(
valueAccessor
());
var
newValue
=
ko
.
utils
.
unwrapObservable
(
valueAccessor
());
var
elementValue
=
ko
.
selectExtensions
.
readValue
(
element
);
var
elementValue
=
ko
.
selectExtensions
.
readValue
(
element
);
var
valueHasChanged
=
(
newValue
!==
elementValue
);
var
valueHasChanged
=
(
newValue
!==
elementValue
);
if
(
valueHasChanged
)
{
if
(
valueHasChanged
)
{
var
applyValueAction
=
function
()
{
ko
.
selectExtensions
.
writeValue
(
element
,
newValue
);
};
if
(
ko
.
utils
.
tagNameLower
(
element
)
===
"
select
"
)
{
applyValueAction
();
var
allowUnset
=
allBindings
.
get
(
'
valueAllowUnset
'
);
var
applyValueAction
=
function
()
{
ko
.
selectExtensions
.
writeValue
(
element
,
newValue
,
allowUnset
);
};
applyValueAction
();
if
(
valueIsSelectOption
)
{
if
(
!
allowUnset
&&
newValue
!==
ko
.
selectExtensions
.
readValue
(
element
))
{
if
(
newValue
!==
ko
.
selectExtensions
.
readValue
(
element
))
{
// If you try to set a model value that can't be represented in an already-populated dropdown, reject that change,
// If you try to set a model value that can't be represented in an already-populated dropdown, reject that change,
// because you're not allowed to have a model value that disagrees with a visible UI selection.
// because you're not allowed to have a model value that disagrees with a visible UI selection.
ko
.
dependencyDetection
.
ignore
(
ko
.
utils
.
triggerEvent
,
null
,
[
element
,
"
change
"
]);
ko
.
dependencyDetection
.
ignore
(
ko
.
utils
.
triggerEvent
,
null
,
[
element
,
"
change
"
]);
...
@@ -3334,6 +3983,8 @@ ko.bindingHandlers['value'] = {
...
@@ -3334,6 +3983,8 @@ ko.bindingHandlers['value'] = {
// to apply the value as well.
// to apply the value as well.
setTimeout
(
applyValueAction
,
0
);
setTimeout
(
applyValueAction
,
0
);
}
}
}
else
{
ko
.
selectExtensions
.
writeValue
(
element
,
newValue
);
}
}
}
}
}
}
...
@@ -3718,7 +4369,8 @@ ko.exportSymbol('__tr_ambtns', ko.templateRewriting.applyMemoizedBindingsToNextS
...
@@ -3718,7 +4369,8 @@ ko.exportSymbol('__tr_ambtns', ko.templateRewriting.applyMemoizedBindingsToNextS
:
new
ko
.
bindingContext
(
ko
.
utils
.
unwrapObservable
(
dataOrBindingContext
));
:
new
ko
.
bindingContext
(
ko
.
utils
.
unwrapObservable
(
dataOrBindingContext
));
// Support selecting template as a function of the data being rendered
// Support selecting template as a function of the data being rendered
var
templateName
=
typeof
(
template
)
==
'
function
'
?
template
(
bindingContext
[
'
$data
'
],
bindingContext
)
:
template
;
var
templateName
=
ko
.
isObservable
(
template
)
?
template
()
:
typeof
(
template
)
==
'
function
'
?
template
(
bindingContext
[
'
$data
'
],
bindingContext
)
:
template
;
var
renderedNodesArray
=
executeTemplate
(
targetNodeOrNodeArray
,
renderMode
,
templateName
,
bindingContext
,
options
);
var
renderedNodesArray
=
executeTemplate
(
targetNodeOrNodeArray
,
renderMode
,
templateName
,
bindingContext
,
options
);
if
(
renderMode
==
"
replaceNode
"
)
{
if
(
renderMode
==
"
replaceNode
"
)
{
...
@@ -3800,15 +4452,18 @@ ko.exportSymbol('__tr_ambtns', ko.templateRewriting.applyMemoizedBindingsToNextS
...
@@ -3800,15 +4452,18 @@ ko.exportSymbol('__tr_ambtns', ko.templateRewriting.applyMemoizedBindingsToNextS
return
{
'
controlsDescendantBindings
'
:
true
};
return
{
'
controlsDescendantBindings
'
:
true
};
},
},
'
update
'
:
function
(
element
,
valueAccessor
,
allBindings
,
viewModel
,
bindingContext
)
{
'
update
'
:
function
(
element
,
valueAccessor
,
allBindings
,
viewModel
,
bindingContext
)
{
var
templateName
=
ko
.
utils
.
unwrapObservable
(
valueAccessor
()),
var
value
=
valueAccessor
(),
options
=
{},
shouldDisplay
=
true
,
dataValue
,
dataValue
,
templateComputed
=
null
;
options
=
ko
.
utils
.
unwrapObservable
(
value
),
shouldDisplay
=
true
,
templateComputed
=
null
,
templateName
;
if
(
typeof
templateName
!=
"
string
"
)
{
if
(
typeof
options
==
"
string
"
)
{
options
=
templateName
;
templateName
=
value
;
templateName
=
ko
.
utils
.
unwrapObservable
(
options
[
'
name
'
]);
options
=
{};
}
else
{
templateName
=
options
[
'
name
'
];
// Support "if"/"ifnot" conditions
// Support "if"/"ifnot" conditions
if
(
'
if
'
in
options
)
if
(
'
if
'
in
options
)
...
@@ -3855,6 +4510,24 @@ ko.exportSymbol('__tr_ambtns', ko.templateRewriting.applyMemoizedBindingsToNextS
...
@@ -3855,6 +4510,24 @@ ko.exportSymbol('__tr_ambtns', ko.templateRewriting.applyMemoizedBindingsToNextS
ko
.
exportSymbol
(
'
setTemplateEngine
'
,
ko
.
setTemplateEngine
);
ko
.
exportSymbol
(
'
setTemplateEngine
'
,
ko
.
setTemplateEngine
);
ko
.
exportSymbol
(
'
renderTemplate
'
,
ko
.
renderTemplate
);
ko
.
exportSymbol
(
'
renderTemplate
'
,
ko
.
renderTemplate
);
// Go through the items that have been added and deleted and try to find matches between them.
ko
.
utils
.
findMovesInArrayComparison
=
function
(
left
,
right
,
limitFailedCompares
)
{
if
(
left
.
length
&&
right
.
length
)
{
var
failedCompares
,
l
,
r
,
leftItem
,
rightItem
;
for
(
failedCompares
=
l
=
0
;
(
!
limitFailedCompares
||
failedCompares
<
limitFailedCompares
)
&&
(
leftItem
=
left
[
l
]);
++
l
)
{
for
(
r
=
0
;
rightItem
=
right
[
r
];
++
r
)
{
if
(
leftItem
[
'
value
'
]
===
rightItem
[
'
value
'
])
{
leftItem
[
'
moved
'
]
=
rightItem
[
'
index
'
];
rightItem
[
'
moved
'
]
=
leftItem
[
'
index
'
];
right
.
splice
(
r
,
1
);
// This item is marked as moved; so remove it from right list
failedCompares
=
r
=
0
;
// Reset failed compares count because we're checking for consecutive failures
break
;
}
}
failedCompares
+=
r
;
}
}
};
ko
.
utils
.
compareArrays
=
(
function
()
{
ko
.
utils
.
compareArrays
=
(
function
()
{
var
statusNotInOld
=
'
added
'
,
statusNotInNew
=
'
deleted
'
;
var
statusNotInOld
=
'
added
'
,
statusNotInNew
=
'
deleted
'
;
...
@@ -3928,25 +4601,10 @@ ko.utils.compareArrays = (function () {
...
@@ -3928,25 +4601,10 @@ ko.utils.compareArrays = (function () {
}
}
}
}
if
(
notInSml
.
length
&&
notInBig
.
length
)
{
// Set a limit on the number of consecutive non-matching comparisons; having it a multiple of
// Set a limit on the number of consecutive non-matching comparisons; having it a multiple of
// smlIndexMax keeps the time complexity of this algorithm linear.
// smlIndexMax keeps the time complexity of this algorithm linear.
ko
.
utils
.
findMovesInArrayComparison
(
notInSml
,
notInBig
,
smlIndexMax
*
10
);
var
limitFailedCompares
=
smlIndexMax
*
10
,
failedCompares
,
a
,
d
,
notInSmlItem
,
notInBigItem
;
// Go through the items that have been added and deleted and try to find matches between them.
for
(
failedCompares
=
a
=
0
;
(
options
[
'
dontLimitMoves
'
]
||
failedCompares
<
limitFailedCompares
)
&&
(
notInSmlItem
=
notInSml
[
a
]);
a
++
)
{
for
(
d
=
0
;
notInBigItem
=
notInBig
[
d
];
d
++
)
{
if
(
notInSmlItem
[
'
value
'
]
===
notInBigItem
[
'
value
'
])
{
notInSmlItem
[
'
moved
'
]
=
notInBigItem
[
'
index
'
];
notInBigItem
[
'
moved
'
]
=
notInSmlItem
[
'
index
'
];
notInBig
.
splice
(
d
,
1
);
// This item is marked as moved; so remove it from notInBig list
failedCompares
=
d
=
0
;
// Reset failed compares count because we're checking for consecutive failures
break
;
}
}
failedCompares
+=
d
;
}
}
return
editScript
.
reverse
();
return
editScript
.
reverse
();
}
}
...
@@ -3954,7 +4612,6 @@ ko.utils.compareArrays = (function () {
...
@@ -3954,7 +4612,6 @@ ko.utils.compareArrays = (function () {
})();
})();
ko
.
exportSymbol
(
'
utils.compareArrays
'
,
ko
.
utils
.
compareArrays
);
ko
.
exportSymbol
(
'
utils.compareArrays
'
,
ko
.
utils
.
compareArrays
);
(
function
()
{
(
function
()
{
// Objective:
// Objective:
// * Given an input array, a container DOM node, and a function from array elements to arrays of DOM nodes,
// * Given an input array, a container DOM node, and a function from array elements to arrays of DOM nodes,
...
@@ -3981,7 +4638,7 @@ ko.exportSymbol('utils.compareArrays', ko.utils.compareArrays);
...
@@ -3981,7 +4638,7 @@ ko.exportSymbol('utils.compareArrays', ko.utils.compareArrays);
// Replace the contents of the mappedNodes array, thereby updating the record
// Replace the contents of the mappedNodes array, thereby updating the record
// of which nodes would be deleted if valueToMap was itself later removed
// of which nodes would be deleted if valueToMap was itself later removed
mappedNodes
.
splice
(
0
,
mappedNodes
.
length
)
;
mappedNodes
.
length
=
0
;
ko
.
utils
.
arrayPushAll
(
mappedNodes
,
newMappedNodes
);
ko
.
utils
.
arrayPushAll
(
mappedNodes
,
newMappedNodes
);
},
null
,
{
disposeWhenNodeIsRemoved
:
containerNode
,
disposeWhen
:
function
()
{
return
!
ko
.
utils
.
anyDomNodeIsAttachedToDocument
(
mappedNodes
);
}
});
},
null
,
{
disposeWhenNodeIsRemoved
:
containerNode
,
disposeWhen
:
function
()
{
return
!
ko
.
utils
.
anyDomNodeIsAttachedToDocument
(
mappedNodes
);
}
});
return
{
mappedNodes
:
mappedNodes
,
dependentObservable
:
(
dependentObservable
.
isActive
()
?
dependentObservable
:
undefined
)
};
return
{
mappedNodes
:
mappedNodes
,
dependentObservable
:
(
dependentObservable
.
isActive
()
?
dependentObservable
:
undefined
)
};
...
@@ -4144,7 +4801,7 @@ ko.exportSymbol('nativeTemplateEngine', ko.nativeTemplateEngine);
...
@@ -4144,7 +4801,7 @@ ko.exportSymbol('nativeTemplateEngine', ko.nativeTemplateEngine);
// Note that as of Knockout 1.3, we only support jQuery.tmpl 1.0.0pre and later,
// Note that as of Knockout 1.3, we only support jQuery.tmpl 1.0.0pre and later,
// which KO internally refers to as version "2", so older versions are no longer detected.
// which KO internally refers to as version "2", so older versions are no longer detected.
var
jQueryTmplVersion
=
this
.
jQueryTmplVersion
=
(
function
()
{
var
jQueryTmplVersion
=
this
.
jQueryTmplVersion
=
(
function
()
{
if
(
(
typeof
(
jQuery
)
==
"
undefined
"
)
||
!
(
jQuery
[
'
tmpl
'
]))
if
(
!
jQuery
||
!
(
jQuery
[
'
tmpl
'
]))
return
0
;
return
0
;
// Since it exposes no official version number, we use our own numbering system. To be updated as jquery-tmpl evolves.
// Since it exposes no official version number, we use our own numbering system. To be updated as jquery-tmpl evolves.
try
{
try
{
...
...
architecture-examples/knockoutjs/bower_components/director/build/director.js
View file @
c5e53bc8
//
//
// Generated on
Sun Dec 16 2012 22:47:05
GMT-0500 (EST) by Nodejitsu, Inc (Using Codesurgeon).
// Generated on
Fri Dec 27 2013 12:02:11
GMT-0500 (EST) by Nodejitsu, Inc (Using Codesurgeon).
// Version 1.
1.9
// Version 1.
2.2
//
//
(
function
(
exports
)
{
(
function
(
exports
)
{
/*
/*
* browser.js: Browser specific functionality for director.
* browser.js: Browser specific functionality for director.
*
*
...
@@ -201,7 +200,7 @@ Router.prototype.init = function (r) {
...
@@ -201,7 +200,7 @@ Router.prototype.init = function (r) {
this
.
handler
=
function
(
onChangeEvent
)
{
this
.
handler
=
function
(
onChangeEvent
)
{
var
newURL
=
onChangeEvent
&&
onChangeEvent
.
newURL
||
window
.
location
.
hash
;
var
newURL
=
onChangeEvent
&&
onChangeEvent
.
newURL
||
window
.
location
.
hash
;
var
url
=
self
.
history
===
true
?
self
.
getPath
()
:
newURL
.
replace
(
/.*#/
,
''
);
var
url
=
self
.
history
===
true
?
self
.
getPath
()
:
newURL
.
replace
(
/.*#/
,
''
);
self
.
dispatch
(
'
on
'
,
url
);
self
.
dispatch
(
'
on
'
,
url
.
charAt
(
0
)
===
'
/
'
?
url
:
'
/
'
+
url
);
};
};
listener
.
init
(
this
.
handler
,
this
.
history
);
listener
.
init
(
this
.
handler
,
this
.
history
);
...
@@ -210,7 +209,7 @@ Router.prototype.init = function (r) {
...
@@ -210,7 +209,7 @@ Router.prototype.init = function (r) {
if
(
dlocHashEmpty
()
&&
r
)
{
if
(
dlocHashEmpty
()
&&
r
)
{
dloc
.
hash
=
r
;
dloc
.
hash
=
r
;
}
else
if
(
!
dlocHashEmpty
())
{
}
else
if
(
!
dlocHashEmpty
())
{
self
.
dispatch
(
'
on
'
,
dloc
.
hash
.
replace
(
/^#
/
,
''
));
self
.
dispatch
(
'
on
'
,
'
/
'
+
dloc
.
hash
.
replace
(
/^
(
#
\/
|#|
\/)
/
,
''
));
}
}
}
}
else
{
else
{
...
@@ -363,11 +362,16 @@ function regifyString(str, params) {
...
@@ -363,11 +362,16 @@ function regifyString(str, params) {
out
+=
str
.
substr
(
0
,
matches
.
index
)
+
matches
[
0
];
out
+=
str
.
substr
(
0
,
matches
.
index
)
+
matches
[
0
];
}
}
str
=
out
+=
str
.
substr
(
last
);
str
=
out
+=
str
.
substr
(
last
);
var
captures
=
str
.
match
(
/:
([^\/]
+
)
/ig
),
length
;
var
captures
=
str
.
match
(
/:
([^\/]
+
)
/ig
),
capture
,
length
;
if
(
captures
)
{
if
(
captures
)
{
length
=
captures
.
length
;
length
=
captures
.
length
;
for
(
var
i
=
0
;
i
<
length
;
i
++
)
{
for
(
var
i
=
0
;
i
<
length
;
i
++
)
{
str
=
str
.
replace
(
captures
[
i
],
paramifyString
(
captures
[
i
],
params
));
capture
=
captures
[
i
];
if
(
capture
.
slice
(
0
,
2
)
===
"
::
"
)
{
str
=
capture
.
slice
(
1
);
}
else
{
str
=
str
.
replace
(
capture
,
paramifyString
(
capture
,
params
));
}
}
}
}
}
return
str
;
return
str
;
...
@@ -485,20 +489,22 @@ Router.prototype.dispatch = function(method, path, callback) {
...
@@ -485,20 +489,22 @@ Router.prototype.dispatch = function(method, path, callback) {
Router
.
prototype
.
invoke
=
function
(
fns
,
thisArg
,
callback
)
{
Router
.
prototype
.
invoke
=
function
(
fns
,
thisArg
,
callback
)
{
var
self
=
this
;
var
self
=
this
;
var
apply
;
if
(
this
.
async
)
{
if
(
this
.
async
)
{
_asyncEverySeries
(
fns
,
function
apply
(
fn
,
next
)
{
apply
=
function
(
fn
,
next
)
{
if
(
Array
.
isArray
(
fn
))
{
if
(
Array
.
isArray
(
fn
))
{
return
_asyncEverySeries
(
fn
,
apply
,
next
);
return
_asyncEverySeries
(
fn
,
apply
,
next
);
}
else
if
(
typeof
fn
==
"
function
"
)
{
}
else
if
(
typeof
fn
==
"
function
"
)
{
fn
.
apply
(
thisArg
,
fns
.
captures
.
concat
(
next
));
fn
.
apply
(
thisArg
,
fns
.
captures
.
concat
(
next
));
}
}
},
function
()
{
};
_asyncEverySeries
(
fns
,
apply
,
function
()
{
if
(
callback
)
{
if
(
callback
)
{
callback
.
apply
(
thisArg
,
arguments
);
callback
.
apply
(
thisArg
,
arguments
);
}
}
});
});
}
else
{
}
else
{
_every
(
fns
,
function
apply
(
fn
)
{
apply
=
function
(
fn
)
{
if
(
Array
.
isArray
(
fn
))
{
if
(
Array
.
isArray
(
fn
))
{
return
_every
(
fn
,
apply
);
return
_every
(
fn
,
apply
);
}
else
if
(
typeof
fn
===
"
function
"
)
{
}
else
if
(
typeof
fn
===
"
function
"
)
{
...
@@ -506,7 +512,8 @@ Router.prototype.invoke = function(fns, thisArg, callback) {
...
@@ -506,7 +512,8 @@ Router.prototype.invoke = function(fns, thisArg, callback) {
}
else
if
(
typeof
fn
===
"
string
"
&&
self
.
resource
)
{
}
else
if
(
typeof
fn
===
"
string
"
&&
self
.
resource
)
{
self
.
resource
[
fn
].
apply
(
thisArg
,
fns
.
captures
||
[]);
self
.
resource
[
fn
].
apply
(
thisArg
,
fns
.
captures
||
[]);
}
}
});
};
_every
(
fns
,
apply
);
}
}
};
};
...
@@ -686,7 +693,7 @@ Router.prototype.mount = function(routes, path) {
...
@@ -686,7 +693,7 @@ Router.prototype.mount = function(routes, path) {
function
insertOrMount
(
route
,
local
)
{
function
insertOrMount
(
route
,
local
)
{
var
rename
=
route
,
parts
=
route
.
split
(
self
.
delimiter
),
routeType
=
typeof
routes
[
route
],
isRoute
=
parts
[
0
]
===
""
||
!
self
.
_methods
[
parts
[
0
]],
event
=
isRoute
?
"
on
"
:
rename
;
var
rename
=
route
,
parts
=
route
.
split
(
self
.
delimiter
),
routeType
=
typeof
routes
[
route
],
isRoute
=
parts
[
0
]
===
""
||
!
self
.
_methods
[
parts
[
0
]],
event
=
isRoute
?
"
on
"
:
rename
;
if
(
isRoute
)
{
if
(
isRoute
)
{
rename
=
rename
.
slice
((
rename
.
match
(
new
RegExp
(
self
.
delimiter
))
||
[
""
])[
0
].
length
);
rename
=
rename
.
slice
((
rename
.
match
(
new
RegExp
(
"
^
"
+
self
.
delimiter
))
||
[
""
])[
0
].
length
);
parts
.
shift
();
parts
.
shift
();
}
}
if
(
isRoute
&&
routeType
===
"
object
"
&&
!
Array
.
isArray
(
routes
[
route
]))
{
if
(
isRoute
&&
routeType
===
"
object
"
&&
!
Array
.
isArray
(
routes
[
route
]))
{
...
...
architecture-examples/knockoutjs/index.html
View file @
c5e53bc8
...
@@ -52,7 +52,7 @@
...
@@ -52,7 +52,7 @@
<p>
Part of
<a
href=
"http://todomvc.com"
>
TodoMVC
</a></p>
<p>
Part of
<a
href=
"http://todomvc.com"
>
TodoMVC
</a></p>
</footer>
</footer>
<script
src=
"bower_components/todomvc-common/base.js"
></script>
<script
src=
"bower_components/todomvc-common/base.js"
></script>
<script
src=
"bower_components/
knockout.js/knockout.debug
.js"
></script>
<script
src=
"bower_components/
component-knockout-passy/knockout
.js"
></script>
<script
src=
"bower_components/director/build/director.js"
></script>
<script
src=
"bower_components/director/build/director.js"
></script>
<script
src=
"js/app.js"
></script>
<script
src=
"js/app.js"
></script>
</body>
</body>
...
...
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