maccman-bowline 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +4 -0
- data/MIT-LICENSE +20 -0
- data/Manifest.txt +58 -0
- data/README.txt +113 -0
- data/Rakefile +24 -0
- data/assets/jquery.bowline.js +96 -0
- data/assets/jquery.chain.js +2348 -0
- data/assets/jquery.js +3549 -0
- data/bin/bowline-gen +6 -0
- data/bowline.gemspec +42 -0
- data/examples/account_binder.rb +29 -0
- data/examples/example.js +24 -0
- data/examples/twitter.html +43 -0
- data/examples/twitter_binder.rb +40 -0
- data/examples/twitter_login.html +29 -0
- data/examples/users_binder.rb +39 -0
- data/lib/bowline.rb +42 -0
- data/lib/bowline/binders.rb +177 -0
- data/lib/bowline/binders/collection.rb +27 -0
- data/lib/bowline/binders/singleton.rb +25 -0
- data/lib/bowline/commands/console.rb +27 -0
- data/lib/bowline/commands/generate.rb +1 -0
- data/lib/bowline/commands/run.rb +13 -0
- data/lib/bowline/ext/array.rb +5 -0
- data/lib/bowline/ext/class.rb +51 -0
- data/lib/bowline/ext/object.rb +12 -0
- data/lib/bowline/ext/string.rb +9 -0
- data/lib/bowline/gem_dependency.rb +42 -0
- data/lib/bowline/generators.rb +59 -0
- data/lib/bowline/generators/application.rb +49 -0
- data/lib/bowline/generators/binder.rb +25 -0
- data/lib/bowline/generators/migration.rb +51 -0
- data/lib/bowline/generators/model.rb +20 -0
- data/lib/bowline/initializer.rb +596 -0
- data/lib/bowline/jquery.rb +31 -0
- data/lib/bowline/observer.rb +43 -0
- data/lib/bowline/tasks/app.rake +70 -0
- data/lib/bowline/tasks/bowline.rb +8 -0
- data/lib/bowline/tasks/database.rake +167 -0
- data/lib/bowline/tasks/log.rake +9 -0
- data/lib/bowline/tasks/misk.rake +3 -0
- data/templates/Rakefile +7 -0
- data/templates/binder.rb +9 -0
- data/templates/config/application.yml +1 -0
- data/templates/config/boot.rb +21 -0
- data/templates/config/database.yml +4 -0
- data/templates/config/environment.rb +12 -0
- data/templates/config/manifest +18 -0
- data/templates/config/tiapp.xml +24 -0
- data/templates/gitignore +15 -0
- data/templates/migration.rb +7 -0
- data/templates/model.rb +4 -0
- data/templates/public/index.html.erb +23 -0
- data/templates/public/javascripts/application.js +0 -0
- data/templates/public/stylesheets/application.css +0 -0
- data/templates/script/console +3 -0
- data/templates/script/init +11 -0
- data/templates/script/run +3 -0
- metadata +143 -0
data/History.txt
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Made by Many Limited and Alexander MacCaw
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest.txt
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
History.txt
|
2
|
+
MIT-LICENSE
|
3
|
+
Manifest.txt
|
4
|
+
README.txt
|
5
|
+
Rakefile
|
6
|
+
assets/jquery.bowline.js
|
7
|
+
assets/jquery.chain.js
|
8
|
+
assets/jquery.js
|
9
|
+
bin/bowline-gen
|
10
|
+
bowline.gemspec
|
11
|
+
examples/account_binder.rb
|
12
|
+
examples/example.js
|
13
|
+
examples/twitter.html
|
14
|
+
examples/twitter_binder.rb
|
15
|
+
examples/twitter_login.html
|
16
|
+
examples/users_binder.rb
|
17
|
+
lib/bowline.rb
|
18
|
+
lib/bowline/binders.rb
|
19
|
+
lib/bowline/binders/collection.rb
|
20
|
+
lib/bowline/binders/singleton.rb
|
21
|
+
lib/bowline/commands/console.rb
|
22
|
+
lib/bowline/commands/generate.rb
|
23
|
+
lib/bowline/commands/run.rb
|
24
|
+
lib/bowline/ext/array.rb
|
25
|
+
lib/bowline/ext/class.rb
|
26
|
+
lib/bowline/ext/object.rb
|
27
|
+
lib/bowline/ext/string.rb
|
28
|
+
lib/bowline/gem_dependency.rb
|
29
|
+
lib/bowline/generators.rb
|
30
|
+
lib/bowline/generators/application.rb
|
31
|
+
lib/bowline/generators/binder.rb
|
32
|
+
lib/bowline/generators/migration.rb
|
33
|
+
lib/bowline/generators/model.rb
|
34
|
+
lib/bowline/initializer.rb
|
35
|
+
lib/bowline/jquery.rb
|
36
|
+
lib/bowline/observer.rb
|
37
|
+
lib/bowline/tasks/app.rake
|
38
|
+
lib/bowline/tasks/bowline.rb
|
39
|
+
lib/bowline/tasks/database.rake
|
40
|
+
lib/bowline/tasks/log.rake
|
41
|
+
lib/bowline/tasks/misk.rake
|
42
|
+
templates/Rakefile
|
43
|
+
templates/binder.rb
|
44
|
+
templates/config/application.yml
|
45
|
+
templates/config/boot.rb
|
46
|
+
templates/config/database.yml
|
47
|
+
templates/config/environment.rb
|
48
|
+
templates/config/manifest
|
49
|
+
templates/config/tiapp.xml
|
50
|
+
templates/gitignore
|
51
|
+
templates/migration.rb
|
52
|
+
templates/model.rb
|
53
|
+
templates/public/index.html.erb
|
54
|
+
templates/public/javascripts/application.js
|
55
|
+
templates/public/stylesheets/application.css
|
56
|
+
templates/script/console
|
57
|
+
templates/script/init
|
58
|
+
templates/script/run
|
data/README.txt
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
= Bowline
|
2
|
+
|
3
|
+
http://github.com/maccman/bowline
|
4
|
+
|
5
|
+
= DESCRIPTION
|
6
|
+
|
7
|
+
My take on Ruby desktop GUIs
|
8
|
+
|
9
|
+
= FEATURES
|
10
|
+
|
11
|
+
* Cross platform
|
12
|
+
* MVC
|
13
|
+
* Uses Webkit
|
14
|
+
* View in HTML/JavaScript
|
15
|
+
* Binding between HTML & Ruby
|
16
|
+
|
17
|
+
= CONTACT
|
18
|
+
|
19
|
+
info@eribium.org
|
20
|
+
http://eribium.org
|
21
|
+
http://twitter.com/maccman
|
22
|
+
|
23
|
+
= Usage
|
24
|
+
|
25
|
+
bowline-gen app bowline_twitter
|
26
|
+
cd bowline_twitter
|
27
|
+
bowline-gen binder twitter
|
28
|
+
|
29
|
+
|
30
|
+
= EXAMPLES
|
31
|
+
|
32
|
+
Have a look at the examples for an example twitter client.
|
33
|
+
|
34
|
+
Usage for a collection (of users):
|
35
|
+
|
36
|
+
module Binders
|
37
|
+
class Users < Bowline::Collection
|
38
|
+
# These are class methods
|
39
|
+
# i.e. methods that appear on
|
40
|
+
# users, rather an user
|
41
|
+
class << self
|
42
|
+
def index
|
43
|
+
# self.items is a magic variable -
|
44
|
+
# it'll update the html binders
|
45
|
+
self.items = User.all
|
46
|
+
end
|
47
|
+
|
48
|
+
def admins
|
49
|
+
# This just replaces all the listed
|
50
|
+
# users with just admins
|
51
|
+
self.items = User.admins.all
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Singleton methods, get added
|
56
|
+
# to individual users.
|
57
|
+
#
|
58
|
+
# self.element is the jQuery element
|
59
|
+
# for that user, so calling highlight
|
60
|
+
# on it is equivalent to:
|
61
|
+
# $(user).highlight()
|
62
|
+
#
|
63
|
+
# self.item is the user object, in this case
|
64
|
+
# an ActiveRecord instance
|
65
|
+
#
|
66
|
+
# self.page gives you access to the dom, e.g:
|
67
|
+
# self.page.alert('hello world')
|
68
|
+
|
69
|
+
def destroy
|
70
|
+
self.item.destroy
|
71
|
+
self.element.remove
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
<html>
|
77
|
+
<head>
|
78
|
+
<script src="jquery.js" type="text/javascript" charset="utf-8"></script>
|
79
|
+
<script src="chain.js" type="text/javascript" charset="utf-8"></script>
|
80
|
+
<script src="bowline.js" type="text/javascript" charset="utf-8"></script>
|
81
|
+
<script type="text/javascript" charset="utf-8">
|
82
|
+
jQuery(function($){
|
83
|
+
// Bind the element users to UserBinder
|
84
|
+
var users = $('#users').bowline('users', function(){
|
85
|
+
var self = $(this);
|
86
|
+
self.find('.destroy').click(function(){
|
87
|
+
self.invoke('destroy');
|
88
|
+
return false;
|
89
|
+
})
|
90
|
+
});
|
91
|
+
|
92
|
+
$('#showAdmins').click(function(){
|
93
|
+
users.invoke('admins');
|
94
|
+
return false;
|
95
|
+
});
|
96
|
+
|
97
|
+
// Populate with all the users
|
98
|
+
users.invoke('index');
|
99
|
+
});
|
100
|
+
</script>
|
101
|
+
</head>
|
102
|
+
<body>
|
103
|
+
<div id="users">
|
104
|
+
<div class="item">
|
105
|
+
<span class="name"></span>
|
106
|
+
<span class="email"></span>
|
107
|
+
<a href="#" class="destroy">Delete</a>
|
108
|
+
</div>
|
109
|
+
</div>
|
110
|
+
|
111
|
+
<a href="#" id="showAdmins">Show admins</a>
|
112
|
+
</body>
|
113
|
+
</html>
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
%w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
|
2
|
+
require File.dirname(__FILE__) + '/lib/bowline'
|
3
|
+
|
4
|
+
# Generate all the Rake tasks
|
5
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
6
|
+
$hoe = Hoe.new('bowline', Bowline::VERSION) do |p|
|
7
|
+
p.developer('Alex MacCaw', 'info@eribium.org')
|
8
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
9
|
+
p.rubyforge_name = 'maccman'
|
10
|
+
p.extra_deps << ['templater', '>=0.3.2']
|
11
|
+
p.extra_dev_deps = [
|
12
|
+
['newgem', ">= #{::Newgem::VERSION}"]
|
13
|
+
]
|
14
|
+
p.clean_globs |= %w[**/.DS_Store tmp *.log]
|
15
|
+
path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
|
16
|
+
p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
|
17
|
+
p.rsync_args = '-av --delete --ignore-errors'
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'newgem/tasks' # load /tasks/*.rake
|
21
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
22
|
+
|
23
|
+
# TODO - want other tests/tasks run by default? Add them to the list
|
24
|
+
# task :default => [:spec, :features]
|
@@ -0,0 +1,96 @@
|
|
1
|
+
(function($){
|
2
|
+
$.bowline = {
|
3
|
+
setup: function(name, el){
|
4
|
+
var rb = eval("bowline_" + name + "_setup");
|
5
|
+
if(!rb) throw 'Unknown class';
|
6
|
+
rb(el);
|
7
|
+
},
|
8
|
+
|
9
|
+
klass: function(name){
|
10
|
+
var rb = eval("bowline_" + name);
|
11
|
+
if(!rb) throw 'Unknown class';
|
12
|
+
return rb;
|
13
|
+
},
|
14
|
+
|
15
|
+
instance: function(name, el){
|
16
|
+
var rb = eval("bowline_" + name + "_instance");
|
17
|
+
if(!rb) throw 'Unknown class';
|
18
|
+
return rb(el);
|
19
|
+
},
|
20
|
+
|
21
|
+
setupForms: function(){
|
22
|
+
// $('form').bind('submit', function(e){
|
23
|
+
// var src = $(this).attr('src').split('.');
|
24
|
+
// var rb = $.bowline.klass[src[0]];
|
25
|
+
// rb.params = $(this).serialize();
|
26
|
+
// rb.send(src[1]);
|
27
|
+
// return false;
|
28
|
+
// });
|
29
|
+
},
|
30
|
+
|
31
|
+
// A lot of JS libs require hashes
|
32
|
+
// without any functions in them
|
33
|
+
rubyHash: function( hsh ) {
|
34
|
+
res = {};
|
35
|
+
$.each(hsh, function(key, value){
|
36
|
+
if(typeof(value) != 'function'){
|
37
|
+
res[key] = value;
|
38
|
+
}
|
39
|
+
});
|
40
|
+
return res;
|
41
|
+
},
|
42
|
+
|
43
|
+
// Length on a Ruby array is a function
|
44
|
+
rubyMap: function( elems, callback ) {
|
45
|
+
var ret = [];
|
46
|
+
|
47
|
+
for ( var i = 0, length = elems.length(); i < length; i++ ) {
|
48
|
+
var value = callback( elems[ i ], i );
|
49
|
+
|
50
|
+
if ( value != null )
|
51
|
+
ret[ ret.length ] = value;
|
52
|
+
}
|
53
|
+
|
54
|
+
return ret.concat.apply( [], ret );
|
55
|
+
}
|
56
|
+
},
|
57
|
+
|
58
|
+
$.fn.bowline = function(name, options){
|
59
|
+
$(this).chain(options);
|
60
|
+
$.bowline.setup(name, $(this));
|
61
|
+
$(this).data('bowline', name);
|
62
|
+
$(this).trigger('setup:bowline');
|
63
|
+
return this;
|
64
|
+
};
|
65
|
+
|
66
|
+
$.fn.invoke = function(){
|
67
|
+
if($(this).chain('active')){
|
68
|
+
if($(this).data('bowline')){
|
69
|
+
// Class method
|
70
|
+
var name = $(this).data('bowline');
|
71
|
+
var func = $.bowline.klass(name);
|
72
|
+
} else {
|
73
|
+
// Instance method
|
74
|
+
var name = $(this).item('root').data('bowline');
|
75
|
+
var func = $.bowline.instance(name, $(this));
|
76
|
+
}
|
77
|
+
return func.apply(func, arguments);
|
78
|
+
} else {
|
79
|
+
throw 'Chain not active';
|
80
|
+
}
|
81
|
+
};
|
82
|
+
|
83
|
+
$.fn.updateCollection = function( items ){
|
84
|
+
items = $.bowline.rubyMap(items, function(n){
|
85
|
+
return $.bowline.rubyHash(n);
|
86
|
+
});
|
87
|
+
$(this).items('replace', items);
|
88
|
+
$(this).trigger('update:bowline');
|
89
|
+
};
|
90
|
+
|
91
|
+
$.fn.updateSingleton = function( item ){
|
92
|
+
item = $.bowline.rubyHash(item);
|
93
|
+
$(this).item('replace', item);
|
94
|
+
$(this).trigger('update:bowline');
|
95
|
+
};
|
96
|
+
})(jQuery)
|
@@ -0,0 +1,2348 @@
|
|
1
|
+
/**
|
2
|
+
* Chain.js
|
3
|
+
* jQuery Plugin for Data Binding
|
4
|
+
*
|
5
|
+
* Copyright (c) 2008 Rizqi Ahmad
|
6
|
+
*
|
7
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
* of this software and associated documentation files (the "Software"), to deal
|
9
|
+
* in the Software without restriction, including without limitation the rights
|
10
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
* copies of the Software, and to permit persons to whom the Software is
|
12
|
+
* furnished to do so, subject to the following conditions:
|
13
|
+
*
|
14
|
+
* The above copyright notice and this permission notice shall be included in
|
15
|
+
* all copies or substantial portions of the Software.
|
16
|
+
*
|
17
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
|
+
* THE SOFTWARE.
|
24
|
+
*/
|
25
|
+
|
26
|
+
|
27
|
+
/* core.js */
|
28
|
+
(function($){
|
29
|
+
|
30
|
+
/**
|
31
|
+
* Chain Namespace
|
32
|
+
*
|
33
|
+
* @alias jQuery.Chain
|
34
|
+
* @namespace
|
35
|
+
*/
|
36
|
+
$.Chain =
|
37
|
+
{
|
38
|
+
/**
|
39
|
+
* Version Number
|
40
|
+
*
|
41
|
+
* @alias jQuery.Chain.version
|
42
|
+
* @property {String}
|
43
|
+
*/
|
44
|
+
version: '0.2',
|
45
|
+
|
46
|
+
/**
|
47
|
+
* Tag for use in @jQuery.Chain.parse@ (which is used in CustomUpdater).
|
48
|
+
* It is can be altered.
|
49
|
+
*
|
50
|
+
* @alias jQuery.Chain.tags
|
51
|
+
*
|
52
|
+
* @property {Array}
|
53
|
+
*
|
54
|
+
* @see jQuery.Chain.parse
|
55
|
+
*/
|
56
|
+
tag: ['{', '}'],
|
57
|
+
|
58
|
+
/**
|
59
|
+
* Namespace containing all defined services.
|
60
|
+
*
|
61
|
+
* @namespace
|
62
|
+
*
|
63
|
+
* @alias jQuery.Chain.services
|
64
|
+
*/
|
65
|
+
services: {},
|
66
|
+
|
67
|
+
/**
|
68
|
+
* Register a service to the service manager.
|
69
|
+
*
|
70
|
+
* @alias jQuery.Chain.service
|
71
|
+
*
|
72
|
+
* @param {String} name Service Name
|
73
|
+
* @param {Object} proto Service Object Prototype
|
74
|
+
*
|
75
|
+
* @example Create a Custom Service
|
76
|
+
* $.Chain.service('test', {
|
77
|
+
* // Default command handler
|
78
|
+
* handler: function(option)
|
79
|
+
* {
|
80
|
+
* // do something
|
81
|
+
* },
|
82
|
+
* // $(selector).test('what', somearg)
|
83
|
+
* $what: function(somearg)
|
84
|
+
* {
|
85
|
+
* // do something
|
86
|
+
* }
|
87
|
+
* });
|
88
|
+
*
|
89
|
+
* $('#element').test();
|
90
|
+
*
|
91
|
+
* @see jQuery.Chain.extend
|
92
|
+
*/
|
93
|
+
service: function(name, proto)
|
94
|
+
{
|
95
|
+
this.services[name] = proto;
|
96
|
+
|
97
|
+
// Creating jQuery fn module with the service
|
98
|
+
$.fn[name] = function(options)
|
99
|
+
{
|
100
|
+
if(!this.length)
|
101
|
+
{return this;}
|
102
|
+
|
103
|
+
// Create Chain instance
|
104
|
+
var instance = this.data('chain-'+name);
|
105
|
+
|
106
|
+
// Extract arguments
|
107
|
+
var args = Array.prototype.slice.call(arguments, 1);
|
108
|
+
|
109
|
+
// Create Instance if it doesn't already exist
|
110
|
+
if(!instance)
|
111
|
+
{
|
112
|
+
// Return immediately if destroyed is called before Instance is created
|
113
|
+
if(options == 'destroy')
|
114
|
+
{return this;}
|
115
|
+
// Create Instance
|
116
|
+
instance = $.extend({element: this}, $.Chain.services[name]);
|
117
|
+
this.data('chain-'+name, instance);
|
118
|
+
// Initialize if possible
|
119
|
+
if(instance.init)
|
120
|
+
{instance.init();}
|
121
|
+
}
|
122
|
+
|
123
|
+
var result;
|
124
|
+
|
125
|
+
// Check whether to execute a command
|
126
|
+
if(typeof options == 'string' && instance['$'+options])
|
127
|
+
{result = instance['$'+options].apply(instance, args);}
|
128
|
+
|
129
|
+
// Otherwise try to execute default handler
|
130
|
+
else if(instance['handler'])
|
131
|
+
{result = instance['handler'].apply(instance, [options].concat(args));}
|
132
|
+
|
133
|
+
// Otherwise do nothing
|
134
|
+
else
|
135
|
+
{result = this;}
|
136
|
+
|
137
|
+
// Remove instance on destroy
|
138
|
+
if(options == 'destroy')
|
139
|
+
{this.removeData('chain-'+name);}
|
140
|
+
|
141
|
+
return result;
|
142
|
+
};
|
143
|
+
},
|
144
|
+
|
145
|
+
/**
|
146
|
+
* Extends service functionalities.
|
147
|
+
*
|
148
|
+
* @alias jQuery.Chain.extend
|
149
|
+
*
|
150
|
+
* @param {String} name Service Name
|
151
|
+
* @param {Object} proto Service Object Prototype
|
152
|
+
*
|
153
|
+
* @see jQuery.Chain.service
|
154
|
+
*/
|
155
|
+
extend: function(name, proto)
|
156
|
+
{
|
157
|
+
if(this.services[name])
|
158
|
+
{this.services[name] = $.extend(this.services[name], proto);}
|
159
|
+
},
|
160
|
+
|
161
|
+
/**
|
162
|
+
* Check whether it is a jQuery Object
|
163
|
+
*
|
164
|
+
* @alias jQuery.Chain.jobject
|
165
|
+
*
|
166
|
+
* @param {Object} obj Object to be checked
|
167
|
+
*
|
168
|
+
* @example Using @jobject@
|
169
|
+
* $.Chain.jobject($()) // returns true
|
170
|
+
* $.Chain.jobject("test") // returns false
|
171
|
+
*
|
172
|
+
* @return {Boolean} True or False
|
173
|
+
*
|
174
|
+
* @see jQuery.Chain.jindentic
|
175
|
+
*/
|
176
|
+
jobject: function(obj)
|
177
|
+
{
|
178
|
+
return obj && obj.init == $.fn.init;
|
179
|
+
},
|
180
|
+
|
181
|
+
/**
|
182
|
+
* Check whether two jQuery Collection identic
|
183
|
+
*
|
184
|
+
* @alias jQuery.Chain.jidentic
|
185
|
+
*
|
186
|
+
* @param {Object} j1 jQuery Object
|
187
|
+
* @param {Object} j2 jQuery Object
|
188
|
+
*
|
189
|
+
* @example Using @jidentic@
|
190
|
+
* a = $('div');
|
191
|
+
* b = $('div');
|
192
|
+
* c = $('div.test');
|
193
|
+
*
|
194
|
+
* (a == b) //returns false
|
195
|
+
*
|
196
|
+
* $.Chain.jidentic(a, b) // returns true
|
197
|
+
* $.Chain.jidentic(a, c) // returns false
|
198
|
+
*
|
199
|
+
* @return {Boolean} True or False
|
200
|
+
*
|
201
|
+
* @see jQuery.Chain.jobject
|
202
|
+
*/
|
203
|
+
jidentic: function(j1, j2)
|
204
|
+
{
|
205
|
+
if(!j1 || !j2 || j1.length != j2.length)
|
206
|
+
{return false;}
|
207
|
+
|
208
|
+
var a1 = j1.get();
|
209
|
+
var a2 = j2.get();
|
210
|
+
|
211
|
+
for(var i=0; i<a1.length; i++)
|
212
|
+
{
|
213
|
+
if(a1[i] != a2[i])
|
214
|
+
{return false;}
|
215
|
+
}
|
216
|
+
|
217
|
+
return true;
|
218
|
+
|
219
|
+
},
|
220
|
+
|
221
|
+
/**
|
222
|
+
* Parse string contained @{something}@ to a Function
|
223
|
+
* that when executed replace those with the data it refers to.
|
224
|
+
* You can change the @{}@ tag by modifying @jQuery.Chain.tag@
|
225
|
+
*
|
226
|
+
* @param {String} text String
|
227
|
+
*
|
228
|
+
* @example Using @
|
229
|
+
* var fn = $.Chain.parse("My name is {first} {last}");
|
230
|
+
* fn({first:'Rizqi', last:'Ahmad'}) // returns "My name is Rizqi Ahmad"
|
231
|
+
*
|
232
|
+
* @return {Function} template string.
|
233
|
+
*
|
234
|
+
* @see jQuery.Chain.tag
|
235
|
+
*/
|
236
|
+
parse: function()
|
237
|
+
{
|
238
|
+
var $this = {};
|
239
|
+
// Function Closure
|
240
|
+
$this.closure =
|
241
|
+
[
|
242
|
+
'function($data, $el){'
|
243
|
+
+'var $text = [];\n'
|
244
|
+
+'$text.print = function(text)'
|
245
|
+
+'{this.push((typeof text == "number") ? text : ((typeof text != "undefined") ? text : ""));};\n'
|
246
|
+
+'with($data){\n',
|
247
|
+
|
248
|
+
'}\n'
|
249
|
+
+'return $text.join("");'
|
250
|
+
+'}'
|
251
|
+
];
|
252
|
+
|
253
|
+
// Print text template
|
254
|
+
$this.textPrint = function(text)
|
255
|
+
{
|
256
|
+
return '$text.print("'
|
257
|
+
+text.split('\\').join('\\\\').split("'").join("\\'").split('"').join('\\"')
|
258
|
+
+'");';
|
259
|
+
};
|
260
|
+
|
261
|
+
// Print script template
|
262
|
+
$this.scriptPrint = function(text)
|
263
|
+
{
|
264
|
+
return '$text.print('+text+');';
|
265
|
+
};
|
266
|
+
|
267
|
+
$this.parser = function(text){
|
268
|
+
var tag = $.Chain.tag;
|
269
|
+
|
270
|
+
var opener, closer, closer2 = null, result = [];
|
271
|
+
|
272
|
+
while(text){
|
273
|
+
|
274
|
+
// Check where the opener and closer tag
|
275
|
+
// are located in the text.
|
276
|
+
opener = text.indexOf(tag[0]);
|
277
|
+
closer = opener + text.substring(opener).indexOf(tag[1]);
|
278
|
+
|
279
|
+
// If opener tag exists, otherwise there are no tags anymore
|
280
|
+
if(opener != -1)
|
281
|
+
{
|
282
|
+
// Handle escape. Tag can be escaped with '\\'.
|
283
|
+
// If tag is escaped. it will be handled as a normal text
|
284
|
+
// Otherwise it will be handled as a script
|
285
|
+
if(text[opener-1] == '\\')
|
286
|
+
{
|
287
|
+
closer2 = opener+tag[0].length + text.substring(opener+tag[0].length).indexOf(tag[0]);
|
288
|
+
if(closer2 != opener+tag[0].length-1 && text[closer2-1] == '\\')
|
289
|
+
{closer2 = closer2-1;}
|
290
|
+
else if(closer2 == opener+tag[0].length-1)
|
291
|
+
{closer2 = text.length;}
|
292
|
+
|
293
|
+
result.push($this.textPrint(text.substring(0, opener-1)));
|
294
|
+
result.push($this.textPrint(text.substring(opener, closer2)));
|
295
|
+
}
|
296
|
+
else
|
297
|
+
{
|
298
|
+
closer2 = null;
|
299
|
+
if(closer == opener-1)
|
300
|
+
{closer = text.length;}
|
301
|
+
|
302
|
+
result.push($this.textPrint(text.substring(0, opener)));
|
303
|
+
result.push($this.scriptPrint(text.substring(opener+tag[0].length, closer)));
|
304
|
+
}
|
305
|
+
|
306
|
+
text = text.substring((closer2 === null) ? closer+tag[1].length : closer2);
|
307
|
+
}
|
308
|
+
// If there are still text, it will be pushed to array
|
309
|
+
// So we won't stuck in an infinite loop
|
310
|
+
else if(text)
|
311
|
+
{
|
312
|
+
result.push($this.textPrint(text));
|
313
|
+
text = '';
|
314
|
+
}
|
315
|
+
}
|
316
|
+
|
317
|
+
return result.join('\n');
|
318
|
+
};
|
319
|
+
|
320
|
+
|
321
|
+
/*
|
322
|
+
* Real function begins here.
|
323
|
+
* We use closure for private variables and function.
|
324
|
+
*/
|
325
|
+
return function($text)
|
326
|
+
{
|
327
|
+
var $fn = function(){};
|
328
|
+
try
|
329
|
+
{
|
330
|
+
eval('$fn = '+ $this.closure[0]+$this.parser($text)+$this.closure[1]);
|
331
|
+
}
|
332
|
+
catch(e)
|
333
|
+
{
|
334
|
+
throw "Parsing Error";
|
335
|
+
}
|
336
|
+
|
337
|
+
return $fn;
|
338
|
+
};
|
339
|
+
}()
|
340
|
+
};
|
341
|
+
|
342
|
+
})(jQuery);
|
343
|
+
|
344
|
+
/* update.js */
|
345
|
+
/**
|
346
|
+
* Chain Update Service
|
347
|
+
*
|
348
|
+
* @alias update
|
349
|
+
*
|
350
|
+
* @syntax $(selector).update(parameters);
|
351
|
+
*/
|
352
|
+
|
353
|
+
(function($){
|
354
|
+
|
355
|
+
/**
|
356
|
+
* Chain Update Service Object - Providing methods of @update@.
|
357
|
+
* All method listed here can only be used internally
|
358
|
+
* using @jQuery.Chain.service@ or @jQuery.Chain.extend@
|
359
|
+
*
|
360
|
+
* @namespace
|
361
|
+
*
|
362
|
+
* @alias jQuery.Chain.services.update
|
363
|
+
*
|
364
|
+
* @see jQuery.Chain.service
|
365
|
+
* @see jQuery.Chain.extend
|
366
|
+
*/
|
367
|
+
|
368
|
+
$.Chain.service('update', {
|
369
|
+
/**
|
370
|
+
* Default Handler
|
371
|
+
*
|
372
|
+
* @alias jQuery.Chain.services.update.handler
|
373
|
+
*
|
374
|
+
* @see jQuery.Chain.service
|
375
|
+
* @see jQuery.Chain.services.update.bind
|
376
|
+
* @see jQuery.Chain.services.update.trigger
|
377
|
+
*/
|
378
|
+
handler: function(opt)
|
379
|
+
{
|
380
|
+
if(typeof opt == 'function')
|
381
|
+
{return this.bind(opt);}
|
382
|
+
else
|
383
|
+
{return this.trigger(opt);}
|
384
|
+
},
|
385
|
+
|
386
|
+
/**
|
387
|
+
* If you pass a function to update, it will bind it to the update event.
|
388
|
+
* just like jQuerys @click()@ or @mouseover()@.
|
389
|
+
*
|
390
|
+
* @alias update(fn)
|
391
|
+
* @alias jQuery.Chain.services.update.bind
|
392
|
+
*
|
393
|
+
* @param {Function} fn Listener
|
394
|
+
*
|
395
|
+
* @example
|
396
|
+
* // assuming #person is already chained
|
397
|
+
* $('#person').update(function{
|
398
|
+
* alert($(this).item().name);
|
399
|
+
* });
|
400
|
+
*
|
401
|
+
* $('#person').item({name: 'Rizqi'})
|
402
|
+
*
|
403
|
+
* @return {Object} jQuery Object
|
404
|
+
*
|
405
|
+
* @see jQuery.Chain.services.update.handler
|
406
|
+
*/
|
407
|
+
bind: function(fn)
|
408
|
+
{
|
409
|
+
return this.element.bind('update', fn);
|
410
|
+
},
|
411
|
+
|
412
|
+
/**
|
413
|
+
* If no argument or "hard" is passed,
|
414
|
+
* it will update the element and trigger the update event.
|
415
|
+
*
|
416
|
+
* @alias update(opt)
|
417
|
+
* @alias jQuery.Chain.services.update.trigger
|
418
|
+
*
|
419
|
+
* @param {String} opt If 'hard', it will update each of items
|
420
|
+
*
|
421
|
+
* @example
|
422
|
+
* $('#person').update();
|
423
|
+
*
|
424
|
+
* @return {Object} jQuery Object
|
425
|
+
*
|
426
|
+
* @see jQuery.Chain.services.update.handler
|
427
|
+
*/
|
428
|
+
trigger: function(opt)
|
429
|
+
{
|
430
|
+
this.element.items('update');
|
431
|
+
this.element.item('update');
|
432
|
+
|
433
|
+
this.element.triggerHandler('preupdate', this.element.item());
|
434
|
+
|
435
|
+
if(opt == 'hard')
|
436
|
+
{this.element.items(true).each(function(){$(this).update();});}
|
437
|
+
|
438
|
+
this.element.triggerHandler('update', this.element.item());
|
439
|
+
|
440
|
+
return this.element;
|
441
|
+
}
|
442
|
+
});
|
443
|
+
|
444
|
+
})(jQuery);
|
445
|
+
|
446
|
+
/* chain.js */
|
447
|
+
/**
|
448
|
+
* Chain Binding Service.
|
449
|
+
* Method to activate the chaining / element rendering service.
|
450
|
+
*
|
451
|
+
* @alias chain
|
452
|
+
*
|
453
|
+
* @syntax $(selector).chain(parameters);
|
454
|
+
*/
|
455
|
+
|
456
|
+
(function($){
|
457
|
+
|
458
|
+
/**
|
459
|
+
* Chain Binding Service Object - Providing methods of @chain@.
|
460
|
+
* All method listed here can only be used internally
|
461
|
+
* using @jQuery.Chain.service@ or @jQuery.Chain.extend@
|
462
|
+
*
|
463
|
+
* @namespace
|
464
|
+
*
|
465
|
+
* @alias jQuery.Chain.services.chain
|
466
|
+
*
|
467
|
+
* @see jQuery.Chain.service
|
468
|
+
* @see jQuery.Chain.extend
|
469
|
+
*/
|
470
|
+
|
471
|
+
$.Chain.service('chain', {
|
472
|
+
/**
|
473
|
+
* Initializer. Executed once at the first time @chain@ invoked.
|
474
|
+
*
|
475
|
+
* @alias jQuery.Chain.services.chain.init
|
476
|
+
*
|
477
|
+
* @see jQuery.Chain.service
|
478
|
+
*/
|
479
|
+
init: function()
|
480
|
+
{
|
481
|
+
this.anchor = this.element;
|
482
|
+
this.template = this.anchor.html();
|
483
|
+
this.tplNumber = 0; // At Default it uses the first template.
|
484
|
+
this.builder = this.createBuilder();
|
485
|
+
this.plugins = {};
|
486
|
+
this.isActive = false;
|
487
|
+
this.destroyers = [];
|
488
|
+
|
489
|
+
// Add class 'chain-element' as identifier
|
490
|
+
this.element.addClass('chain-element');
|
491
|
+
},
|
492
|
+
|
493
|
+
/**
|
494
|
+
* Default handler.
|
495
|
+
*
|
496
|
+
* @alias jQuery.Chain.services.chain.handler
|
497
|
+
*
|
498
|
+
* @param {Object} obj Object to be handled
|
499
|
+
*
|
500
|
+
* @return {Object} jQuery Object
|
501
|
+
*
|
502
|
+
* @see jQuery.Chain.service
|
503
|
+
* @see jQuery.Chain.services.chain.handleUpdater
|
504
|
+
* @see jQuery.Chain.services.chain.handleBuilder
|
505
|
+
*/
|
506
|
+
handler: function(obj, bool)
|
507
|
+
{
|
508
|
+
// Backup items and item, all items will be stored in Buffer
|
509
|
+
this.element.items('backup');
|
510
|
+
this.element.item('backup');
|
511
|
+
|
512
|
+
if(typeof obj == 'object')
|
513
|
+
{this.handleUpdater(obj);}
|
514
|
+
else if(typeof obj == 'function')
|
515
|
+
{this.handleBuilder(obj, bool);}
|
516
|
+
|
517
|
+
// Empty element, if @item@ it will filled again later
|
518
|
+
this.anchor.empty();
|
519
|
+
|
520
|
+
this.isActive = true;
|
521
|
+
this.element.update();
|
522
|
+
|
523
|
+
return this.element;
|
524
|
+
},
|
525
|
+
|
526
|
+
/**
|
527
|
+
* Updater Handler.
|
528
|
+
* If you pass an object to @chain@, it will treated as a updater object.
|
529
|
+
* The updater is a hash of selector and value string:
|
530
|
+
* like @chain({'my css selector': 'My Content String'})@
|
531
|
+
* or @chain({'my css selector': {attributes}})@
|
532
|
+
*
|
533
|
+
* @alias chain(updater)
|
534
|
+
* @alias jQuery.Chain.services.chain.handleUpdater
|
535
|
+
*
|
536
|
+
* @param {Object} rules Updater rules to be parsed
|
537
|
+
*
|
538
|
+
* @example Usage
|
539
|
+
* $(selector)
|
540
|
+
* .chain({
|
541
|
+
* // Items anchor, where the Item iteration should be placed
|
542
|
+
* anchor: anchor,
|
543
|
+
* // If true, the default updater is overridden
|
544
|
+
* override: false,
|
545
|
+
* // Use custom builder
|
546
|
+
* builder: function(){},
|
547
|
+
* // Update the element self
|
548
|
+
* self: "This is my {data}",
|
549
|
+
* // Use css selector to update child element
|
550
|
+
* '.element.selector': "Using String Updater",
|
551
|
+
* // Use Function as updater
|
552
|
+
* '.element.selector': function(data, el){},
|
553
|
+
* // Updating Attributes
|
554
|
+
* '.element.selector': {
|
555
|
+
* attribute1: "{attribute}",
|
556
|
+
* className: "{className}",
|
557
|
+
* content: "This is the {content}",
|
558
|
+
* value: "This is the {value}"
|
559
|
+
* }
|
560
|
+
* });
|
561
|
+
*
|
562
|
+
* @example Using Default Updater
|
563
|
+
* $('<div><span class="name">Name</span></div>')
|
564
|
+
* .item({name: 'Steve Jobs'})
|
565
|
+
* .chain()
|
566
|
+
* .appendTo(document.body);
|
567
|
+
*
|
568
|
+
* @example Using Custom Updater
|
569
|
+
* $('<div><div class="name"><span class="first">First</span> <span class="last">Last</span></div></div>')
|
570
|
+
* .item({first:'Steve', last:'Jobs'})
|
571
|
+
* .chain({
|
572
|
+
* '.name .first': {
|
573
|
+
* style: 'color: blue;',
|
574
|
+
* content: 'First Name: {first}'
|
575
|
+
* },
|
576
|
+
* '.name .last': 'Family Name: {last}'
|
577
|
+
* })
|
578
|
+
* .appendTo(document.body);
|
579
|
+
*
|
580
|
+
* @example Attach Builder Inside Updater
|
581
|
+
* $('<div><div class="name">Name</div><div class="address">Address</div></div>')
|
582
|
+
* .item({name:'Steve Jobs', address:'Cupertino'})
|
583
|
+
* .chain({
|
584
|
+
* builder: function(){
|
585
|
+
* var data = this.item();
|
586
|
+
* this.find('.name').click(function(){alert(data.name)});
|
587
|
+
* this.find('.address').mouseout(function(){alert(data.address)});
|
588
|
+
* },
|
589
|
+
* '.name': '{name}',
|
590
|
+
* '.address': '{address}'
|
591
|
+
* })
|
592
|
+
* .appendTo(document.body);
|
593
|
+
*/
|
594
|
+
handleUpdater: function(rules)
|
595
|
+
{
|
596
|
+
// Extract Builder
|
597
|
+
var builder = rules.builder;
|
598
|
+
delete rules.builder;
|
599
|
+
|
600
|
+
// Extract Options
|
601
|
+
this.options = rules.options || {};
|
602
|
+
delete rules.options;
|
603
|
+
|
604
|
+
// Extract Anchor
|
605
|
+
if(rules.anchor)
|
606
|
+
{this.setAnchor(rules.anchor);}
|
607
|
+
delete rules.anchor;
|
608
|
+
|
609
|
+
// Extract Override
|
610
|
+
var override = rules.override;
|
611
|
+
delete rules.override;
|
612
|
+
|
613
|
+
for(var i in rules)
|
614
|
+
{
|
615
|
+
// Parse String to Function
|
616
|
+
if(typeof rules[i] == 'string')
|
617
|
+
{
|
618
|
+
rules[i] = $.Chain.parse(rules[i]);
|
619
|
+
}
|
620
|
+
// Parse Attributes Object to Functions
|
621
|
+
else if(typeof rules[i] == 'object')
|
622
|
+
{
|
623
|
+
for(var j in rules[i])
|
624
|
+
{
|
625
|
+
if(typeof rules[i][j] == 'string')
|
626
|
+
{
|
627
|
+
rules[i][j] = $.Chain.parse(rules[i][j]);
|
628
|
+
}
|
629
|
+
}
|
630
|
+
}
|
631
|
+
}
|
632
|
+
|
633
|
+
// Create Updater
|
634
|
+
var fn = function(event, data)
|
635
|
+
{
|
636
|
+
var el, val;
|
637
|
+
var self = $(this);
|
638
|
+
for(var i in rules)
|
639
|
+
{
|
640
|
+
// If self, update the element itself
|
641
|
+
if(i == 'self')
|
642
|
+
{el = self;}
|
643
|
+
// Otherwise find element inside self
|
644
|
+
else
|
645
|
+
{el = $(i, self);}
|
646
|
+
|
647
|
+
// Executing
|
648
|
+
// If no attributes, put the result to html (value if input)
|
649
|
+
if (typeof rules[i] == 'function')
|
650
|
+
{
|
651
|
+
val = rules[i].apply(self, [data, el]);
|
652
|
+
if(typeof val == 'string')
|
653
|
+
{el.not(':input').html(val).end().filter(':input').val(val);}
|
654
|
+
}
|
655
|
+
// If attributes, then execute the function for each attr.
|
656
|
+
else if(typeof rules[i] == 'object')
|
657
|
+
{
|
658
|
+
for(var j in rules[i])
|
659
|
+
{
|
660
|
+
if (typeof rules[i][j] == 'function')
|
661
|
+
{
|
662
|
+
val = rules[i][j].apply(self, [data, el]);
|
663
|
+
if(typeof val == 'string')
|
664
|
+
{
|
665
|
+
// Some special attributes
|
666
|
+
if(j == 'content')
|
667
|
+
{el.html(val);}
|
668
|
+
else if(j == 'text')
|
669
|
+
{el.text(val);}
|
670
|
+
else if(j == 'value')
|
671
|
+
{el.val(val);}
|
672
|
+
else if(j == 'class' || j == 'className')
|
673
|
+
{el.addClass(val);}
|
674
|
+
// Otherwise fill attribute as normal
|
675
|
+
else
|
676
|
+
{el.attr(j, val);}
|
677
|
+
}
|
678
|
+
|
679
|
+
}
|
680
|
+
}
|
681
|
+
}
|
682
|
+
}
|
683
|
+
};
|
684
|
+
|
685
|
+
var defBuilder = this.defaultBuilder;
|
686
|
+
|
687
|
+
// Define Builder
|
688
|
+
this.builder = function(root)
|
689
|
+
{
|
690
|
+
if(builder)
|
691
|
+
{builder.apply(this, [root]);}
|
692
|
+
|
693
|
+
if(!override)
|
694
|
+
{defBuilder.apply(this);}
|
695
|
+
|
696
|
+
// Here goes the updater
|
697
|
+
this.update(fn);
|
698
|
+
|
699
|
+
// This prevent infinite recursion
|
700
|
+
// see: jQuery.Chain.services.item.build
|
701
|
+
return false;
|
702
|
+
};
|
703
|
+
},
|
704
|
+
|
705
|
+
/**
|
706
|
+
* Builder Handler.
|
707
|
+
* If you pass a function to @chain@, it will be handled
|
708
|
+
* as @{builder: function}@, enabling you to use the default
|
709
|
+
* updater while customizing the events etc.
|
710
|
+
*
|
711
|
+
* @alias chain(fn)
|
712
|
+
* @alias jQuery.Chain.services.chain.handleBuilder
|
713
|
+
*
|
714
|
+
* @param {Function} fn Builder Function
|
715
|
+
* @param {Boolean} bool If true, it just use the builder provided. Not creating new Builder
|
716
|
+
*
|
717
|
+
* @example
|
718
|
+
* $('<div><div class="name">Name</div><div class="address">Address</div></div>')
|
719
|
+
* .item({name:'Steve Jobs', address:'Cupertino'})
|
720
|
+
* .chain(function(){
|
721
|
+
* this.bind('click', function(){
|
722
|
+
* var data = this.item();
|
723
|
+
* alert('name:'+data.name+', address:'+data.address);
|
724
|
+
* });
|
725
|
+
*
|
726
|
+
* // if you return false, default builder wont be executed
|
727
|
+
* // You don't have to return true;
|
728
|
+
* return true;
|
729
|
+
* })
|
730
|
+
* .appendTo(document.body);
|
731
|
+
*
|
732
|
+
* @see jQuery.Chain.services.chain.handleUpdater
|
733
|
+
* @see jQuery.Chain.services.chain.createBuilder
|
734
|
+
*/
|
735
|
+
handleBuilder: function(fn, bool)
|
736
|
+
{
|
737
|
+
if(bool)
|
738
|
+
{this.builder = fn;}
|
739
|
+
else
|
740
|
+
{this.builder = this.createBuilder(fn);}
|
741
|
+
},
|
742
|
+
|
743
|
+
|
744
|
+
/**
|
745
|
+
* Default Builder - Automatic Data filler
|
746
|
+
*
|
747
|
+
* @alias jQuery.Chain.services.chain.defaultBuilder
|
748
|
+
*
|
749
|
+
* @param {Function} builder Builder Function
|
750
|
+
* @param {Object} root Root Element Object
|
751
|
+
*
|
752
|
+
* @see jQuery.Chain.services.chain.createBuilder
|
753
|
+
*/
|
754
|
+
defaultBuilder: function(builder, root)
|
755
|
+
{
|
756
|
+
// Caution:
|
757
|
+
// @this@ is in this function @this.element@
|
758
|
+
|
759
|
+
// if builder return false, res will be false
|
760
|
+
// Otherwise true
|
761
|
+
// Using this, the default updater can be disabled
|
762
|
+
var res = builder ? (builder.apply(this, [root]) !== false) : true;
|
763
|
+
|
764
|
+
// Default Updater
|
765
|
+
if(res)
|
766
|
+
{
|
767
|
+
this.bind('update', function(event, data){
|
768
|
+
var self = $(this);
|
769
|
+
// Iterate through data
|
770
|
+
// Find element with the same class as data property
|
771
|
+
// Insert data depending of elemen type
|
772
|
+
for(var i in data)
|
773
|
+
{
|
774
|
+
if(typeof data[i] != 'object' && typeof data[i] != 'function')
|
775
|
+
{
|
776
|
+
// This prevents selector to select inside nested chain-element
|
777
|
+
// Important to support recursion & nested element
|
778
|
+
// NEED OPTIMIZATION
|
779
|
+
self.find('> .'+i+', *:not(.chain-element) .'+i)
|
780
|
+
.each(function(){
|
781
|
+
var match = $(this);
|
782
|
+
if(match.filter(':input').length)
|
783
|
+
{match.val(data[i]);}
|
784
|
+
else if(match.filter('img').length)
|
785
|
+
{match.attr('src', data[i]);}
|
786
|
+
else
|
787
|
+
{match.html(data[i]);}
|
788
|
+
});
|
789
|
+
}
|
790
|
+
}
|
791
|
+
});
|
792
|
+
}
|
793
|
+
},
|
794
|
+
|
795
|
+
/**
|
796
|
+
* Builder Generator (Wrapper).
|
797
|
+
*
|
798
|
+
* @alias jQuery.Chain.services.chain.createBuilder
|
799
|
+
*
|
800
|
+
* @param {Function} builder Builder
|
801
|
+
*
|
802
|
+
* @return {Function} Wrapped Builder
|
803
|
+
*
|
804
|
+
* @see jQuery.Chain.services.chain.defaultBuilder;
|
805
|
+
*/
|
806
|
+
createBuilder: function(builder)
|
807
|
+
{
|
808
|
+
var defBuilder = this.defaultBuilder;
|
809
|
+
return function(root){
|
810
|
+
defBuilder.apply(this, [builder, root]);
|
811
|
+
return false;
|
812
|
+
};
|
813
|
+
},
|
814
|
+
|
815
|
+
/**
|
816
|
+
* Set Anchor (Container for @items@ to be populated, default: @this.element@)
|
817
|
+
*
|
818
|
+
* @alias jQuery.Chain.services.chain.setAnchor
|
819
|
+
*
|
820
|
+
* @param {Object} anchor Anchor element
|
821
|
+
*
|
822
|
+
* @see jQuery.Chain.services.chain.$anchor
|
823
|
+
*/
|
824
|
+
setAnchor: function(anchor)
|
825
|
+
{
|
826
|
+
this.anchor.html(this.template);
|
827
|
+
this.anchor = anchor == this.element ? anchor : this.element.find(anchor).eq(0);
|
828
|
+
this.template = this.anchor.html();
|
829
|
+
this.anchor.empty();
|
830
|
+
},
|
831
|
+
|
832
|
+
/**
|
833
|
+
* Set new Anchor and rerender if new anchor passed.
|
834
|
+
* Otherwise return current anchor.
|
835
|
+
*
|
836
|
+
* If you use @items()@ with @chain()@,
|
837
|
+
* you can use @chain('anchor', selector)@ to move the element,
|
838
|
+
* where the items will be generated.
|
839
|
+
*
|
840
|
+
* @alias chain('anchor')
|
841
|
+
* @alias jQuery.Chain.services.chain.$anchor
|
842
|
+
*
|
843
|
+
* @param {Object} anchor Anchor element or selector
|
844
|
+
*
|
845
|
+
* @return {Object} current element (if new Anchor passed), otherwise current anchor
|
846
|
+
*
|
847
|
+
* @example
|
848
|
+
* $('#persons').chain('anchor', '.wrapper');
|
849
|
+
*
|
850
|
+
* // Define Anchor directly while building
|
851
|
+
* $('#persons').items([...]).chain({anchor:'.wrapper', builder: ...});
|
852
|
+
*/
|
853
|
+
$anchor: function(anchor)
|
854
|
+
{
|
855
|
+
if(anchor)
|
856
|
+
{
|
857
|
+
this.element.items('backup');
|
858
|
+
this.element.item('backup');
|
859
|
+
|
860
|
+
this.setAnchor(anchor);
|
861
|
+
this.element.update();
|
862
|
+
|
863
|
+
return this.element;
|
864
|
+
}
|
865
|
+
else
|
866
|
+
{
|
867
|
+
return this.anchor;
|
868
|
+
}
|
869
|
+
},
|
870
|
+
|
871
|
+
/**
|
872
|
+
* Getting/Switching Template.
|
873
|
+
*
|
874
|
+
* @alias chain('template')
|
875
|
+
* @alias jQuery.Chain.services.chain.$template
|
876
|
+
*
|
877
|
+
* @param {Number, String} arg Argument
|
878
|
+
*
|
879
|
+
* @return {Object} jQuery Object
|
880
|
+
*
|
881
|
+
* @example
|
882
|
+
* $(selector).chain('template') // Returns current Template (jQuery Object)
|
883
|
+
* $(selector).chain('template', 'raw') // Returns raw HTML Templates (all)
|
884
|
+
* $(selector).chain('template', nr) // Switch to template nr (read: Number)
|
885
|
+
* $(selector).chain('template', '.tree-column') // Switch by selector
|
886
|
+
*/
|
887
|
+
$template: function(arg)
|
888
|
+
{
|
889
|
+
// Returns current Template (jQuery Object)
|
890
|
+
if(!arguments.length)
|
891
|
+
{return $('<div>').html(this.template).children().eq(this.tplNumber);}
|
892
|
+
|
893
|
+
// Returns raw HTML Template
|
894
|
+
if(arg == 'raw')
|
895
|
+
{return this.template;}
|
896
|
+
|
897
|
+
// Switch template by Number
|
898
|
+
if(typeof arg == 'number')
|
899
|
+
{
|
900
|
+
this.tplNumber = arg;
|
901
|
+
}
|
902
|
+
// Switch template by selector
|
903
|
+
else
|
904
|
+
{
|
905
|
+
var tpl = $('<div>').html(this.template).children();
|
906
|
+
var node = tpl.filter(arg).eq(0);
|
907
|
+
|
908
|
+
if(node.length)
|
909
|
+
{this.tplNumber = tpl.index(node);}
|
910
|
+
else
|
911
|
+
{return this.element;} // If not found do nothing
|
912
|
+
}
|
913
|
+
|
914
|
+
this.element.items('backup');
|
915
|
+
this.element.item('backup');
|
916
|
+
this.element.update();
|
917
|
+
|
918
|
+
return this.element;
|
919
|
+
},
|
920
|
+
|
921
|
+
/**
|
922
|
+
* Get/Change Builder.
|
923
|
+
* If you don't pass any argument, it will return the created builder.
|
924
|
+
*
|
925
|
+
* @alias chain('builder')
|
926
|
+
* @alias jQuery.Chain.services.chain.$builder
|
927
|
+
*
|
928
|
+
* @param {Function, Object} builder (Optional)
|
929
|
+
*
|
930
|
+
* @return {Function, Object} returns builder function, or jQuery Object depends on arg
|
931
|
+
*
|
932
|
+
* @example
|
933
|
+
* $('#el').chain('builder') // returns builder function
|
934
|
+
* $('#el').chain('builder', newBuilder) // Replace Builder
|
935
|
+
*/
|
936
|
+
$builder: function(builder)
|
937
|
+
{
|
938
|
+
if(builder)
|
939
|
+
{return this.handler(builder);}
|
940
|
+
else
|
941
|
+
{return this.builder;}
|
942
|
+
},
|
943
|
+
|
944
|
+
/**
|
945
|
+
* Check status
|
946
|
+
*
|
947
|
+
* @alias chain('active')
|
948
|
+
* @alias jQuery.Chain.services.chain.$active
|
949
|
+
*
|
950
|
+
* @return {Boolean} true if active
|
951
|
+
*/
|
952
|
+
$active: function()
|
953
|
+
{
|
954
|
+
return this.isActive;
|
955
|
+
},
|
956
|
+
|
957
|
+
/**
|
958
|
+
* Set/Get options
|
959
|
+
*
|
960
|
+
* @alias chain('options')
|
961
|
+
* @alias jQuery.Chain.services.chain.$options
|
962
|
+
*
|
963
|
+
* @param {String} opt Option name
|
964
|
+
* @param {Anything} val Option value
|
965
|
+
*
|
966
|
+
* @return {Object} if no value given, it returns the value, otherwise the element itself
|
967
|
+
*/
|
968
|
+
|
969
|
+
$options: function(opt, val)
|
970
|
+
{
|
971
|
+
this.options = this.options || {};
|
972
|
+
|
973
|
+
if(arguments.length == 2)
|
974
|
+
{
|
975
|
+
this.options[opt] = val;
|
976
|
+
return this.element;
|
977
|
+
}
|
978
|
+
|
979
|
+
else
|
980
|
+
{
|
981
|
+
return this.options[opt];
|
982
|
+
}
|
983
|
+
},
|
984
|
+
|
985
|
+
/**
|
986
|
+
* Add/Remove Plugins that extend builder
|
987
|
+
*
|
988
|
+
* @alias chain('plugin')
|
989
|
+
* @alias jQuery.Chain.services.chain.$plugin
|
990
|
+
*
|
991
|
+
* @param {String} name Plugin Name
|
992
|
+
* @param {Function, Boolean} fn Plugin Function / False to remove
|
993
|
+
*
|
994
|
+
* @return {Object} jQuery Object
|
995
|
+
*/
|
996
|
+
$plugin: function(name, fn)
|
997
|
+
{
|
998
|
+
if(fn === null)
|
999
|
+
{delete this.plugins[name];}
|
1000
|
+
else if(typeof fn == 'function')
|
1001
|
+
{this.plugins[name] = fn;}
|
1002
|
+
else if(name && !fn)
|
1003
|
+
{return this.plugins[name];}
|
1004
|
+
else
|
1005
|
+
{return this.plugins;}
|
1006
|
+
|
1007
|
+
if(typeof fn == 'function')
|
1008
|
+
{
|
1009
|
+
this.element.items(true).each(function(){
|
1010
|
+
var self = $(this);
|
1011
|
+
fn.call(self, self.item('root'));
|
1012
|
+
});
|
1013
|
+
}
|
1014
|
+
|
1015
|
+
this.element.update();
|
1016
|
+
|
1017
|
+
return this.element;
|
1018
|
+
},
|
1019
|
+
|
1020
|
+
/**
|
1021
|
+
* Clone Element unchained, with ID removed.
|
1022
|
+
*
|
1023
|
+
* @alias chain('clone')
|
1024
|
+
* @alias jQuery.Chain.services.chain.$clone
|
1025
|
+
*
|
1026
|
+
* @return {Object} jQuery Object containing cloned Element
|
1027
|
+
*/
|
1028
|
+
$clone: function()
|
1029
|
+
{
|
1030
|
+
var id = this.element.attr('id');
|
1031
|
+
this.element.attr('id', '');
|
1032
|
+
|
1033
|
+
var clone = this.element.clone().empty().html(this.template);
|
1034
|
+
this.element.attr('id', id);
|
1035
|
+
|
1036
|
+
return clone;
|
1037
|
+
},
|
1038
|
+
|
1039
|
+
/**
|
1040
|
+
* Destroy Chain, restore Element to previous condition.
|
1041
|
+
*
|
1042
|
+
* @alias chain('destroy')
|
1043
|
+
* @alias jQuery.Chain.services.chain.$destroy
|
1044
|
+
*
|
1045
|
+
* @param {Boolean} nofollow If true, it won't destroy nested chain elements
|
1046
|
+
*
|
1047
|
+
* @return {Object} jQuery Object
|
1048
|
+
*/
|
1049
|
+
$destroy: function(nofollow)
|
1050
|
+
{
|
1051
|
+
this.element.removeClass('chain-element');
|
1052
|
+
|
1053
|
+
if(!nofollow)
|
1054
|
+
{
|
1055
|
+
// Backup to buffer
|
1056
|
+
this.element.items('backup');
|
1057
|
+
this.element.item('backup');
|
1058
|
+
|
1059
|
+
// Destroy nested elements
|
1060
|
+
this.element.find('.chain-element').each(function(){
|
1061
|
+
$(this).chain('destroy', true);
|
1062
|
+
});
|
1063
|
+
}
|
1064
|
+
|
1065
|
+
// Trigger destroy event
|
1066
|
+
this.element.triggerHandler('destroy');
|
1067
|
+
|
1068
|
+
this.isActive = false;
|
1069
|
+
|
1070
|
+
// Restore HTML
|
1071
|
+
this.anchor.html(this.template);
|
1072
|
+
|
1073
|
+
return this.element;
|
1074
|
+
}
|
1075
|
+
});
|
1076
|
+
|
1077
|
+
})(jQuery);
|
1078
|
+
|
1079
|
+
/* item.js */
|
1080
|
+
/**
|
1081
|
+
* Chain Item Service.
|
1082
|
+
* Method to bind item to object.
|
1083
|
+
*
|
1084
|
+
* @alias item
|
1085
|
+
*
|
1086
|
+
* @syntax $(selector).item(parameters);
|
1087
|
+
*/
|
1088
|
+
|
1089
|
+
(function($){
|
1090
|
+
|
1091
|
+
/**
|
1092
|
+
* Chain Item Manager - Providing methods of @item@.
|
1093
|
+
* All method listed here can only be used internally
|
1094
|
+
* using @jQuery.Chain.service@ or @jQuery.Chain.extend@
|
1095
|
+
*
|
1096
|
+
* @namespace
|
1097
|
+
*
|
1098
|
+
* @alias jQuery.Chain.services.item
|
1099
|
+
*
|
1100
|
+
* @see jQuery.Chain.service
|
1101
|
+
* @see jQuery.Chain.extend
|
1102
|
+
*/
|
1103
|
+
|
1104
|
+
$.Chain.service('item', {
|
1105
|
+
/**
|
1106
|
+
* Initializer. Executed once at the first time @item@ invoked.
|
1107
|
+
*
|
1108
|
+
* @alias jQuery.Chain.services.item.init
|
1109
|
+
*
|
1110
|
+
* @see jQuery.Chain.service
|
1111
|
+
*/
|
1112
|
+
init: function()
|
1113
|
+
{
|
1114
|
+
this.isActive = false;
|
1115
|
+
this.isBuilt = false;
|
1116
|
+
this.root = this.element;
|
1117
|
+
this.data = false;
|
1118
|
+
this.datafn = this.dataHandler;
|
1119
|
+
},
|
1120
|
+
|
1121
|
+
/**
|
1122
|
+
* Default handler.
|
1123
|
+
*
|
1124
|
+
* @alias jQuery.Chain.services.item.handler
|
1125
|
+
*
|
1126
|
+
* @param {Object} obj Object to be handled
|
1127
|
+
*
|
1128
|
+
* @return {Object} jQuery Object
|
1129
|
+
*
|
1130
|
+
* @see jQuery.Chain.service
|
1131
|
+
* @see jQuery.Chain.services.item.handleObject
|
1132
|
+
* @see jQuery.Chain.services.item.handleFunction
|
1133
|
+
* @see jQuery.Chain.services.item.handleDefault
|
1134
|
+
*/
|
1135
|
+
handler: function(obj)
|
1136
|
+
{
|
1137
|
+
if(typeof obj == 'object')
|
1138
|
+
{return this.handleObject(obj);}
|
1139
|
+
else if(typeof obj == 'function')
|
1140
|
+
{return this.handleFunction(obj);}
|
1141
|
+
else
|
1142
|
+
{return this.handleDefault();}
|
1143
|
+
},
|
1144
|
+
|
1145
|
+
/**
|
1146
|
+
* Edit/Bind Item.
|
1147
|
+
* If no Object defined, it will bind the object to the Item, otherwise
|
1148
|
+
* it will alter the object using the provided object.
|
1149
|
+
*
|
1150
|
+
* @alias item(object)
|
1151
|
+
* @alias jQuery.Chain.services.item.handleObject
|
1152
|
+
*
|
1153
|
+
* @param {Object} obj Object to be inserted
|
1154
|
+
*
|
1155
|
+
* @return {Object} jQuery Object
|
1156
|
+
*
|
1157
|
+
* @example
|
1158
|
+
* $('#element').item({name:'Rizqi', country:'Germany'});
|
1159
|
+
* $('#element').item({country:'Indonesia'});
|
1160
|
+
* $('#element').item(); // Returns {name:'Rizqi', country:'Indonesia'}
|
1161
|
+
*
|
1162
|
+
* @see jQuery.Chain.services.item.handler
|
1163
|
+
*/
|
1164
|
+
handleObject: function(obj)
|
1165
|
+
{
|
1166
|
+
this.setData(obj);
|
1167
|
+
this.isActive = true;
|
1168
|
+
this.update();
|
1169
|
+
|
1170
|
+
return this.element;
|
1171
|
+
},
|
1172
|
+
|
1173
|
+
/**
|
1174
|
+
* Add setter and getter to item.
|
1175
|
+
* This function will change the way @item(object)@ and @item()@ works.
|
1176
|
+
*
|
1177
|
+
* @alias item(fn)
|
1178
|
+
* @alias jQuery.Chain.services.item.handleFunction
|
1179
|
+
*
|
1180
|
+
* @param {Function} fn Getter&Setter Function
|
1181
|
+
*
|
1182
|
+
* @return {Object} jQuery Object
|
1183
|
+
*
|
1184
|
+
* @example
|
1185
|
+
* $(element).item(function(oldval, newval){
|
1186
|
+
* //setter
|
1187
|
+
* if(newval)
|
1188
|
+
* return $.extend(oldval, newval);
|
1189
|
+
* //getter
|
1190
|
+
* else
|
1191
|
+
* return oldval;
|
1192
|
+
* })
|
1193
|
+
*/
|
1194
|
+
handleFunction: function(fn)
|
1195
|
+
{
|
1196
|
+
// datafn stores the getter/setter function
|
1197
|
+
this.datafn = fn;
|
1198
|
+
|
1199
|
+
return this.element;
|
1200
|
+
},
|
1201
|
+
|
1202
|
+
/**
|
1203
|
+
* Get Data if no argument passed.
|
1204
|
+
*
|
1205
|
+
* @alias item()
|
1206
|
+
* @alias jQuery.Chain.services.item.handleDefault
|
1207
|
+
*
|
1208
|
+
* @return {Object, Boolean} Returns Data Object if exist, otherwise false
|
1209
|
+
*/
|
1210
|
+
handleDefault: function()
|
1211
|
+
{
|
1212
|
+
if(this.isActive)
|
1213
|
+
{return this.getData();}
|
1214
|
+
else
|
1215
|
+
{return false;}
|
1216
|
+
},
|
1217
|
+
|
1218
|
+
/**
|
1219
|
+
* Data Getter Wrapper Function
|
1220
|
+
*
|
1221
|
+
* @alias jQuery.Chain.services.item.getData
|
1222
|
+
*
|
1223
|
+
* @return {Object} data
|
1224
|
+
*/
|
1225
|
+
getData: function()
|
1226
|
+
{
|
1227
|
+
// Call Getter
|
1228
|
+
this.data = this.datafn.call(this.element, this.data);
|
1229
|
+
|
1230
|
+
return this.data;
|
1231
|
+
},
|
1232
|
+
|
1233
|
+
/**
|
1234
|
+
* Data Setter Wrapper Function
|
1235
|
+
*
|
1236
|
+
* @alias jQuery.Chain.services.item.setData
|
1237
|
+
*/
|
1238
|
+
setData: function(obj)
|
1239
|
+
{
|
1240
|
+
var data;
|
1241
|
+
|
1242
|
+
// Determine whether object is a jQuery object or a data object
|
1243
|
+
if($.Chain.jobject(obj) && obj.item())
|
1244
|
+
{data = $.extend({}, obj.item());}
|
1245
|
+
else if($.Chain.jobject(obj))
|
1246
|
+
{data = {};}
|
1247
|
+
else
|
1248
|
+
{data = obj;}
|
1249
|
+
|
1250
|
+
// Call Setter
|
1251
|
+
this.data = this.datafn.call(this.element, this.data || data, data);
|
1252
|
+
|
1253
|
+
// Handle Linked Element
|
1254
|
+
if(this.linkElement && this.linkElement[0] != obj[0])
|
1255
|
+
{
|
1256
|
+
var el = this.linkFunction();
|
1257
|
+
if($.Chain.jobject(el) && el.length && el.item())
|
1258
|
+
{el.item(this.data);}
|
1259
|
+
}
|
1260
|
+
},
|
1261
|
+
|
1262
|
+
/**
|
1263
|
+
* Default Getter/Setter
|
1264
|
+
*
|
1265
|
+
* @alias jQuery.Chain.services.item.dataHandler
|
1266
|
+
*
|
1267
|
+
* @param {Object} oldval Old value
|
1268
|
+
* @param {Object} newval New Value
|
1269
|
+
*
|
1270
|
+
* @return {Object} returns data value
|
1271
|
+
*/
|
1272
|
+
dataHandler: function(oldval, newval)
|
1273
|
+
{
|
1274
|
+
if(arguments.length == 2)
|
1275
|
+
{return $.extend(oldval, newval);}
|
1276
|
+
else
|
1277
|
+
{return oldval;}
|
1278
|
+
},
|
1279
|
+
|
1280
|
+
/**
|
1281
|
+
* Update element. Wrapper for @jQuery.Chain.services.item.element.update@
|
1282
|
+
*
|
1283
|
+
* @alias jQuery.Chain.services.item.update
|
1284
|
+
*
|
1285
|
+
* @return {Object} jQuery Object
|
1286
|
+
*/
|
1287
|
+
update: function()
|
1288
|
+
{
|
1289
|
+
return this.element.update();
|
1290
|
+
},
|
1291
|
+
|
1292
|
+
/**
|
1293
|
+
* Build item, apply builder and plugins
|
1294
|
+
*
|
1295
|
+
* @alias jQuery.Chain.services.item.build
|
1296
|
+
*
|
1297
|
+
* @see jQuery.Chain.services.item.$update
|
1298
|
+
*/
|
1299
|
+
build: function()
|
1300
|
+
{
|
1301
|
+
// IE Fix
|
1302
|
+
var fix = this.element.chain('template', 'raw').replace(/jQuery\d+\=\"null\"/gi, "");
|
1303
|
+
this.element.chain('anchor').html(fix);
|
1304
|
+
|
1305
|
+
// If item has root (items)
|
1306
|
+
if(!$.Chain.jidentic(this.root, this.element))
|
1307
|
+
{
|
1308
|
+
// Get plugin from root and apply them
|
1309
|
+
var plugins = this.root.chain('plugin');
|
1310
|
+
for(var i in plugins)
|
1311
|
+
{
|
1312
|
+
plugins[i].apply(this.element, [this.root]);
|
1313
|
+
}
|
1314
|
+
|
1315
|
+
}
|
1316
|
+
|
1317
|
+
// Apply builder
|
1318
|
+
this.element.chain('builder').apply(this.element, [this.root]);
|
1319
|
+
this.isBuilt = true;
|
1320
|
+
},
|
1321
|
+
|
1322
|
+
/**
|
1323
|
+
* Item Updater, called within @$(element).update()@
|
1324
|
+
*
|
1325
|
+
* @alias item('update')
|
1326
|
+
* @alias jQuery.Chain.services.item.$update
|
1327
|
+
*
|
1328
|
+
* @return {Object} jQuery Object
|
1329
|
+
*/
|
1330
|
+
$update: function()
|
1331
|
+
{
|
1332
|
+
if(this.element.chain('active') && this.isActive && !this.isBuilt && this.getData())
|
1333
|
+
{this.build();}
|
1334
|
+
|
1335
|
+
return this.element;
|
1336
|
+
},
|
1337
|
+
|
1338
|
+
/**
|
1339
|
+
* Replace Data with new data
|
1340
|
+
*
|
1341
|
+
* @alias item('replace')
|
1342
|
+
* @alias jQuery.Chain.services.item.$replace
|
1343
|
+
*
|
1344
|
+
* @param {Object} obj Data Object
|
1345
|
+
*
|
1346
|
+
* @return {Object} jQuery Object
|
1347
|
+
*
|
1348
|
+
* @example
|
1349
|
+
* $(element).item('replace', data);
|
1350
|
+
*/
|
1351
|
+
$replace: function(obj)
|
1352
|
+
{
|
1353
|
+
this.data = {};
|
1354
|
+
this.setData(obj);
|
1355
|
+
this.isActive = true;
|
1356
|
+
this.update();
|
1357
|
+
return this.element;
|
1358
|
+
},
|
1359
|
+
|
1360
|
+
/**
|
1361
|
+
* Remove Item And destroy it.
|
1362
|
+
*
|
1363
|
+
* @alias item('remove')
|
1364
|
+
* @alias jQuery.Chain.services.item.$remove
|
1365
|
+
*
|
1366
|
+
* @param {Boolean} noupdate If true it won't update the root element
|
1367
|
+
*/
|
1368
|
+
$remove: function(noupdate)
|
1369
|
+
{
|
1370
|
+
// Destroy And Remove
|
1371
|
+
this.element.chain('destroy');
|
1372
|
+
this.element.remove();
|
1373
|
+
this.element.item('link', null);
|
1374
|
+
this.element.item('destroy');
|
1375
|
+
|
1376
|
+
// Update root under certain circumtances
|
1377
|
+
if(!$.Chain.jidentic(this.root, this.element) && !noupdate)
|
1378
|
+
{this.root.update();}
|
1379
|
+
},
|
1380
|
+
|
1381
|
+
/**
|
1382
|
+
* Check Status of @item@
|
1383
|
+
*
|
1384
|
+
* @alias item('active')
|
1385
|
+
* @alias jQuery.Chain.services.item.$active
|
1386
|
+
*
|
1387
|
+
* @return {Boolean} Status
|
1388
|
+
*/
|
1389
|
+
$active: function()
|
1390
|
+
{
|
1391
|
+
return this.isActive;
|
1392
|
+
},
|
1393
|
+
|
1394
|
+
/**
|
1395
|
+
* Get/Set Root element.
|
1396
|
+
*
|
1397
|
+
* @alias item('root');
|
1398
|
+
* @alias jQuery.Chain.services.item.$root
|
1399
|
+
*
|
1400
|
+
* @param {Object} root New Root element
|
1401
|
+
*
|
1402
|
+
* @return {Object} If a new root passed, it will be item Element. Otherwise current root.
|
1403
|
+
*/
|
1404
|
+
$root: function(root)
|
1405
|
+
{
|
1406
|
+
if(arguments.length)
|
1407
|
+
{
|
1408
|
+
this.root = root;
|
1409
|
+
this.update();
|
1410
|
+
return this.element;
|
1411
|
+
}
|
1412
|
+
else
|
1413
|
+
{
|
1414
|
+
return this.root;
|
1415
|
+
}
|
1416
|
+
},
|
1417
|
+
|
1418
|
+
/**
|
1419
|
+
* Backup Item to the state before being built.
|
1420
|
+
*
|
1421
|
+
* @alias item('backup')
|
1422
|
+
* @alias jQuery.Chain.services.item.$backup
|
1423
|
+
*
|
1424
|
+
* @return {Object} jQuery Object
|
1425
|
+
*/
|
1426
|
+
$backup: function()
|
1427
|
+
{
|
1428
|
+
this.isBuilt = false;
|
1429
|
+
|
1430
|
+
return this.element;
|
1431
|
+
},
|
1432
|
+
|
1433
|
+
/**
|
1434
|
+
* Bind Item to other (chained) element. If one of them is updated,
|
1435
|
+
* the linked element will be updated.
|
1436
|
+
*
|
1437
|
+
* @alias item('link')
|
1438
|
+
* @alias jQuery.Chain.services.item.$link
|
1439
|
+
*
|
1440
|
+
* @param {Object} element element/selector to be linked with
|
1441
|
+
* @param {String} collection Collection to be linked with (has to be @"self"@ if linked to item)
|
1442
|
+
*
|
1443
|
+
* @return {Object} jQuery Element
|
1444
|
+
*
|
1445
|
+
* @see jQuery.Chain.services.items.collection
|
1446
|
+
*/
|
1447
|
+
$link: function(element, collection)
|
1448
|
+
{
|
1449
|
+
// If there are previous linkElement
|
1450
|
+
if(this.linkElement)
|
1451
|
+
{
|
1452
|
+
this.linkElement.unbind('update', this.linkUpdater);
|
1453
|
+
this.linkElement = null;
|
1454
|
+
}
|
1455
|
+
|
1456
|
+
element = $(element);
|
1457
|
+
if(element.length)
|
1458
|
+
{
|
1459
|
+
var self = this;
|
1460
|
+
this.isActive = true;
|
1461
|
+
this.linkElement = element;
|
1462
|
+
// Function that get the linked item.
|
1463
|
+
this.linkFunction = function()
|
1464
|
+
{
|
1465
|
+
if(typeof collection == 'function')
|
1466
|
+
{
|
1467
|
+
try{
|
1468
|
+
return collection.call(self.element, self.linkElement);
|
1469
|
+
}catch(e){
|
1470
|
+
return $().eq(-1);
|
1471
|
+
}
|
1472
|
+
}
|
1473
|
+
else if(typeof collection == 'string')
|
1474
|
+
{
|
1475
|
+
return self.linkElement.items('collection', collection);
|
1476
|
+
}
|
1477
|
+
else
|
1478
|
+
{
|
1479
|
+
return $().eq(-1);
|
1480
|
+
}
|
1481
|
+
};
|
1482
|
+
|
1483
|
+
// Watch linked element for update, and trigger update in self
|
1484
|
+
this.linkUpdater = function()
|
1485
|
+
{
|
1486
|
+
var res = self.linkFunction();
|
1487
|
+
if(res && res.length)
|
1488
|
+
{self.element.item(res);}
|
1489
|
+
};
|
1490
|
+
|
1491
|
+
this.linkElement.bind('update', this.linkUpdater);
|
1492
|
+
this.linkUpdater();
|
1493
|
+
}
|
1494
|
+
|
1495
|
+
return this.element;
|
1496
|
+
},
|
1497
|
+
|
1498
|
+
/**
|
1499
|
+
* Destroy item service.
|
1500
|
+
*
|
1501
|
+
* @alias item('destroy')
|
1502
|
+
* @alias jQuery.Chain.services.item.$destroy
|
1503
|
+
*
|
1504
|
+
* @return {Object} jQuery Element
|
1505
|
+
*/
|
1506
|
+
$destroy: function()
|
1507
|
+
{
|
1508
|
+
return this.element;
|
1509
|
+
}
|
1510
|
+
});
|
1511
|
+
|
1512
|
+
})(jQuery);
|
1513
|
+
|
1514
|
+
/* items.js */
|
1515
|
+
/**
|
1516
|
+
* Chain Items Service.
|
1517
|
+
* Method to bind items to object.
|
1518
|
+
*
|
1519
|
+
* @alias items
|
1520
|
+
*
|
1521
|
+
* @syntax $(selector).items(parameters);
|
1522
|
+
*/
|
1523
|
+
|
1524
|
+
(function($){
|
1525
|
+
|
1526
|
+
/**
|
1527
|
+
* Chain Items Manager - Providing methods of @items@.
|
1528
|
+
* All method listed here can only be used internally
|
1529
|
+
* using @jQuery.Chain.service@ or @jQuery.Chain.extend@
|
1530
|
+
*
|
1531
|
+
* @namespace
|
1532
|
+
*
|
1533
|
+
* @alias jQuery.Chain.services.items
|
1534
|
+
*
|
1535
|
+
* @see jQuery.Chain.service
|
1536
|
+
* @see jQuery.Chain.extend
|
1537
|
+
*/
|
1538
|
+
|
1539
|
+
$.Chain.service('items', {
|
1540
|
+
/**
|
1541
|
+
* Collection of Function for getting items
|
1542
|
+
*
|
1543
|
+
* @namespace
|
1544
|
+
* @alias jQuery.Chain.services.items.collections
|
1545
|
+
*
|
1546
|
+
* @see jQuery.Chain.services.items.collection
|
1547
|
+
*/
|
1548
|
+
collections:
|
1549
|
+
{
|
1550
|
+
/**
|
1551
|
+
* Get all items, including hidden
|
1552
|
+
*
|
1553
|
+
* @alias jQuery.Chain.services.items.collections.all
|
1554
|
+
*
|
1555
|
+
* @return {Object} jQuery Object containing items
|
1556
|
+
*/
|
1557
|
+
all: function()
|
1558
|
+
{
|
1559
|
+
return this.element.chain('anchor').children('.chain-item');
|
1560
|
+
},
|
1561
|
+
|
1562
|
+
/**
|
1563
|
+
* Get all visible items
|
1564
|
+
*
|
1565
|
+
* @alias jQuery.Chain.services.items.collections.visible
|
1566
|
+
*
|
1567
|
+
* @return {Object} jQuery Object containing items
|
1568
|
+
*/
|
1569
|
+
visible: function()
|
1570
|
+
{
|
1571
|
+
return this.element.chain('anchor').children('.chain-item:visible');
|
1572
|
+
},
|
1573
|
+
|
1574
|
+
/**
|
1575
|
+
* Get all hidden items
|
1576
|
+
*
|
1577
|
+
* @alias jQuery.Chain.services.items.collections.hidden
|
1578
|
+
*
|
1579
|
+
* @return {Object} jQuery Object containing items
|
1580
|
+
*/
|
1581
|
+
hidden: function()
|
1582
|
+
{
|
1583
|
+
return this.element.chain('anchor').children('.chain-item:hidden');
|
1584
|
+
},
|
1585
|
+
|
1586
|
+
/**
|
1587
|
+
* Get self
|
1588
|
+
*
|
1589
|
+
* @alias jQuery.Chain.services.items.collections.self
|
1590
|
+
*
|
1591
|
+
* @return {Object} jQuery Object of the element
|
1592
|
+
*/
|
1593
|
+
self: function()
|
1594
|
+
{
|
1595
|
+
return this.element;
|
1596
|
+
}
|
1597
|
+
},
|
1598
|
+
|
1599
|
+
/**
|
1600
|
+
* Initializer. Executed once at the first time @items@ invoked.
|
1601
|
+
*
|
1602
|
+
* @alias jQuery.Chain.services.items.init
|
1603
|
+
*
|
1604
|
+
* @see jQuery.Chain.service
|
1605
|
+
*/
|
1606
|
+
init: function()
|
1607
|
+
{
|
1608
|
+
this.isActive = false;
|
1609
|
+
this.pushBuffer = [];
|
1610
|
+
this.shiftBuffer = [];
|
1611
|
+
this.collections = $.extend({}, this.collections);
|
1612
|
+
},
|
1613
|
+
|
1614
|
+
/**
|
1615
|
+
* Default handler.
|
1616
|
+
*
|
1617
|
+
* @alias jQuery.Chain.services.items.handler
|
1618
|
+
*
|
1619
|
+
* @param {Object} obj Object to be handled
|
1620
|
+
*
|
1621
|
+
* @return {Object} jQuery Object
|
1622
|
+
*
|
1623
|
+
* @see jQuery.Chain.service
|
1624
|
+
* @see jQuery.Chain.services.items.handleObject
|
1625
|
+
* @see jQuery.Chain.services.items.handleElement
|
1626
|
+
* @see jQuery.Chain.services.items.handleArray
|
1627
|
+
* @see jQuery.Chain.services.items.handleNumber
|
1628
|
+
* @see jQuery.Chain.services.items.handleTrue
|
1629
|
+
* @see jQuery.Chain.services.items.handleDefault
|
1630
|
+
*/
|
1631
|
+
handler: function(obj)
|
1632
|
+
{
|
1633
|
+
// Array
|
1634
|
+
if(obj instanceof Array)
|
1635
|
+
{return this.handleArray(obj);}
|
1636
|
+
// Inactive
|
1637
|
+
else if(!this.isActive)
|
1638
|
+
{return $().eq(-1);}
|
1639
|
+
// jQuery Object
|
1640
|
+
else if($.Chain.jobject(obj))
|
1641
|
+
{return this.handleElement(obj);}
|
1642
|
+
// Normal Object
|
1643
|
+
else if(typeof obj == 'object')
|
1644
|
+
{return this.handleObject(obj);}
|
1645
|
+
// Number
|
1646
|
+
else if(typeof obj == 'number')
|
1647
|
+
{return this.handleNumber(obj);}
|
1648
|
+
// True
|
1649
|
+
else if(obj === true)
|
1650
|
+
{return this.handleTrue();}
|
1651
|
+
// Default
|
1652
|
+
else
|
1653
|
+
{return this.handleDefault();}
|
1654
|
+
},
|
1655
|
+
|
1656
|
+
/**
|
1657
|
+
* If a Data Object is given, it will return the item element
|
1658
|
+
* containing the object if it exists, otherwise empty.
|
1659
|
+
*
|
1660
|
+
* @alias items(object)
|
1661
|
+
* @alias jQuery.Chain.services.items.handleObject
|
1662
|
+
*
|
1663
|
+
* @param {Object} obj Data Object
|
1664
|
+
*
|
1665
|
+
* @return {Object} jQuery Object
|
1666
|
+
*/
|
1667
|
+
handleObject: function(obj)
|
1668
|
+
{
|
1669
|
+
// Get Element By Data
|
1670
|
+
return this.collection('all').filter(function(){return $(this).item() == obj;});
|
1671
|
+
},
|
1672
|
+
|
1673
|
+
/**
|
1674
|
+
* If a jQuery Element is given, it will return itself if it is part of the items,
|
1675
|
+
* otherwise empty jQuery object.
|
1676
|
+
*
|
1677
|
+
* @alias items(element)
|
1678
|
+
* @alias jQuery.Chain.services.items.handleElement
|
1679
|
+
*
|
1680
|
+
* @param {Object} obj jQuery Object
|
1681
|
+
*
|
1682
|
+
* @return {Object} jQuery Object
|
1683
|
+
*/
|
1684
|
+
handleElement: function(obj)
|
1685
|
+
{
|
1686
|
+
// Check element whether it is part of items or not.
|
1687
|
+
if(!$.Chain.jidentic(obj, obj.item('root')) && $.Chain.jidentic(this.element, obj.item('root')))
|
1688
|
+
{return obj;}
|
1689
|
+
else
|
1690
|
+
{return $().eq(-1);}
|
1691
|
+
},
|
1692
|
+
|
1693
|
+
/**
|
1694
|
+
* If array is given, it will merge it to current items
|
1695
|
+
*
|
1696
|
+
* @alias items(array)
|
1697
|
+
* @alias jQuery.Chain.services.items.handleArray
|
1698
|
+
*
|
1699
|
+
* @param {Array} array Array of Data
|
1700
|
+
*
|
1701
|
+
* @return {Object} jQuery Object
|
1702
|
+
*/
|
1703
|
+
handleArray: function(array)
|
1704
|
+
{
|
1705
|
+
// Array will be merged in
|
1706
|
+
return this.$merge(array);
|
1707
|
+
},
|
1708
|
+
|
1709
|
+
/**
|
1710
|
+
* If number is given, it will get the object with the current number. Use -1 to get the last number.
|
1711
|
+
*
|
1712
|
+
* @alias items(number)
|
1713
|
+
* @alias jQuery.Chain.services.items.handleNumber
|
1714
|
+
*
|
1715
|
+
* @param {Number} number Index
|
1716
|
+
*
|
1717
|
+
* @return {Object} jQuery Object
|
1718
|
+
*/
|
1719
|
+
handleNumber: function(number)
|
1720
|
+
{
|
1721
|
+
// if -1, it will get the last.
|
1722
|
+
if(number == -1)
|
1723
|
+
{return this.collection('visible').filter(':last');}
|
1724
|
+
else
|
1725
|
+
{return this.collection('visible').eq(number);}
|
1726
|
+
},
|
1727
|
+
|
1728
|
+
/**
|
1729
|
+
* If @true@ is given, it will get all items including the hidden one.
|
1730
|
+
*
|
1731
|
+
* @alias items(true)
|
1732
|
+
* @alias jQuery.Chain.services.items.handleTrue
|
1733
|
+
*
|
1734
|
+
* @return {Object} jQuery Object
|
1735
|
+
*
|
1736
|
+
* @see jQuery.Chain.services.items.collections.all
|
1737
|
+
*/
|
1738
|
+
handleTrue: function()
|
1739
|
+
{
|
1740
|
+
return this.collection('all');
|
1741
|
+
},
|
1742
|
+
|
1743
|
+
/**
|
1744
|
+
* If nothing is given, it will get all visible items.
|
1745
|
+
*
|
1746
|
+
* @alias items(true)
|
1747
|
+
* @alias jQuery.Chain.services.items.handleTrue
|
1748
|
+
*
|
1749
|
+
* @return {Object} jQuery Object
|
1750
|
+
*
|
1751
|
+
* @see jQuery.Chain.services.items.collections.visible
|
1752
|
+
*/
|
1753
|
+
handleDefault: function()
|
1754
|
+
{
|
1755
|
+
return this.collection('visible');
|
1756
|
+
},
|
1757
|
+
|
1758
|
+
/**
|
1759
|
+
* Update element
|
1760
|
+
*
|
1761
|
+
* @alias jQuery.Chain.services.items.update
|
1762
|
+
*/
|
1763
|
+
update: function()
|
1764
|
+
{
|
1765
|
+
this.element.update();
|
1766
|
+
},
|
1767
|
+
|
1768
|
+
/**
|
1769
|
+
* Clear all items
|
1770
|
+
*
|
1771
|
+
* @alias jQuery.Chain.services.items.empty
|
1772
|
+
*/
|
1773
|
+
empty: function()
|
1774
|
+
{
|
1775
|
+
var all = this.collection('all');
|
1776
|
+
|
1777
|
+
// Remove items
|
1778
|
+
// Make it run in the background. for responsiveness.
|
1779
|
+
setTimeout(function(){all.each(function(){$(this).item('remove', true);});}, 1);
|
1780
|
+
|
1781
|
+
// Empty anchor container
|
1782
|
+
this.element.chain('anchor').empty();
|
1783
|
+
},
|
1784
|
+
|
1785
|
+
/**
|
1786
|
+
* Get collection of items. Define a collection by adding a function argument
|
1787
|
+
*
|
1788
|
+
* @alias jQuery.Chain.services.items.collection
|
1789
|
+
*
|
1790
|
+
* @param {String} col Collection name
|
1791
|
+
* @param {Function} fn Create a collection function
|
1792
|
+
*
|
1793
|
+
* @return {Object} jQuery Object
|
1794
|
+
*/
|
1795
|
+
collection: function(col, fn)
|
1796
|
+
{
|
1797
|
+
if(arguments.length > 1)
|
1798
|
+
{
|
1799
|
+
if(typeof fn == 'function')
|
1800
|
+
{this.collections[col] = fn;}
|
1801
|
+
|
1802
|
+
return this.element;
|
1803
|
+
}
|
1804
|
+
else
|
1805
|
+
{
|
1806
|
+
if(this.collections[col])
|
1807
|
+
{return this.collections[col].apply(this);}
|
1808
|
+
else
|
1809
|
+
{return $().eq(-1);}
|
1810
|
+
}
|
1811
|
+
|
1812
|
+
},
|
1813
|
+
|
1814
|
+
/**
|
1815
|
+
* Items Updater, called by @$(element).update()@
|
1816
|
+
*
|
1817
|
+
* @alias items('update')
|
1818
|
+
* @alias jQuery.Chain.services.items.$update
|
1819
|
+
*
|
1820
|
+
* @return {Object} jQuery Element
|
1821
|
+
*/
|
1822
|
+
$update: function()
|
1823
|
+
{
|
1824
|
+
if(!this.element.chain('active') || !this.isActive)
|
1825
|
+
{return this.element;}
|
1826
|
+
|
1827
|
+
var self = this;
|
1828
|
+
var builder = this.element.chain('builder');
|
1829
|
+
var template = this.element.chain('template');
|
1830
|
+
var push;
|
1831
|
+
|
1832
|
+
var iterator = function(){
|
1833
|
+
var clone = template
|
1834
|
+
.clone()[push ? 'appendTo' :'prependTo'](self.element.chain('anchor'))
|
1835
|
+
.addClass('chain-item')
|
1836
|
+
.item('root', self.element);
|
1837
|
+
|
1838
|
+
if(self.linkElement && $.Chain.jobject(this) && this.item())
|
1839
|
+
{clone.item('link', this, 'self');}
|
1840
|
+
else
|
1841
|
+
{clone.item(this);}
|
1842
|
+
|
1843
|
+
clone.chain(builder, true);
|
1844
|
+
};
|
1845
|
+
|
1846
|
+
push = false;
|
1847
|
+
$.each(this.shiftBuffer, iterator);
|
1848
|
+
push = true;
|
1849
|
+
$.each(this.pushBuffer, iterator);
|
1850
|
+
|
1851
|
+
|
1852
|
+
this.shiftBuffer = [];
|
1853
|
+
this.pushBuffer = [];
|
1854
|
+
|
1855
|
+
return this.element;
|
1856
|
+
},
|
1857
|
+
|
1858
|
+
/**
|
1859
|
+
* Add item(s). use @items('add', 'shift', item)@ to add item at the top
|
1860
|
+
*
|
1861
|
+
* @alias items('add')
|
1862
|
+
* @alias jQuery.Chain.services.items.$add
|
1863
|
+
*
|
1864
|
+
* @param {Object} item
|
1865
|
+
*
|
1866
|
+
* @return {Object} jQuery Object
|
1867
|
+
*/
|
1868
|
+
$add: function()
|
1869
|
+
{
|
1870
|
+
if(this.linkElement)
|
1871
|
+
{return this.element;}
|
1872
|
+
|
1873
|
+
var cmd;
|
1874
|
+
var args = Array.prototype.slice.call(arguments);
|
1875
|
+
// Extract command
|
1876
|
+
if(typeof args[0] == 'string')
|
1877
|
+
{cmd = args.shift();}
|
1878
|
+
|
1879
|
+
var buffer = (cmd == 'shift') ? 'shiftBuffer' : 'pushBuffer';
|
1880
|
+
|
1881
|
+
this.isActive = true;
|
1882
|
+
this[buffer] = this[buffer].concat(args);
|
1883
|
+
this.update();
|
1884
|
+
|
1885
|
+
return this.element;
|
1886
|
+
},
|
1887
|
+
|
1888
|
+
/**
|
1889
|
+
* Merge items with array of item data
|
1890
|
+
*
|
1891
|
+
* @alias items('merge')
|
1892
|
+
* @alias jQuery.Chain.services.items.$merge
|
1893
|
+
*
|
1894
|
+
* @param {String} cmd Switch for push/shift
|
1895
|
+
* @param {Array} items Item Data
|
1896
|
+
*
|
1897
|
+
* @return {Object} jQuery Element
|
1898
|
+
*/
|
1899
|
+
$merge: function(cmd, items)
|
1900
|
+
{
|
1901
|
+
if(this.linkElement)
|
1902
|
+
{return this.element;}
|
1903
|
+
|
1904
|
+
if(typeof cmd != 'string')
|
1905
|
+
{items = cmd;}
|
1906
|
+
var buffer = (cmd == 'shift') ? 'shiftBuffer' : 'pushBuffer';
|
1907
|
+
|
1908
|
+
this.isActive = true;
|
1909
|
+
if($.Chain.jobject(items))
|
1910
|
+
{this[buffer] = this[buffer].concat(items.map(function(){return $(this);}).get());}
|
1911
|
+
else if(items instanceof Array)
|
1912
|
+
{this[buffer] = this[buffer].concat(items);}
|
1913
|
+
this.update();
|
1914
|
+
|
1915
|
+
return this.element;
|
1916
|
+
},
|
1917
|
+
|
1918
|
+
/**
|
1919
|
+
* Replace items with new items array
|
1920
|
+
*
|
1921
|
+
* @alias items('replace')
|
1922
|
+
* @alias jQuery.Chain.services.items.$replace
|
1923
|
+
*
|
1924
|
+
* @param {String} cmd Switch for push/shift
|
1925
|
+
* @param {Array} items Item Data
|
1926
|
+
*
|
1927
|
+
* @return {Object} jQuery Element
|
1928
|
+
*/
|
1929
|
+
$replace: function(cmd, items)
|
1930
|
+
{
|
1931
|
+
if(this.linkElement && arguments.callee.caller != this.linkUpdater)
|
1932
|
+
{return this.element;}
|
1933
|
+
|
1934
|
+
if(typeof cmd != 'string')
|
1935
|
+
{items = cmd;}
|
1936
|
+
var buffer = (cmd == 'shift') ? 'shiftBuffer' : 'pushBuffer';
|
1937
|
+
|
1938
|
+
this.isActive = true;
|
1939
|
+
this.empty();
|
1940
|
+
|
1941
|
+
if($.Chain.jobject(items))
|
1942
|
+
{this[buffer] = items.map(function(){return $(this);}).get();}
|
1943
|
+
else if(items instanceof Array)
|
1944
|
+
{this[buffer] = items;}
|
1945
|
+
|
1946
|
+
this.update();
|
1947
|
+
|
1948
|
+
return this.element;
|
1949
|
+
},
|
1950
|
+
|
1951
|
+
/**
|
1952
|
+
* Remove item
|
1953
|
+
*
|
1954
|
+
* @alias items('remove')
|
1955
|
+
* @alias jQuery.Chain.services.items.$remove
|
1956
|
+
*
|
1957
|
+
* @param {Object, Number} item
|
1958
|
+
*
|
1959
|
+
* @return {Object} jQuery Object
|
1960
|
+
*/
|
1961
|
+
$remove: function()
|
1962
|
+
{
|
1963
|
+
if(this.linkElement)
|
1964
|
+
{return this.element;}
|
1965
|
+
|
1966
|
+
for(var i=0; i<arguments.length; i++)
|
1967
|
+
{this.handler(arguments[i]).item('remove', true);}
|
1968
|
+
this.update();
|
1969
|
+
|
1970
|
+
return this.element;
|
1971
|
+
},
|
1972
|
+
|
1973
|
+
/**
|
1974
|
+
* Reorder Item
|
1975
|
+
*
|
1976
|
+
* @alias items('reorder')
|
1977
|
+
* @alias jQuery.Chain.services.items.$reorder
|
1978
|
+
*
|
1979
|
+
* @param {Object} item1 Item 1
|
1980
|
+
* @param {Object} item2 Item 2
|
1981
|
+
*
|
1982
|
+
* @return {Object} jQuery object
|
1983
|
+
*/
|
1984
|
+
$reorder: function(item1, item2)
|
1985
|
+
{
|
1986
|
+
if(item2)
|
1987
|
+
{this.handler(item1).before(this.handler(item2));}
|
1988
|
+
else
|
1989
|
+
{this.handler(item1).appendTo(this.element.chain('anchor'));}
|
1990
|
+
this.update();
|
1991
|
+
|
1992
|
+
return this.element;
|
1993
|
+
},
|
1994
|
+
|
1995
|
+
/**
|
1996
|
+
* Clear all items
|
1997
|
+
*
|
1998
|
+
* @alias items('empty')
|
1999
|
+
* @alias jQuery.Chain.services.items.$empty
|
2000
|
+
*
|
2001
|
+
* @return {Object} jQuery object
|
2002
|
+
*/
|
2003
|
+
$empty: function()
|
2004
|
+
{
|
2005
|
+
if(this.linkElement)
|
2006
|
+
{return this.element;}
|
2007
|
+
|
2008
|
+
this.empty();
|
2009
|
+
this.shiftBuffer = [];
|
2010
|
+
this.pushBuffer = [];
|
2011
|
+
this.update();
|
2012
|
+
|
2013
|
+
return this.element;
|
2014
|
+
},
|
2015
|
+
|
2016
|
+
/**
|
2017
|
+
* Like @items()@ but returns array of data instead of the jQuery object.
|
2018
|
+
*
|
2019
|
+
* @alias items('data')
|
2020
|
+
* @alias jQuery.Chain.services.items.$data
|
2021
|
+
*
|
2022
|
+
* @return {Array} list of data
|
2023
|
+
*/
|
2024
|
+
$data: function(x)
|
2025
|
+
{
|
2026
|
+
return this.handler(x).map(function(){return $(this).item();}).get();
|
2027
|
+
},
|
2028
|
+
|
2029
|
+
/**
|
2030
|
+
* Bind Items to other (chained) element. If one of them is updated,
|
2031
|
+
* the linked element will be updated.
|
2032
|
+
*
|
2033
|
+
* @alias items('link')
|
2034
|
+
* @alias jQuery.Chain.services.items.$link
|
2035
|
+
*
|
2036
|
+
* @param {Object} element element/selector to be linked with
|
2037
|
+
* @param {String} collection Collection to be linked with (has to be @"self"@ if linked to item)
|
2038
|
+
*
|
2039
|
+
* @return {Object} jQuery Element
|
2040
|
+
*
|
2041
|
+
* @see jQuery.Chain.services.items.collection
|
2042
|
+
*/
|
2043
|
+
$link: function(element, collection)
|
2044
|
+
{
|
2045
|
+
// Remove linked element if it already exist
|
2046
|
+
if(this.linkElement)
|
2047
|
+
{
|
2048
|
+
this.linkElement.unbind('update', this.linkUpdater);
|
2049
|
+
this.linkElement = null;
|
2050
|
+
}
|
2051
|
+
|
2052
|
+
element = $(element);
|
2053
|
+
// If element exists
|
2054
|
+
if(element.length)
|
2055
|
+
{
|
2056
|
+
var self = this;
|
2057
|
+
this.linkElement = element;
|
2058
|
+
// Create Collector Function
|
2059
|
+
this.linkFunction = function()
|
2060
|
+
{
|
2061
|
+
if(typeof collection == 'function')
|
2062
|
+
{
|
2063
|
+
try{
|
2064
|
+
return collection.call(self.element, self.linkElement);
|
2065
|
+
}catch(e){
|
2066
|
+
return $().eq(-1);
|
2067
|
+
}
|
2068
|
+
}
|
2069
|
+
else if(typeof collection == 'string')
|
2070
|
+
{
|
2071
|
+
return self.linkElement.items('collection', collection);
|
2072
|
+
}
|
2073
|
+
else
|
2074
|
+
{
|
2075
|
+
return $().eq(-1);
|
2076
|
+
}
|
2077
|
+
};
|
2078
|
+
|
2079
|
+
// Create Updater Function
|
2080
|
+
this.linkUpdater = function()
|
2081
|
+
{
|
2082
|
+
self.$replace(self.linkFunction());
|
2083
|
+
};
|
2084
|
+
|
2085
|
+
// Bind updater to linked element
|
2086
|
+
this.linkElement.bind('update', this.linkUpdater);
|
2087
|
+
this.linkUpdater();
|
2088
|
+
}
|
2089
|
+
|
2090
|
+
return this.element;
|
2091
|
+
},
|
2092
|
+
|
2093
|
+
/**
|
2094
|
+
* Get index of an Item
|
2095
|
+
*
|
2096
|
+
* @alias items('index')
|
2097
|
+
* @alias jQuery.Chain.services.items.$index
|
2098
|
+
*
|
2099
|
+
* @param {Object} item
|
2100
|
+
*
|
2101
|
+
* @return {Number} index
|
2102
|
+
*/
|
2103
|
+
$index: function(item)
|
2104
|
+
{
|
2105
|
+
return this.collection('all').index(this.handler(item));
|
2106
|
+
},
|
2107
|
+
|
2108
|
+
/**
|
2109
|
+
* Get collection of items. Define a collection by adding a function argument
|
2110
|
+
*
|
2111
|
+
* @alias items('collection')
|
2112
|
+
* @alias jQuery.Chain.services.items.$collection
|
2113
|
+
*
|
2114
|
+
* @param {String} col Collection name
|
2115
|
+
* @param {Function} fn Create a collection function
|
2116
|
+
*
|
2117
|
+
* @return {Object} jQuery Object
|
2118
|
+
*/
|
2119
|
+
$collection: function()
|
2120
|
+
{
|
2121
|
+
return this.collection.apply(this, Array.prototype.slice.call(arguments));
|
2122
|
+
},
|
2123
|
+
|
2124
|
+
/**
|
2125
|
+
* Check Status of @items@
|
2126
|
+
*
|
2127
|
+
* @alias items('active')
|
2128
|
+
* @alias jQuery.Chain.services.items.$active
|
2129
|
+
*
|
2130
|
+
* @return {Boolean} Status
|
2131
|
+
*/
|
2132
|
+
$active: function()
|
2133
|
+
{
|
2134
|
+
return this.isActive;
|
2135
|
+
},
|
2136
|
+
|
2137
|
+
/**
|
2138
|
+
* Backup Item to the state before being built.
|
2139
|
+
*
|
2140
|
+
* @alias items('backup')
|
2141
|
+
* @alias jQuery.Chain.services.items.$backup
|
2142
|
+
*
|
2143
|
+
* @return {Object} jQuery Object
|
2144
|
+
*/
|
2145
|
+
$backup: function()
|
2146
|
+
{
|
2147
|
+
if(!this.element.chain('active') || !this.isActive)
|
2148
|
+
{return this.element;}
|
2149
|
+
|
2150
|
+
var buffer = [];
|
2151
|
+
this.collection('all').each(function(){
|
2152
|
+
var item = $(this).item();
|
2153
|
+
if(item)
|
2154
|
+
{buffer.push(item);}
|
2155
|
+
});
|
2156
|
+
|
2157
|
+
this.pushBuffer = buffer.concat(this.pushBuffer);
|
2158
|
+
|
2159
|
+
this.empty();
|
2160
|
+
|
2161
|
+
return this.element;
|
2162
|
+
},
|
2163
|
+
|
2164
|
+
/**
|
2165
|
+
* Destroy items service.
|
2166
|
+
*
|
2167
|
+
* @alias items('destroy')
|
2168
|
+
* @alias jQuery.Chain.services.items.$destroy
|
2169
|
+
*
|
2170
|
+
* @return {Object} jQuery Element
|
2171
|
+
*/
|
2172
|
+
$destroy: function()
|
2173
|
+
{
|
2174
|
+
this.empty();
|
2175
|
+
return this.element;
|
2176
|
+
}
|
2177
|
+
});
|
2178
|
+
|
2179
|
+
// Filtering extension
|
2180
|
+
$.Chain.extend('items', {
|
2181
|
+
/**
|
2182
|
+
* Filtering subroutine
|
2183
|
+
*
|
2184
|
+
* @alias jQuery.Chain.services.items.doFilter
|
2185
|
+
*/
|
2186
|
+
doFilter: function()
|
2187
|
+
{
|
2188
|
+
var props = this.searchProperties;
|
2189
|
+
var text = this.searchText;
|
2190
|
+
|
2191
|
+
if(text)
|
2192
|
+
{
|
2193
|
+
// Make text lowerCase if it is a string
|
2194
|
+
if(typeof text == 'string')
|
2195
|
+
{text = text.toLowerCase();}
|
2196
|
+
|
2197
|
+
// Filter items
|
2198
|
+
var items = this.element.items(true).filter(function(){
|
2199
|
+
var data = $(this).item();
|
2200
|
+
// If search properties is defined, search for text in those properties
|
2201
|
+
if(props)
|
2202
|
+
{
|
2203
|
+
for(var i=0; i<props.length; i++)
|
2204
|
+
{
|
2205
|
+
if(typeof data[props[i]] == 'string'
|
2206
|
+
&& !!(typeof text == 'string' ? data[props[i]].toLowerCase() : data[props[i]]).match(text))
|
2207
|
+
{return true;}
|
2208
|
+
}
|
2209
|
+
}
|
2210
|
+
// Otherwise search in all properties
|
2211
|
+
else
|
2212
|
+
{
|
2213
|
+
for(var prop in data)
|
2214
|
+
{
|
2215
|
+
if(typeof data[prop] == 'string'
|
2216
|
+
&& !!(typeof text == 'string' ? data[prop].toLowerCase() : data[prop]).match(text))
|
2217
|
+
{return true;}
|
2218
|
+
}
|
2219
|
+
}
|
2220
|
+
});
|
2221
|
+
this.element.items(true).not(items).hide();
|
2222
|
+
items.show();
|
2223
|
+
}
|
2224
|
+
else
|
2225
|
+
{
|
2226
|
+
this.element.items(true).show();
|
2227
|
+
this.element.unbind('preupdate', this.searchBinding);
|
2228
|
+
this.searchBinding = null;
|
2229
|
+
}
|
2230
|
+
},
|
2231
|
+
|
2232
|
+
/**
|
2233
|
+
* Filter items by criteria. Filtered items will be hidden.
|
2234
|
+
*
|
2235
|
+
* @alias items('filter')
|
2236
|
+
* @alias jQuery.Chain.services.items.$filter
|
2237
|
+
*
|
2238
|
+
* @param {String, RegExp} text Search keyword
|
2239
|
+
* @param {String, Array} properties Search properties
|
2240
|
+
*
|
2241
|
+
* @return {Object} jQuery Object
|
2242
|
+
*/
|
2243
|
+
$filter: function(text, properties)
|
2244
|
+
{
|
2245
|
+
// If no argument, just refilter
|
2246
|
+
if(!arguments.length)
|
2247
|
+
{return this.update();}
|
2248
|
+
|
2249
|
+
this.searchText = text;
|
2250
|
+
|
2251
|
+
if(typeof properties == 'string')
|
2252
|
+
{this.searchProperties = [properties];}
|
2253
|
+
else if(properties instanceof Array)
|
2254
|
+
{this.searchProperties = properties;}
|
2255
|
+
else
|
2256
|
+
{this.searchProperties = null;}
|
2257
|
+
|
2258
|
+
// Bind to preupdate
|
2259
|
+
if(!this.searchBinding)
|
2260
|
+
{
|
2261
|
+
var self = this;
|
2262
|
+
this.searchBinding = function(event, item){self.doFilter();};
|
2263
|
+
this.element.bind('preupdate', this.searchBinding);
|
2264
|
+
}
|
2265
|
+
|
2266
|
+
return this.update();
|
2267
|
+
}
|
2268
|
+
});
|
2269
|
+
|
2270
|
+
// Sorting extension
|
2271
|
+
$.Chain.extend('items', {
|
2272
|
+
/**
|
2273
|
+
* Sorting subroutine
|
2274
|
+
*
|
2275
|
+
* @alias jQuery.Chain.services.items.doSort
|
2276
|
+
*/
|
2277
|
+
doSort: function()
|
2278
|
+
{
|
2279
|
+
var name = this.sortName;
|
2280
|
+
var opt = this.sortOpt;
|
2281
|
+
|
2282
|
+
var sorter =
|
2283
|
+
{
|
2284
|
+
'number': function(a, b){
|
2285
|
+
return parseFloat(($(a).item()[name]+'').match(/\d+/gi)[0])
|
2286
|
+
- parseFloat(($(b).item()[name]+'').match(/\d+/gi)[0]);
|
2287
|
+
},
|
2288
|
+
|
2289
|
+
'default': function(a, b){
|
2290
|
+
return $(a).item()[name] > $(b).item()[name] ? 1 : -1;
|
2291
|
+
}
|
2292
|
+
};
|
2293
|
+
|
2294
|
+
if(name)
|
2295
|
+
{
|
2296
|
+
var sortfn = opt.fn || sorter[opt.type] || sorter['default'];
|
2297
|
+
|
2298
|
+
var array = this.element.items(true).get().sort(sortfn);
|
2299
|
+
|
2300
|
+
array = opt.desc ? array.reverse() : array;
|
2301
|
+
|
2302
|
+
for(var i=0; i<array.length; i++)
|
2303
|
+
{this.element.chain('anchor').append(array[i]);}
|
2304
|
+
|
2305
|
+
opt.desc = opt.toggle ? !opt.desc : opt.desc;
|
2306
|
+
}
|
2307
|
+
else
|
2308
|
+
{
|
2309
|
+
this.element.unbind('preupdate', this.sortBinding);
|
2310
|
+
this.sortBinding = null;
|
2311
|
+
}
|
2312
|
+
},
|
2313
|
+
|
2314
|
+
/**
|
2315
|
+
* Sort items by property.
|
2316
|
+
*
|
2317
|
+
* @alias items('sort')
|
2318
|
+
* @alias jQuery.Chain.services.items.$sort
|
2319
|
+
*
|
2320
|
+
* @param {String} name sorting property
|
2321
|
+
* @param {Object} opt {toggle:true/false, desc:true/false, type:'number/default'}
|
2322
|
+
*
|
2323
|
+
* @return {Object} jQuery Object
|
2324
|
+
*/
|
2325
|
+
$sort: function(name, opt)
|
2326
|
+
{
|
2327
|
+
if(!name && name !== null && name !== false)
|
2328
|
+
{return this.update();}
|
2329
|
+
|
2330
|
+
if(this.sortName != name)
|
2331
|
+
{this.sortOpt = $.extend({desc:false, type:'default', toggle:false}, opt);}
|
2332
|
+
else
|
2333
|
+
{$.extend(this.sortOpt, opt);}
|
2334
|
+
|
2335
|
+
this.sortName = name;
|
2336
|
+
|
2337
|
+
if(!this.sortBinding)
|
2338
|
+
{
|
2339
|
+
var self = this;
|
2340
|
+
this.sortBinding = function(event, item){self.doSort();};
|
2341
|
+
this.element.bind('preupdate', this.sortBinding);
|
2342
|
+
}
|
2343
|
+
|
2344
|
+
return this.update();
|
2345
|
+
}
|
2346
|
+
});
|
2347
|
+
|
2348
|
+
})(jQuery);
|