Skip to content

Commit

Permalink
Adding bower and grunt tasks to build source files
Browse files Browse the repository at this point in the history
  • Loading branch information
jakiestfu committed May 29, 2015
1 parent 9bfe643 commit e1ae3df
Show file tree
Hide file tree
Showing 11 changed files with 659 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.DS_Store
node_modules
bower_components
Empty file added .jshintrc
Empty file.
63 changes: 63 additions & 0 deletions Gruntfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
var fs = require('fs');

module.exports = function(grunt) {

var pkg = grunt.file.readJSON('package.json');

// Will add once beta is released
var banner = [
"<%= pkg.name %> v<%= pkg.version %>",
"The MIT License (MIT)",
"Copyright (c) 2014 <%= pkg.author %>"
].join("\n * ").trim();

grunt.initConfig({

pkg: pkg,

concat: {
options: {
//banner: "/*! " + banner + " */\n\n"
},
copy: {
files: {
'dist/ko-reactor.js': ["src/knockout.reactor.js"],
'dist/ko-reactor-beta.js': ["src/knockout.reactor-beta.js"]
}
}
},

jshint: {
all: ['src/**/*.js']
},

uglify: {
options: {
//banner: "/*! " + banner + " */\n",
footer: "window.foo = \"<%= pkg.version %>\";",
preserveComments: 'some'
},
main: {
files: {
'dist/ko-reactor.min.js': ['dist/ko-reactor.js'],
'dist/ko-reactor-beta.min.js': ["dist/ko-reactor-beta.js"]
}
}
},

watch: {
scripts: {
files: 'src/*.js',
tasks: ['jshint', 'uglify']
}
}
});

grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');

grunt.registerTask('default', ['concat', 'uglify']);
grunt.registerTask('develop', ['concat', 'uglify', 'watch']);
};
29 changes: 29 additions & 0 deletions bower.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "ko-reactor",
"homepage": "https://github.com/ZiadJ/knockoutjs-reactor.git",
"authors": [
"Ziad Jeeroburkhan",
"Jacob Kelley <[email protected]>"
],
"description": "Deeply watches observable changes",
"main": [
"dist/ko-reactor.min.js"
],
"keywords": [
"knockout",
"reactive",
"reactor",
"watch"
],
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"dependencies": {
"knockout": "~3.3.0"
}
}
File renamed without changes.
1 change: 1 addition & 0 deletions dist/ko-reactor-beta.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

247 changes: 247 additions & 0 deletions dist/ko-reactor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
// Deep observer plugin for Knockout http://knockoutjs.com/
// (c) Ziad Jeeroburkhan
// License: MIT (http://www.opensource.org/licenses/mit-license.php)
// Version 1.3.0

ko.subscribable.fn['watch'] = function (targetOrEvaluatorCallback, options, evaluatorCallback, context) {
/// <summary>
/// Track and manage changes within the chained observable down to any given level.
/// </summary>
/// <param name="options" type="object">
/// false -> Disables tracking on the chained observable.
/// { depth: 2 } -> Track all nested subscribables down to the 2nd level(default is 1).<br/>
/// { depth: -1 } -> Track all nested subscribables.<br/>
/// { hide: [...] } -> Property or array of properties to be ignored.<br/>
/// { hideArrays: true } -> Ignore all nested arrays.<br/>
/// { hideWrappedValues: true } -> Ignore observables wrapped under yet another parent observable.<br/>
/// { mutable: true } -> Dynamically adapt to changes made to the target structure through its own subscribables.<br/>
/// { watchedOnly: true } -> Watch only subscribables tagged with .watch().<br/>
/// { beforeWatch: function(parents, child) {...} } -> Function called prior to creating a subscription. Returning false aborts the operation and ignores its children.<br/>
/// { wrap: true } -> Wrap all fields into observables. This happens on the fly for new array items(or child objects when mutable is set to true).<br/>
/// { beforeWrap: function(parents, field, value) {...} } -> Function called prior to wrapping a value into an observable. Returning false leaves it as it is.<br/>
/// { tagParentsWithName: true } -> Add the property '_fieldName' under each parent for easy identification.<br/>
/// { keepOldValues: 3 } -> Keep the last three values for each subscribable under the property 'oldValues'.<br/>
/// { seal: true } -> Prevent any subsequent watcher from watching down the target tree.<br/>
/// </param>
/// <param name="evaluatorCallback" type="function">
/// The callback function called during changes. Any return value is assigned to the chained observable.
/// </param>

var targetType = typeof targetOrEvaluatorCallback;

if (targetType === 'boolean' || targetType === 'undefined') {
// Turn on or off the watcher for the specified target along with any of its children.
ko.watch(this, { enabled: targetOrEvaluatorCallback !== false });
} else if (targetType === 'function' && !ko.isSubscribable(targetOrEvaluatorCallback)) {
// Target the chained subscribable itself if no target subscribable or object was passed.
ko.watch(this, options || {}, targetOrEvaluatorCallback, context || this);
} else {
ko.watch(targetOrEvaluatorCallback, options, evaluatorCallback, context || this);
}

return this;
};

