twitter-flight-rails 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - ree
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Yousef Ourabi
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # Twitter Flight framework for Rails
2
+
3
+ This asset gem packages the [twitter flight](https://github.com/twitter/flight/) framework for the Rails asset pipeline.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'twitter-flight-rails'
10
+
11
+ or
12
+
13
+ gem 'twitter-flight-rails', :git => "git@github.com:yourabi/twitter-flight-rails.git"
14
+
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install twitter-flight-rails
23
+
24
+ ## Usage
25
+
26
+ To start using the twitter flight fraemwork in your rails app enable it via the asset pipeline (app/assets/javascripts/application.js).
27
+
28
+ Add the folllwing:
29
+
30
+ ```js
31
+
32
+ //= require twitter/flight
33
+
34
+ ```
35
+
36
+ Currently this version tracks flight master [commit e07b90c78d](https://github.com/twitter/flight/commit/e07b90c78d416549455354cbcd3e7f8a001c4fdf) and may support release tags in the future.
37
+
38
+ ## Dependencies
39
+
40
+ Flight uses [ES5-shim](https://github.com/kriskowal/es5-shim) to polyfill ES5 support for older browsers and [JQuery](http://jquery.com) for DOM manipulation API.
41
+
42
+ Note: as of version 0.0.2 the es5-shim dependency is not handled.
43
+
44
+ ## Contributing
45
+
46
+ 1. Fork it
47
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
48
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
49
+ 4. Push to the branch (`git push origin my-new-feature`)
50
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,7 @@
1
+ module Twitter
2
+ module Flight
3
+ module Rails
4
+ VERSION = "0.0.2"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ require "twitter-flight-rails/version"
2
+
3
+ module Twitter
4
+ module Flight
5
+ module Rails
6
+ class Engine < ::Rails::Engine
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'twitter-flight-rails/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "twitter-flight-rails"
8
+ gem.version = Twitter::Flight::Rails::VERSION
9
+ gem.authors = ["Yousef Ourabi"]
10
+ gem.email = ["yourabi@gmail.com"]
11
+ gem.description = %q{twitter-flight-rails flight framework for Rails asset pipeline}
12
+ gem.summary = %q{twitter-flight-rails packages the flight framework into an asset gem}
13
+ gem.homepage = "https://github.com/yourabi/twitter-flight-rails"
14
+
15
+ gem.add_dependency 'railties', '>= 3.1'
16
+ gem.add_dependency 'actionpack', '>= 3.1'
17
+ gem.add_dependency 'jquery-rails'
18
+
19
+ gem.add_development_dependency 'rails', '>= 3.1'
20
+
21
+ gem.files = `git ls-files`.split($/)
22
+ # gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
23
+ # gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
24
+ gem.require_paths = ["lib"]
25
+ end
@@ -0,0 +1,75 @@
1
+ // ==========================================
2
+ // Copyright 2013 Twitter, Inc
3
+ // Licensed under The MIT License
4
+ // http://opensource.org/licenses/MIT
5
+ // ==========================================
6
+
7
+ "use strict";
8
+
9
+ define(
10
+
11
+ [
12
+ './utils',
13
+ './compose'
14
+ ],
15
+
16
+ function (util, compose) {
17
+
18
+ var advice = {
19
+
20
+ around: function(base, wrapped) {
21
+ return function() {
22
+ var args = util.toArray(arguments);
23
+ return wrapped.apply(this, [base.bind(this)].concat(args));
24
+ }
25
+ },
26
+
27
+ before: function(base, before) {
28
+ return this.around(base, function() {
29
+ var args = util.toArray(arguments),
30
+ orig = args.shift(),
31
+ beforeFn;
32
+
33
+ beforeFn = (typeof before == 'function') ? before : before.obj[before.fnName];
34
+ beforeFn.apply(this, args);
35
+ return (orig).apply(this, args);
36
+ });
37
+ },
38
+
39
+ after: function(base, after) {
40
+ return this.around(base, function() {
41
+ var args = util.toArray(arguments),
42
+ orig = args.shift(),
43
+ afterFn;
44
+
45
+ // this is a separate statement for debugging purposes.
46
+ var res = (orig.unbound || orig).apply(this, args);
47
+
48
+ afterFn = (typeof after == 'function') ? after : after.obj[after.fnName];
49
+ afterFn.apply(this, args);
50
+ return res;
51
+ });
52
+ },
53
+
54
+ // a mixin that allows other mixins to augment existing functions by adding additional
55
+ // code before, after or around.
56
+ withAdvice: function() {
57
+ ['before', 'after', 'around'].forEach(function(m) {
58
+ this[m] = function(method, fn) {
59
+
60
+ compose.unlockProperty(this, method, function() {
61
+ if (typeof this[method] == 'function') {
62
+ return this[method] = advice[m](this[method], fn);
63
+ } else {
64
+ return this[method] = fn;
65
+ }
66
+ });
67
+
68
+ };
69
+ }, this);
70
+ }
71
+ };
72
+
73
+ return advice;
74
+ }
75
+ );
@@ -0,0 +1,262 @@
1
+ // ==========================================
2
+ // Copyright 2013 Twitter, Inc
3
+ // Licensed under The MIT License
4
+ // http://opensource.org/licenses/MIT
5
+ // ==========================================
6
+
7
+ "use strict";
8
+
9
+ define(
10
+
11
+ [
12
+ './advice',
13
+ './utils',
14
+ './compose',
15
+ './registry'
16
+ ],
17
+
18
+ function(advice, utils, compose, registry) {
19
+
20
+ function teardownInstance(instanceInfo){
21
+ instanceInfo.events.slice().forEach(function(event) {
22
+ var args = [event.type];
23
+
24
+ event.element && args.unshift(event.element);
25
+ (typeof event.callback == 'function') && args.push(event.callback);
26
+
27
+ this.off.apply(this, args);
28
+ }, instanceInfo.instance);
29
+ }
30
+
31
+
32
+ function teardown() {
33
+ this.trigger("componentTearDown");
34
+ teardownInstance(registry.findInstanceInfo(this));
35
+ }
36
+
37
+ //teardown for all instances of this constructor
38
+ function teardownAll() {
39
+ var componentInfo = registry.findComponentInfo(this);
40
+
41
+ componentInfo && componentInfo.instances.slice().forEach(function(info) {
42
+ info.instance.teardown();
43
+ });
44
+ }
45
+
46
+ //common mixin allocates basic functionality - used by all component prototypes
47
+ //callback context is bound to component
48
+ function withBaseComponent() {
49
+
50
+ // delegate trigger, bind and unbind to an element
51
+ // if $element not supplied, use component's node
52
+ // other arguments are passed on
53
+ this.trigger = function() {
54
+ var $element, type, data;
55
+ var args = utils.toArray(arguments);
56
+
57
+ if (typeof args[args.length - 1] != "string") {
58
+ data = args.pop();
59
+ }
60
+
61
+ $element = (args.length == 2) ? $(args.shift()) : this.$node;
62
+ type = args[0];
63
+
64
+ if (window.DEBUG && window.postMessage) {
65
+ try {
66
+ window.postMessage(data, '*');
67
+ } catch(e) {
68
+ console.log('unserializable data for event',type,':',data);
69
+ throw new Error(
70
+ ["The event", event.type, "on component", this.describe, "was triggered with non-serializable data"].join(" ")
71
+ );
72
+ }
73
+ }
74
+
75
+ if (typeof this.attr.eventData === 'object') {
76
+ data = $.extend(true, {}, this.attr.eventData, data);
77
+ }
78
+
79
+ return $element.trigger(type, data);
80
+ };
81
+
82
+ this.on = function() {
83
+ var $element, type, callback, originalCb;
84
+ var args = utils.toArray(arguments);
85
+
86
+ if (typeof args[args.length - 1] == "object") {
87
+ //delegate callback
88
+ originalCb = utils.delegate(
89
+ this.resolveDelegateRules(args.pop())
90
+ );
91
+ } else {
92
+ originalCb = args.pop();
93
+ }
94
+
95
+ callback = originalCb && originalCb.bind(this);
96
+ callback.target = originalCb;
97
+
98
+ // if the original callback is already branded by jQuery's guid, copy it to the context-bound version
99
+ if (originalCb.guid) {
100
+ callback.guid = originalCb.guid;
101
+ }
102
+
103
+ $element = (args.length == 2) ? $(args.shift()) : this.$node;
104
+ type = args[0];
105
+
106
+ if (typeof callback == 'undefined') {
107
+ throw new Error("Unable to bind to '" + type + "' because the given callback is undefined");
108
+ }
109
+
110
+ $element.on(type, callback);
111
+
112
+ // get jquery's guid from our bound fn, so unbinding will work
113
+ originalCb.guid = callback.guid;
114
+
115
+ return callback;
116
+ };
117
+
118
+ this.off = function() {
119
+ var $element, type, callback;
120
+ var args = utils.toArray(arguments);
121
+
122
+ if (typeof args[args.length - 1] == "function") {
123
+ callback = args.pop();
124
+ }
125
+
126
+ $element = (args.length == 2) ? $(args.shift()) : this.$node;
127
+ type = args[0];
128
+
129
+ return $element.off(type, callback);
130
+ };
131
+
132
+ this.resolveDelegateRules = function(ruleInfo) {
133
+ var rules = {};
134
+
135
+ Object.keys(ruleInfo).forEach(
136
+ function(r) {
137
+ if (!this.attr.hasOwnProperty(r)) {
138
+ throw new Error('Component "' + this.describe + '" wants to listen on "' + r + '" but no such attribute was defined.');
139
+ }
140
+ rules[this.attr[r]] = ruleInfo[r];
141
+ },
142
+ this
143
+ );
144
+
145
+ return rules;
146
+ };
147
+
148
+ this.defaultAttrs = function(defaults) {
149
+ utils.push(this.defaults, defaults, true) || (this.defaults = defaults);
150
+ };
151
+
152
+ this.select = function(attributeKey) {
153
+ return this.$node.find(this.attr[attributeKey]);
154
+ };
155
+
156
+ this.initialize = $.noop;
157
+ this.teardown = teardown;
158
+ }
159
+
160
+ function attachTo(selector/*, options args */) {
161
+ if (!selector) {
162
+ throw new Error("Component needs to be attachTo'd a jQuery object, native node or selector string");
163
+ }
164
+
165
+ var options = utils.merge.apply(utils, utils.toArray(arguments, 1));
166
+
167
+ $(selector).each(function(i, node) {
168
+ new this(node, options);
169
+ }.bind(this));
170
+ }
171
+
172
+ // define the constructor for a custom component type
173
+ // takes an unlimited number of mixin functions as arguments
174
+ // typical api call with 3 mixins: define(timeline, withTweetCapability, withScrollCapability);
175
+ function define(/*mixins*/) {
176
+ var mixins = utils.toArray(arguments);
177
+
178
+ Component.toString = function() {
179
+ var prettyPrintMixins = mixins.map(function(mixin) {
180
+ if ($.browser.msie) {
181
+ var m = mixin.toString().match(/function (.*?)\s?\(/);
182
+ return (m && m[1]) ? m[1] : "";
183
+ } else {
184
+ return mixin.name;
185
+ }
186
+ }).join(', ').replace(/\s\,/g,'');//weed out no-named mixins
187
+
188
+ return prettyPrintMixins;
189
+ };
190
+
191
+ Component.describe = Component.toString();
192
+
193
+ //'options' is optional hash to be merged with 'defaults' in the component definition
194
+ function Component(node, options) {
195
+ var fnCache = {}, uuid = 0;
196
+
197
+ if (!node) {
198
+ throw new Error("Component needs a node");
199
+ }
200
+
201
+ if (node.jquery) {
202
+ this.node = node[0];
203
+ this.$node = node;
204
+ } else {
205
+ this.node = node;
206
+ this.$node = $(node);
207
+ }
208
+
209
+ this.describe = this.constructor.describe;
210
+
211
+ this.bind = function(func) {
212
+ var bound;
213
+
214
+ if (func.uuid && (bound = fnCache[func.uuid])) {
215
+ return bound;
216
+ }
217
+
218
+ var bindArgs = utils.toArray(arguments, 1);
219
+ bindArgs.unshift(this); //prepend context
220
+
221
+ bound = func.bind.apply(func, bindArgs);
222
+ bound.target = func;
223
+ func.uuid = uuid++;
224
+ fnCache[func.uuid] = bound;
225
+
226
+ return bound;
227
+ };
228
+
229
+ //merge defaults with supplied options
230
+ this.attr = utils.merge(this.defaults, options);
231
+ this.defaults && Object.keys(this.defaults).forEach(function(key) {
232
+ if (this.defaults[key] === null && this.attr[key] === null) {
233
+ throw new Error('Required attribute "' + key + '" not specified in attachTo for component "' + this.describe + '".');
234
+ }
235
+ }, this);
236
+
237
+ this.initialize.call(this, options || {});
238
+
239
+ this.trigger('componentInitialized');
240
+ }
241
+
242
+ Component.attachTo = attachTo;
243
+ Component.teardownAll = teardownAll;
244
+
245
+ // prepend common mixins to supplied list, then mixin all flavors
246
+ mixins.unshift(withBaseComponent, advice.withAdvice, registry.withRegistration);
247
+
248
+ compose.mixin(Component.prototype, mixins);
249
+
250
+ return Component;
251
+ }
252
+
253
+ define.teardownAll = function() {
254
+ registry.components.slice().forEach(function(c) {
255
+ c.component.teardownAll();
256
+ });
257
+ registry.reset();
258
+ };
259
+
260
+ return define;
261
+ }
262
+ );
@@ -0,0 +1,86 @@
1
+ // ==========================================
2
+ // Copyright 2013 Twitter, Inc
3
+ // Licensed under The MIT License
4
+ // http://opensource.org/licenses/MIT
5
+ // ==========================================
6
+
7
+ "use strict";
8
+
9
+ define(
10
+
11
+ [
12
+ './utils',
13
+ '../tools/debug/debug'
14
+ ],
15
+
16
+ function(util, debug) {
17
+
18
+ //enumerables are shims - getOwnPropertyDescriptor shim doesn't work
19
+ var canWriteProtect = debug.enabled && !util.isEnumerable(Object, 'getOwnPropertyDescriptor');
20
+ //whitelist of unlockable property names
21
+ var dontLock = ['mixedIn'];
22
+
23
+ if (canWriteProtect) {
24
+ //IE8 getOwnPropertyDescriptor is built-in but throws exeption on non DOM objects
25
+ try {
26
+ Object.getOwnPropertyDescriptor(Object, 'keys');
27
+ } catch(e) {
28
+ canWriteProtect = false;
29
+ }
30
+ }
31
+
32
+ function setPropertyWritability(obj, isWritable) {
33
+ if (!canWriteProtect) {
34
+ return;
35
+ }
36
+
37
+ var props = Object.create(null);
38
+
39
+ Object.keys(obj).forEach(
40
+ function (key) {
41
+ if (dontLock.indexOf(key) < 0) {
42
+ var desc = Object.getOwnPropertyDescriptor(obj, key);
43
+ desc.writable = isWritable;
44
+ props[key] = desc;
45
+ }
46
+ }
47
+ );
48
+
49
+ Object.defineProperties(obj, props);
50
+ }
51
+
52
+ function unlockProperty(obj, prop, op) {
53
+ var writable;
54
+
55
+ if (!canWriteProtect || !obj.hasOwnProperty(prop)) {
56
+ op.call(obj);
57
+ return;
58
+ }
59
+
60
+ writable = Object.getOwnPropertyDescriptor(obj, prop).writable;
61
+ Object.defineProperty(obj, prop, { writable: true });
62
+ op.call(obj);
63
+ Object.defineProperty(obj, prop, { writable: writable });
64
+ }
65
+
66
+ function mixin(base, mixins) {
67
+ base.mixedIn = base.hasOwnProperty('mixedIn') ? base.mixedIn : [];
68
+
69
+ mixins.forEach(function(mixin) {
70
+ if (base.mixedIn.indexOf(mixin) == -1) {
71
+ setPropertyWritability(base, false);
72
+ mixin.call(base);
73
+ base.mixedIn.push(mixin);
74
+ }
75
+ });
76
+
77
+ setPropertyWritability(base, true);
78
+ }
79
+
80
+ return {
81
+ mixin: mixin,
82
+ unlockProperty: unlockProperty
83
+ };
84
+
85
+ }
86
+ );
@@ -0,0 +1,30 @@
1
+ // ==========================================
2
+ // Copyright 2013 Twitter, Inc
3
+ // Licensed under The MIT License
4
+ // http://opensource.org/licenses/MIT
5
+ // ==========================================
6
+
7
+ define(
8
+
9
+ [
10
+ './advice',
11
+ './component',
12
+ './compose',
13
+ './logger',
14
+ './registry',
15
+ './utils'
16
+ ],
17
+
18
+ function (advice, component, compose, logger, registry, utils) {
19
+
20
+ return {
21
+ advice: advice,
22
+ component: component,
23
+ compose: compose,
24
+ logger: logger,
25
+ registry: registry,
26
+ utils: utils
27
+ };
28
+
29
+ }
30
+ );
@@ -0,0 +1,93 @@
1
+ // ==========================================
2
+ // Copyright 2013 Twitter, Inc
3
+ // Licensed under The MIT License
4
+ // http://opensource.org/licenses/MIT
5
+ // ==========================================
6
+
7
+ "use strict";
8
+
9
+ define(
10
+
11
+ [
12
+ './compose',
13
+ './utils'
14
+ ],
15
+
16
+ function (compose, util) {
17
+
18
+ var actionSymbols = {
19
+ on:'<-',
20
+ trigger: '->',
21
+ off: 'x '
22
+ };
23
+
24
+ function elemToString(elem) {
25
+ var tagStr = elem.tagName ? elem.tagName.toLowerCase() : elem.toString();
26
+ var classStr = elem.className ? "." + (elem.className) : "";
27
+ var result = tagStr + classStr;
28
+ return elem.tagName ? ['\'', '\''].join(result) : result;
29
+ }
30
+
31
+ function log(action, component, eventArgs) {
32
+
33
+ var name, elem, fn, fnName, logFilter, toRegExp, actionLoggable, nameLoggable;
34
+
35
+ if (typeof eventArgs[eventArgs.length-1] == 'function') {
36
+ fn = eventArgs.pop();
37
+ fn = fn.unbound || fn; //use unbound version if any (better info)
38
+ }
39
+
40
+ if (typeof eventArgs[eventArgs.length - 1] == 'object') {
41
+ eventArgs.pop(); //trigger data arg - not logged right now
42
+ }
43
+
44
+ if (eventArgs.length == 2) {
45
+ elem = eventArgs[0];
46
+ name = eventArgs[1];
47
+ } else {
48
+ elem = component.$node[0];
49
+ name = eventArgs[0];
50
+ }
51
+
52
+ if (window.DEBUG) {
53
+ logFilter = DEBUG.events.logFilter;
54
+
55
+ // no regex for you, actions...
56
+ actionLoggable = logFilter.actions=="all" || (logFilter.actions.indexOf(action) > -1);
57
+ // event name filter allow wildcards or regex...
58
+ toRegExp = function(expr) {
59
+ return expr.test ? expr : new RegExp("^" + expr.replace(/\*/g, ".*") + "$");
60
+ };
61
+ nameLoggable =
62
+ logFilter.eventNames=="all" ||
63
+ logFilter.eventNames.some(function(e) {return toRegExp(e).test(name)});
64
+
65
+ if (actionLoggable && nameLoggable) {
66
+ console.info(
67
+ actionSymbols[action],
68
+ action,
69
+ '[' + name + ']',
70
+ elemToString(elem),
71
+ component.constructor.describe,
72
+ fn && (fnName = fn.name || fn.displayName) && '-> ' + fnName
73
+ );
74
+ }
75
+ }
76
+ }
77
+
78
+
79
+ function withLogging() {
80
+ this.before('trigger', function() {
81
+ log('trigger', this, util.toArray(arguments));
82
+ });
83
+ this.before('on', function() {
84
+ log('on', this, util.toArray(arguments));
85
+ });
86
+ this.before('off', function(eventArgs) {
87
+ log('off', this, util.toArray(arguments));
88
+ });
89
+ }
90
+
91
+ return withLogging;
92
+ }
93
+ );
@@ -0,0 +1,234 @@
1
+ // ==========================================
2
+ // Copyright 2013 Twitter, Inc
3
+ // Licensed under The MIT License
4
+ // http://opensource.org/licenses/MIT
5
+ // ==========================================
6
+
7
+ "use strict";
8
+
9
+ define(
10
+
11
+ [
12
+ './utils'
13
+ ],
14
+
15
+ function (util) {
16
+
17
+ function parseEventArgs(instance, args) {
18
+ var element, type, callback;
19
+
20
+ args = util.toArray(args);
21
+
22
+ if (typeof args[args.length-1] === 'function') {
23
+ callback = args.pop();
24
+ }
25
+
26
+ if (typeof args[args.length-1] === 'object') {
27
+ args.pop();
28
+ }
29
+
30
+ if (args.length == 2) {
31
+ element = args[0];
32
+ type = args[1];
33
+ } else {
34
+ element = instance.node;
35
+ type = args[0];
36
+ }
37
+
38
+ return {
39
+ element: element,
40
+ type: type,
41
+ callback: callback
42
+ };
43
+ }
44
+
45
+ function matchEvent(a, b) {
46
+ return (
47
+ (a.element == b.element) &&
48
+ (a.type == b.type) &&
49
+ (b.callback == null || (a.callback == b.callback))
50
+ );
51
+ }
52
+
53
+ function Registry() {
54
+
55
+ var registry = this;
56
+
57
+ (this.reset = function() {
58
+ this.components = [];
59
+ this.allInstances = [];
60
+ this.events = [];
61
+ }).call(this);
62
+
63
+ function ComponentInfo(component) {
64
+ this.component = component;
65
+ this.instances = [];
66
+
67
+ this.addInstance = function(instance) {
68
+ this.throwIfInstanceExistsOnNode(instance);
69
+
70
+ var instanceInfo = new InstanceInfo(instance);
71
+ this.instances.push(instanceInfo);
72
+
73
+ return instanceInfo;
74
+ }
75
+
76
+ this.throwIfInstanceExistsOnNode = function(instance) {
77
+ this.instances.forEach(function (instanceInfo) {
78
+ if (instanceInfo.instance.$node[0] === instance.$node[0]) {
79
+ throw new Error('Instance of ' + instance.constructor + ' already exists on node ' + instance.$node[0]);
80
+ }
81
+ });
82
+ }
83
+
84
+ this.removeInstance = function(instance) {
85
+ var instanceInfo = this.instances.filter(function(instanceInfo) {
86
+ return instanceInfo.instance == instance;
87
+ })[0];
88
+
89
+ var index = this.instances.indexOf(instanceInfo);
90
+
91
+ (index > -1) && this.instances.splice(index, 1);
92
+
93
+ if (!this.instances.length) {
94
+ //if I hold no more instances remove me from registry
95
+ registry.removeComponentInfo(this);
96
+ }
97
+ }
98
+ }
99
+
100
+ function InstanceInfo(instance) {
101
+ this.instance = instance;
102
+ this.events = [];
103
+
104
+ this.addTrigger = function() {};
105
+
106
+ this.addBind = function(event) {
107
+ this.events.push(event);
108
+ registry.events.push(event);
109
+ };
110
+
111
+ this.removeBind = function(event) {
112
+ for (var i = 0, e; e = this.events[i]; i++) {
113
+ if (matchEvent(e, event)) {
114
+ this.events.splice(i, 1);
115
+ }
116
+ }
117
+ }
118
+ }
119
+
120
+ this.addInstance = function(instance) {
121
+ var component = this.findComponentInfo(instance);
122
+
123
+ if (!component) {
124
+ component = new ComponentInfo(instance.constructor);
125
+ this.components.push(component);
126
+ }
127
+
128
+ var inst = component.addInstance(instance);
129
+
130
+ this.allInstances.push(inst);
131
+
132
+ return component;
133
+ };
134
+
135
+ this.removeInstance = function(instance) {
136
+ var index, instInfo = this.findInstanceInfo(instance);
137
+
138
+ //remove from component info
139
+ var componentInfo = this.findComponentInfo(instance);
140
+ componentInfo.removeInstance(instance);
141
+
142
+ //remove from registry
143
+ var index = this.allInstances.indexOf(instInfo);
144
+ (index > -1) && this.allInstances.splice(index, 1);
145
+ };
146
+
147
+ this.removeComponentInfo = function(componentInfo) {
148
+ var index = this.components.indexOf(componentInfo);
149
+ (index > -1) && this.components.splice(index, 1);
150
+ };
151
+
152
+ this.findComponentInfo = function(which) {
153
+ var component = which.attachTo ? which : which.constructor;
154
+
155
+ for (var i = 0, c; c = this.components[i]; i++) {
156
+ if (c.component === component) {
157
+ return c;
158
+ }
159
+ }
160
+
161
+ return null;
162
+ };
163
+
164
+ this.findInstanceInfo = function(which) {
165
+ var testFn;
166
+
167
+ if (which.node) {
168
+ //by instance (returns matched instance)
169
+ testFn = function(inst) {return inst.instance === which};
170
+ } else {
171
+ //by node (returns array of matches)
172
+ testFn = function(inst) {return inst.instance.node === which};
173
+ }
174
+
175
+ var matches = this.allInstances.filter(testFn);
176
+ if (!matches.length) {
177
+ return which.node ? null : [];
178
+ }
179
+ return which.node ? matches[0] : matches;
180
+ };
181
+
182
+ this.trigger = function() {
183
+ var event = parseEventArgs(this, arguments),
184
+ instance = registry.findInstanceInfo(this);
185
+
186
+ if (instance) {
187
+ instance.addTrigger(event);
188
+ }
189
+ };
190
+
191
+ this.on = function(componentOn) {
192
+ var otherArgs = util.toArray(arguments, 1);
193
+ var instance = registry.findInstanceInfo(this);
194
+ var boundCallback;
195
+
196
+ if (instance) {
197
+ boundCallback = componentOn.apply(null, otherArgs);
198
+ if(boundCallback) {
199
+ otherArgs[otherArgs.length-1] = boundCallback;
200
+ }
201
+ var event = parseEventArgs(this, otherArgs);
202
+ instance.addBind(event);
203
+ }
204
+ };
205
+
206
+ this.off = function(el, type, callback) {
207
+ var event = parseEventArgs(this, arguments),
208
+ instance = registry.findInstanceInfo(this);
209
+
210
+ if (instance) {
211
+ instance.removeBind(event);
212
+ }
213
+ };
214
+
215
+ this.teardown = function() {
216
+ registry.removeInstance(this);
217
+ };
218
+
219
+ this.withRegistration = function() {
220
+ this.before('initialize', function() {
221
+ registry.addInstance(this);
222
+ });
223
+
224
+ this.after('trigger', registry.trigger);
225
+ this.around('on', registry.on);
226
+ this.after('off', registry.off);
227
+ this.after('teardown', {obj:registry, fnName:'teardown'});
228
+ };
229
+
230
+ }
231
+
232
+ return new Registry;
233
+ }
234
+ );
@@ -0,0 +1,228 @@
1
+ // ==========================================
2
+ // Copyright 2013 Twitter, Inc
3
+ // Licensed under The MIT License
4
+ // http://opensource.org/licenses/MIT
5
+ // ==========================================
6
+
7
+ "use strict";
8
+
9
+ define(
10
+
11
+ [],
12
+
13
+ function () {
14
+
15
+ var arry = [];
16
+ var DEFAULT_INTERVAL = 100;
17
+
18
+ var utils = {
19
+
20
+ isDomObj: function(obj) {
21
+ return !!(obj.nodeType || (obj === window));
22
+ },
23
+
24
+ toArray: function(obj, from) {
25
+ return arry.slice.call(obj, from);
26
+ },
27
+
28
+ // returns new object representing multiple objects merged together
29
+ // optional final argument is boolean which specifies if merge is recursive
30
+ // original objects are unmodified
31
+ //
32
+ // usage:
33
+ // var base = {a:2, b:6};
34
+ // var extra = {b:3, c:4};
35
+ // merge(base, extra); //{a:2, b:3, c:4}
36
+ // base; //{a:2, b:6}
37
+ //
38
+ // var base = {a:2, b:6};
39
+ // var extra = {b:3, c:4};
40
+ // var extraExtra = {a:4, d:9};
41
+ // merge(base, extra, extraExtra); //{a:4, b:3, c:4. d: 9}
42
+ // base; //{a:2, b:6}
43
+ //
44
+ // var base = {a:2, b:{bb:4, cc:5}};
45
+ // var extra = {a:4, b:{cc:7, dd:1}};
46
+ // merge(base, extra, true); //{a:4, b:{bb:4, cc:7, dd:1}}
47
+ // base; //{a:2, b:6}
48
+
49
+ merge: function(/*obj1, obj2,....deepCopy*/) {
50
+ var args = this.toArray(arguments);
51
+
52
+ //start with empty object so a copy is created
53
+ args.unshift({});
54
+
55
+ if (args[args.length - 1] === true) {
56
+ //jquery extend requires deep copy as first arg
57
+ args.pop();
58
+ args.unshift(true);
59
+ }
60
+
61
+ return $.extend.apply(undefined, args);
62
+ },
63
+
64
+ // updates base in place by copying properties of extra to it
65
+ // optionally clobber protected
66
+ // usage:
67
+ // var base = {a:2, b:6};
68
+ // var extra = {c:4};
69
+ // push(base, extra); //{a:2, b:6, c:4}
70
+ // base; //{a:2, b:6, c:4}
71
+ //
72
+ // var base = {a:2, b:6};
73
+ // var extra = {b: 4 c:4};
74
+ // push(base, extra, true); //Error ("utils.push attempted to overwrite 'b' while running in protected mode")
75
+ // base; //{a:2, b:6}
76
+ //
77
+ // objects with the same key will merge recursively when protect is false
78
+ // eg:
79
+ // var base = {a:16, b:{bb:4, cc:10}};
80
+ // var extra = {b:{cc:25, dd:19}, c:5};
81
+ // push(base, extra); //{a:16, {bb:4, cc:25, dd:19}, c:5}
82
+ //
83
+ push: function(base, extra, protect) {
84
+ if (base) {
85
+ Object.keys(extra || {}).forEach(function(key) {
86
+ if (base[key] && protect) {
87
+ throw Error("utils.push attempted to overwrite '" + key + "' while running in protected mode");
88
+ }
89
+
90
+ if (typeof base[key] == "object" && typeof extra[key] == "object") {
91
+ //recurse
92
+ this.push(base[key], extra[key]);
93
+ } else {
94
+ //no protect, so extra wins
95
+ base[key] = extra[key];
96
+ }
97
+ }, this);
98
+ }
99
+
100
+ return base;
101
+ },
102
+
103
+ isEnumerable: function(obj, property) {
104
+ return Object.keys(obj).indexOf(property) > -1;
105
+ },
106
+
107
+ //build a function from other function(s)
108
+ //util.compose(a,b,c) -> a(b(c()));
109
+ //implementation lifted from underscore.js (c) 2009-2012 Jeremy Ashkenas
110
+ compose: function() {
111
+ var funcs = arguments;
112
+
113
+ return function() {
114
+ var args = arguments;
115
+
116
+ for (var i = funcs.length-1; i >= 0; i--) {
117
+ args = [funcs[i].apply(this, args)];
118
+ }
119
+
120
+ return args[0];
121
+ };
122
+ },
123
+
124
+ // Can only unique arrays of homogeneous primitives, e.g. an array of only strings, an array of only booleans, or an array of only numerics
125
+ uniqueArray: function(array) {
126
+ var u = {}, a = [];
127
+
128
+ for (var i = 0, l = array.length; i < l; ++i) {
129
+ if (u.hasOwnProperty(array[i])) {
130
+ continue;
131
+ }
132
+
133
+ a.push(array[i]);
134
+ u[array[i]] = 1;
135
+ }
136
+
137
+ return a;
138
+ },
139
+
140
+ debounce: function(func, wait, immediate) {
141
+ if (typeof wait != 'number') {
142
+ wait = DEFAULT_INTERVAL;
143
+ }
144
+
145
+ var timeout, result;
146
+
147
+ return function() {
148
+ var context = this, args = arguments;
149
+ var later = function() {
150
+ timeout = null;
151
+ if (!immediate) {
152
+ result = func.apply(context, args);
153
+ }
154
+ };
155
+ var callNow = immediate && !timeout;
156
+
157
+ clearTimeout(timeout);
158
+ timeout = setTimeout(later, wait);
159
+
160
+ if (callNow) {
161
+ result = func.apply(context, args);
162
+ }
163
+
164
+ return result;
165
+ };
166
+ },
167
+
168
+ throttle: function(func, wait) {
169
+ if (typeof wait != 'number') {
170
+ wait = DEFAULT_INTERVAL;
171
+ }
172
+
173
+ var context, args, timeout, throttling, more, result;
174
+ var whenDone = this.debounce(function(){
175
+ more = throttling = false;
176
+ }, wait);
177
+
178
+ return function() {
179
+ context = this; args = arguments;
180
+ var later = function() {
181
+ timeout = null;
182
+ if (more) {
183
+ result = func.apply(context, args);
184
+ }
185
+ whenDone();
186
+ };
187
+
188
+ if (!timeout) {
189
+ timeout = setTimeout(later, wait);
190
+ }
191
+
192
+ if (throttling) {
193
+ more = true;
194
+ } else {
195
+ throttling = true;
196
+ result = func.apply(context, args);
197
+ }
198
+
199
+ whenDone();
200
+ return result;
201
+ };
202
+ },
203
+
204
+ countThen: function(num, base) {
205
+ return function() {
206
+ if (!--num) { return base.apply(this, arguments); }
207
+ };
208
+ },
209
+
210
+ delegate: function(rules) {
211
+ return function(e, data) {
212
+ var target = $(e.target), parent;
213
+
214
+ Object.keys(rules).forEach(function(selector) {
215
+ if ((parent = target.closest(selector)).length) {
216
+ data = data || {};
217
+ data.el = parent[0];
218
+ return rules[selector].apply(this, [e, data]);
219
+ }
220
+ }, this);
221
+ };
222
+ }
223
+
224
+ };
225
+
226
+ return utils;
227
+ }
228
+ );
@@ -0,0 +1 @@
1
+ //= require_directory twitter/flight
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: twitter-flight-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Yousef Ourabi
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: railties
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '3.1'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '3.1'
30
+ - !ruby/object:Gem::Dependency
31
+ name: actionpack
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '3.1'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '3.1'
46
+ - !ruby/object:Gem::Dependency
47
+ name: jquery-rails
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rails
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '3.1'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '3.1'
78
+ description: twitter-flight-rails flight framework for Rails asset pipeline
79
+ email:
80
+ - yourabi@gmail.com
81
+ executables: []
82
+ extensions: []
83
+ extra_rdoc_files: []
84
+ files:
85
+ - .gitignore
86
+ - .travis.yml
87
+ - Gemfile
88
+ - LICENSE.txt
89
+ - README.md
90
+ - Rakefile
91
+ - lib/twitter-flight-rails.rb
92
+ - lib/twitter-flight-rails/version.rb
93
+ - twitter-flight-rails.gemspec
94
+ - vendor/assets/javascripts/twitter/flight.js
95
+ - vendor/assets/javascripts/twitter/flight/advice.js
96
+ - vendor/assets/javascripts/twitter/flight/component.js
97
+ - vendor/assets/javascripts/twitter/flight/compose.js
98
+ - vendor/assets/javascripts/twitter/flight/index.js
99
+ - vendor/assets/javascripts/twitter/flight/logger.js
100
+ - vendor/assets/javascripts/twitter/flight/registry.js
101
+ - vendor/assets/javascripts/twitter/flight/utils.js
102
+ homepage: https://github.com/yourabi/twitter-flight-rails
103
+ licenses: []
104
+ post_install_message:
105
+ rdoc_options: []
106
+ require_paths:
107
+ - lib
108
+ required_ruby_version: !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ! '>='
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ none: false
116
+ requirements:
117
+ - - ! '>='
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ requirements: []
121
+ rubyforge_project:
122
+ rubygems_version: 1.8.23
123
+ signing_key:
124
+ specification_version: 3
125
+ summary: twitter-flight-rails packages the flight framework into an asset gem
126
+ test_files: []