ko['watch'] = function (target, options, evaluatorCallback, context) {
/// <summary>
/// Track and manage changes within a specific target object down to any given level.
/// </summary>
/// <param name="target">
/// An object or function containing targeted subscribables or the subscribable itself.
/// </param>
/// <param name="options" type="object">
/// { depth: 2 } -> Track all nested subscribables down to the 2nd level(default is 1).<br/>
/// { depth: -1 } -> Track all nested subscribables.<br/>
/// { hide: [...] } -> Property or array of properties to be ignored.<br/>
/// { hideArrays: true } -> Ignore all nested arrays.<br/>
/// { hideWrappedValues: true } -> Ignore observables wrapped under yet another parent observable.<br/>
/// { mutable: true } -> Dynamically adapt to changes made to the target structure through its own subscribables.<br/>
/// { watchedOnly: true } -> Watch only subscribables tagged with .watch().<br/>
/// { beforeWatch: function(parents, child) {...} } -> Function called prior to creating a subscription. Returning false aborts the operation and ignores its children.<br/>
/// { wrap: true } -> Wrap all fields into observables. This happens on the fly for new array items(or child objects when mutable is set to true).<br/>
/// { beforeWrap: function(parents, field, value) {...} } -> Function called prior to wrapping a value into an observable. Returning false leaves it as it is.<br/>
/// { tagParentsWithName: true } -> Add the property '_fieldName' under each parent for easy identification.<br/>
/// { keepOldValues: 3 } -> Keep the last three values for each subscribable under the property 'oldValues'.<br/>
/// { seal: true } -> Prevent any subsequent watcher from watching down the target tree.<br/>
/// </param>
/// <param name="evaluatorCallback" type="function">
/// The callback function called during changes.
/// </param>

if (typeof options === 'function') {
context = context || evaluatorCallback;
evaluatorCallback = options;
options = {};
}

context = context || this;

function watchChildren(child, parent, grandParents, unwatch, keepOffParentList) {

if (child && options.depth !== 0 && (options.depth === -1 || grandParents.length < (options.depth || 1))) {

// Proceed on watched children only when in watched-only mode.
if (options.watchedOnly && !child.watchable && child != target)
return;

// Setting the target as false prevents it from being watched later on.
if (options.enabled === false || options.enabled === true)
child.watchable = options.enabled;

// Ignore watch-disabled objects.
if (child.watchable === false)
return;

// Prevent subsequent watchers from watching the target when sealed.
if (options.seal === true)
child.watchable = false;

// Bypass circular references.
if (child === parent || ko.utils.arrayIndexOf(grandParents, child) > -1)
return;

// Merge parents. Using a fresh array so it is not referenced in the next recursion if any.
var parents = [].concat(grandParents, parent && parent !== target ? parent : []);

// Ignore hidden objects. Also applies to any of their children.
if (options.hide)
if (typeof options.hide === 'object'
? options.hide === child
: ko.utils.arrayIndexOf(options.hide, child) > -1)
return;

if (ko.isSubscribable(child)) {
if (evaluatorCallback) {
if (unwatch === true || !options.beforeWatch || options.beforeWatch.call(context, parents, child) !== false) {
if (typeof child.pop === 'function') {
// Child is an observable array. Watch all item changes within it.
child.subscribe(function (changes) {
ko.utils.arrayForEach(changes, function (item) {

var returnValue = evaluatorCallback.call(context, parents, child, item);
if (returnValue !== undefined)
context(returnValue);

if (!item.moved) {
// Deleted or brand new item. Unwatch or watch it.
setTimeout(function () {
watchChildren(item.value, (keepOffParentList ? null : child), parents, item.status === 'deleted');
}, 0);
}
});
}, undefined, 'arrayChange');

watchChildren(child(), (keepOffParentList ? null : child), parents, unwatch, true);

return true;

} else {

if (unwatch === true) {
// A subscribable was removed from an array or mutable object.
// Clean up all its change subscriptions through either H or _subscriptions
// depending on whether the Knockout code is minified or not.
var subsc = child.H || child._subscriptions;

if (subsc) {
if (subsc.change)
for (var i = subsc.change.length - 1; i >= 0; i--)
if (subsc.change[i]._watcher === context)
subsc.change[i].dispose();

if (subsc.beforeChange && options.keepOldValues > 0)
// Also clean up any before-change subscriptions used for tracking old values.
for (var i = subsc.beforeChange.length - 1; i >= 0; i--)
if (subsc.beforeChange[i]._watcher === context)
subsc.beforeChange[i].dispose();
}

watchChildren(child(), (keepOffParentList ? null : child), parents, true, true);

} else {

if (options.enabled === true && child.watchable === false)
// Only waking up an existing watcher. Let's not add another.
return;

if (options.keepOldValues > 0 || options.mutable) {
child.subscribe(function (oldValue) {
if (options.keepOldValues > 0) {
// Add old value to history list before every update.
var values = (child['oldValues']
? child['oldValues']
: child['oldValues'] = []);

values.unshift(oldValue);

while (values.length > options.keepOldValues)
values.pop();
}

if (options.mutable && typeof oldValue === 'object')
// Clean up all subscriptions for the old child object.
watchChildren(oldValue, (keepOffParentList ? null : child), parents, true, true);

}, null, 'beforeChange')._watcher = context;
}

child.subscribe(function () {
if (child.watchable !== false) {
var returnValue = evaluatorCallback.call(context, parents, child);

if (returnValue !== undefined)
context(returnValue);

if (options.mutable && typeof child() === 'object')
// Watch the new comer object.
watchChildren(child(), (keepOffParentList ? null : child), parents);
}

}, null, 'change')._watcher = context;

if (options.hideWrappedValues !== true)
watchChildren(child(), (keepOffParentList ? null : child), parents);
}
}
}
}

} else {
switch (Object.prototype.toString.call(child)) {
case '[object Object]':
ko.utils.objectForEach(child, function (property, sub) {
if (options.wrap) {
// Wrap simple objects and arrays into observables.
var type = Object.prototype.toString.call(sub);
if (type !== '[object Function]' && type !== '[object Object]') {
if (!options.beforeWrap || options.beforeWrap.call(context, parents, child, sub) !== false) {
sub = child[property] = type === '[object Array]'
? ko.observableArray(sub)
: ko.observable(sub);
}
}
}

var subHasChildren = watchChildren(sub, (keepOffParentList ? null : child), parents, unwatch);

if (subHasChildren && options.tagParentsWithName === true && !sub['_fieldName'])
sub['_fieldName'] = property;
});
return true;

case '[object Array]':
if (options.hideArrays !== true)
for (var i = 0; i < child.length; i++)
watchChildren(child[i], (keepOffParentList ? null : child), parents, unwatch);
return true;
}
}
}
}

// Use a computed when targeting a non-watchable function.
if (typeof target === 'function' && !ko.isSubscribable(target))
return ko.computed(target, evaluatorCallback, options);

watchChildren(target, null, []);
};
Loading

0 comments on commit e1ae3df

Please sign in to comment.