voltron 0.2.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,159 @@
1
+ //= require voltron-ext
2
+ //= require voltron-core
3
+
4
+ Voltron.addModule('Dispatch', function(){
5
+ var _events = {}
6
+ _globals = {};
7
+
8
+ return {
9
+ addEventWatcher: function(event){
10
+ var args = Array.prototype.slice.call(arguments, 1).flatten().compact();
11
+ _events[event] = args;
12
+ $.each(args, function(index, evt){
13
+ if(['element', 'event', 'data'].includes(evt.toLowerCase())){
14
+ Voltron.debug('error', 'Provided event watcher argument %o is a reserved observer param and will be overridden when the event is dispatched. Consider changing the name of the argument in your call to addEventWatcher for %o', evt, event);
15
+ }
16
+ });
17
+ Voltron.debug('info', 'Added event watcher for %o', event);
18
+ return this.listen();
19
+ },
20
+
21
+ addGlobalEvent: function(event, selector){
22
+ var options = this.getDispatchOptions(event, { data: {} }, {});
23
+ event = Object.keys(options).first();
24
+ var module = options[event].module;
25
+ var alias = options[event].alias;
26
+ if(!$.isArray(_globals[event])) _globals[event] = [];
27
+ _globals[event].push({ selector: selector, alias: alias, module: module });
28
+ return this.listen();
29
+ },
30
+
31
+ listen: function(){
32
+ $(document).off('.voltron').off('.voltron_global');
33
+ $(document).on(this.getEvents(), '[data-dispatch]', this.trigger);
34
+
35
+ var globals = this.getGlobalEvents();
36
+ for(var i=0; i<globals.length; i++){
37
+ for(var j=0; j<globals[i].data.length; j++){
38
+ $(document).on(globals[i].event, [globals[i].data[j].selector].flatten().join(', '), this.getGlobalCallback(globals[i].data[j]))
39
+ }
40
+ }
41
+
42
+ return this;
43
+ },
44
+
45
+ getEvents: function(){
46
+ return $.map(_events, function(val,key){
47
+ return key + '.voltron';
48
+ }).join(' ');
49
+ },
50
+
51
+ getGlobalEvents: function(){
52
+ return $.map(_globals, function(val,key){
53
+ return { event: key + '.voltron_global', data: val };
54
+ });
55
+ },
56
+
57
+ getGlobalCallback: function(data){
58
+ return function(event){
59
+ // In case the element in question has a data-dispatch attribute already, don't overwrite it
60
+ // We'll restore the existing data after we trigger the event
61
+ var oldDispatch = $(this).data('dispatch');
62
+ $(this).data('dispatch', [[data.module, event.type].compact().join(':'), data.alias].compact().join('/') );
63
+ Voltron.getModule('Dispatch').trigger.call(this, event);
64
+ $(this).data('dispatch', oldDispatch);
65
+ };
66
+ },
67
+
68
+ getSelectors: function(event){
69
+ var pattern = new RegExp(event);
70
+ return $.map(_globals, function(val,key){
71
+ if(pattern.test(key)){
72
+ var selectors = val.map(function(v){ return v.selector; });
73
+ return selectors.flatten();
74
+ }
75
+ }).flatten().compact();
76
+ },
77
+
78
+ getHash: function(keys, vals){
79
+ return keys.length === vals.length ? keys.reduce(function(obj, key, index){
80
+ obj[key] = vals[index];
81
+ return obj;
82
+ }, {}) : {};
83
+ },
84
+
85
+ getArgumentHash: function(event, args){
86
+ if(_events[event]){
87
+ return this.getHash(_events[event], args);
88
+ }
89
+ return {};
90
+ },
91
+
92
+ trigger: function(event){
93
+ if($(this).data('dispatch')){
94
+ var args = Voltron('Dispatch/getArgumentHash', event.type, Array.prototype.slice.call(arguments, 1));
95
+ var params = $.extend(args, { element: this, event: event, data: $(this).data() });
96
+
97
+ var dispatches = params.data.dispatch.split(/\s+/);
98
+ var events = {};
99
+
100
+ for(var i=0; i<dispatches.length; i++){
101
+ events = $.extend(events, Voltron('Dispatch/getDispatchOptions', dispatches[i], params, this));
102
+ }
103
+
104
+ if(events[event.type]){
105
+ var alias = events[event.type]['alias'];
106
+ var moduleName = events[event.type]['module'];
107
+
108
+ if(Voltron.hasModule(moduleName)){
109
+ var module = Voltron.getModule(moduleName);
110
+ var aliasMethod = Voltron('Dispatch/getDispatchMethod', event.type, alias);
111
+
112
+ if(module.canReceiveEvents()){
113
+ if($.isFunction(module[aliasMethod])){
114
+ module[aliasMethod](params);
115
+ Voltron.debug('info', 'Dispatching callback function %o in the %o module with observer object: %o', aliasMethod, module.name(), params);
116
+ }else if(!$.isFunction(module[aliasMethod])){
117
+ Voltron.debug('warn', 'Callback function %o was not found in the %o module. Try defining it, or remove %o from your element\'s data-dispatch attribute if the event does not need to be observed', aliasMethod, module.name(), event.type);
118
+ return;
119
+ }else{
120
+ Voltron.debug('log', 'Attempted to dispatch %o in %o module with observer object: %o', aliasMethod, module.name(), params);
121
+ }
122
+ }
123
+ }else if(Voltron._modules[moduleName.toLowerCase()]){
124
+ Voltron.debug('log', 'Tried to dispatch the %o event, but the module %o does not exist. Triggered on element: %o', event.type, moduleName, this);
125
+ }
126
+ }
127
+ }
128
+ },
129
+
130
+ getDispatchOptions: function(dispatch, params, element){
131
+ var defaultModule = params.data.module || Voltron.getConfig('controller');
132
+ var defaultAlias = element.id || element.tagName;
133
+ var options = [];
134
+
135
+ if((matches = dispatch.match(/^([a-z_\-]+):([a-z\_\-:]+)\/([a-z\_\-]+)/i)) !== null){
136
+ // Match format: "module:action/alias"
137
+ options[matches[2]] = { alias: matches[3], module: matches[1] };
138
+ }else if((matches = dispatch.match(/^([a-z\_\-:]+)\/([a-z\_\-]+)/i)) !== null){
139
+ // Match format: "action/alias", using default module
140
+ options[matches[1]] = { alias: matches[2], module: defaultModule };
141
+ }else if((matches = dispatch.match(/^([a-z_\-]+):([a-z\_\-:]+)/i)) !== null){
142
+ // Match format: "module:action", using default alias
143
+ options[matches[2]] = { alias: defaultAlias, module: matches[1] };
144
+ }else{
145
+ // Match everthing else as if no alias or module was defined, use defaults for both
146
+ options[dispatch.toLowerCase()] = { alias: defaultAlias, module: defaultModule };
147
+ }
148
+ return options;
149
+ },
150
+
151
+ getDispatchMethod: function(event, alias){
152
+ return ['on', event, alias].compact().join('_')
153
+ .replace(/[^a-z0-9\_\-:]+/ig, '')
154
+ .replace(/([a-z0-9])([A-Z])/g, function(match){ return [match[0], match[1]].join('_') })
155
+ .toLowerCase()
156
+ .replace(/_([a-z0-9])|\-([a-z0-9])|:([a-z0-9])/ig, function(match){ return match[1].toUpperCase(); });
157
+ }
158
+ };
159
+ }, true);
@@ -0,0 +1,122 @@
1
+ // String
2
+
3
+ String.prototype.trim = function(what){
4
+ var out = $.trim(this);
5
+ if(what){
6
+ var re = new RegExp('^' + what.toString() + '|' + what.toString() + '$');
7
+ out = out.replace(re, '');
8
+ }
9
+ return $.trim(out);
10
+ };
11
+
12
+ String.prototype.blank = function(){
13
+ return this.trim() == '';
14
+ };
15
+
16
+ String.prototype.startsWith = function(what){
17
+ var re = new RegExp('^' + what.toString());
18
+ return re.test(this.trim());
19
+ };
20
+
21
+ String.prototype.endsWidth = function(what){
22
+ var re = new RegExp(what.toString() + '$');
23
+ return re.test(this.trim());
24
+ };
25
+
26
+ String.prototype.contains = function(what){
27
+ var re = new RegExp(what.toString(), 'i');
28
+ return re.test(this);
29
+ };
30
+
31
+ // Array
32
+
33
+ Array.prototype.compact = function(){
34
+ for(var i=0; i<this.length; i++){
35
+ if(!this[i] || (typeof this[i].blank == 'function' && this[i].blank())){
36
+ this.splice(i, 1);
37
+ i--;
38
+ }
39
+ }
40
+ return this;
41
+ };
42
+
43
+ Array.prototype.includes = function(what){
44
+ if(typeof what == 'object'){
45
+ var re = new RegExp(what);
46
+ for(var i=0; i<this.length; i++){
47
+ if(re.test(this[i].toString())){
48
+ return true;
49
+ }
50
+ }
51
+ }else{
52
+ for(var i=0; i<this.length; i++){
53
+ if(this[i] == what){
54
+ return true;
55
+ }
56
+ }
57
+ }
58
+ return false;
59
+ };
60
+
61
+ Array.prototype.flatten = function(){
62
+ var b = Array.prototype.concat.apply([], this);
63
+ if(b.length != this.length){
64
+ b = b.flatten();
65
+ };
66
+
67
+ return b;
68
+ };
69
+
70
+ Array.prototype.blank = function(){
71
+ return this.compact().length == 0;
72
+ };
73
+
74
+ Array.prototype.uniq = function(){
75
+ var u = {}, a = [];
76
+ for(var i=0, l=this.length; i<l; ++i){
77
+ if(u.hasOwnProperty(this[i])){
78
+ continue;
79
+ }
80
+ a.push(this[i]);
81
+ u[this[i]] = 1;
82
+ }
83
+ return a;
84
+ };
85
+
86
+ Array.prototype.first = function(){
87
+ return this[0];
88
+ };
89
+
90
+ Array.prototype.last = function(){
91
+ return this[this.length-1];
92
+ };
93
+
94
+ Array.prototype.any = function(callback){
95
+ for(var i=0; i<this.length; i++){
96
+ if(callback(this[i])){
97
+ return true;
98
+ }
99
+ }
100
+ return false;
101
+ };
102
+
103
+ Array.prototype.all = function(callback){
104
+ for(var i=0; i<this.length; i++){
105
+ if(!callback(this[i])){
106
+ return false;
107
+ }
108
+ }
109
+ return true;
110
+ };
111
+
112
+ // Boolean
113
+
114
+ Boolean.prototype.blank = function(){
115
+ return this === false;
116
+ };
117
+
118
+ // Number
119
+
120
+ Number.prototype.blank = function(){
121
+ return this <= 0;
122
+ };
@@ -0,0 +1,156 @@
1
+ //= require voltron
2
+
3
+ /**
4
+ * Voltron::Observer
5
+ *
6
+ * Adds ability to monitor DOM events that can be observed by means of the +Voltron.on()+ method
7
+ *
8
+ * List of events that can now be observed:
9
+ *
10
+ * +append+ When an element is append to the DOM
11
+ * +remove+ When an element is removed from the DOM
12
+ * +conceal+ When an element on the DOM is hidden (defined as the result of jQuery's `:hidden` selector)
13
+ * +reveal+ When an element on the DOM is displayed (defined as the result of jQuery's `:visible` selector)
14
+ *
15
+ * Example usage, from within any method defined in a Voltron module:
16
+ *
17
+ * Voltron.on('append:div', function(o){
18
+ * // Do things with the appended element, context is +Voltron+
19
+ * });
20
+ *
21
+ * OR
22
+ *
23
+ * this.on('append:div', function(o){
24
+ * // Do things with the appended element, context is the module in which this observer was defined
25
+ * });
26
+ *
27
+ */
28
+ Voltron.addModule('Observer', '*', function(){
29
+ 'use strict';
30
+
31
+ var _observer = null;
32
+
33
+ var _defaults = {
34
+ subtree: true,
35
+ childList: true,
36
+ characterData: false,
37
+ attributes: true,
38
+ attributeFilter: ['style', 'class']
39
+ };
40
+
41
+ return {
42
+ initialize: function(options){
43
+ options = $.extend(_defaults, options);
44
+ this.getObserver().observe(document.body, options);
45
+
46
+ // Trigger append and reveal events on start up for appropriate elements
47
+ $(this.getElements('append')).trigger('append');
48
+ $(this.getElements('reveal')).filter(function(){
49
+ return Voltron('Observer/isVisible', this);
50
+ }).trigger('reveal');
51
+ },
52
+
53
+ stop: function(){
54
+ this.getObserver().disconnect();
55
+ },
56
+
57
+ process: function(mutations){
58
+ // Get a unique array of DOM elements that each has the associated mutation as a part of it's dataset
59
+ var elements = this.getMutationElements(mutations);
60
+
61
+ // Iterate through each element, dispatching the appropriate event for each elements mutation
62
+ for(var i=0; i<elements.length; i++){
63
+ var mutation = $(elements[i]).data('_mutation');
64
+
65
+ if(!mutation || !mutation.type) continue;
66
+
67
+ if(mutation.type == 'childList'){
68
+ // Flag nodes that have been added, and don't dispatch on any that have
69
+ // This solves the issue of recursion if an element that dispatches `added` is moved in the DOM
70
+ // Also dispatch only on elements that are configured to have `added` dispatched,
71
+ // including the element itself if applicable
72
+ $(mutation.addedNodes).filter(function(){
73
+ return !$(this).data('_mutation_appended');
74
+ }).data('_mutation_appended', true)
75
+ .find(this.getElements('append', 'reveal'))
76
+ .addBack(this.getElements('append', 'reveal'))
77
+ .trigger('append').filter(function(){
78
+ return Voltron('Observer/isVisible', this);
79
+ }).trigger('reveal');
80
+
81
+ // Flag nodes that have been removed to avoid unnecessary dispatching
82
+ // Dispatch the removed event on any child elements configured to do so,
83
+ // including the element itself if applicable
84
+ // Event must be dispatched manually since at this point the element no
85
+ // longer exists in the DOM, and can't be trigger()'ed
86
+ $(mutation.removedNodes).filter(function(){
87
+ return !$(this).data('_mutation_removed');
88
+ }).data('_mutation_removed', true)
89
+ .find(this.getElements('remove', 'conceal'))
90
+ .addBack(this.getElements('remove', 'conceal'))
91
+ .each(function(){
92
+ Voltron.getModule('Dispatch').trigger.call(this, $.Event('remove', { target: this }));
93
+ Voltron.getModule('Dispatch').trigger.call(this, $.Event('conceal', { target: this }));
94
+ });
95
+ }else if(mutation.type == 'attributes'){
96
+ var target = $(mutation.target);
97
+ // If currently animating, break out. We only want to dispatch when the state is truly reached
98
+ if(target.is(':animated')) break;
99
+
100
+ if(!this.isVisible(mutation.target)){
101
+ target.find(this.getElements('conceal')).trigger('conceal');
102
+ }else if(this.isVisible(mutation.target)){
103
+ target.find(this.getElements('reveal')).filter(function(){
104
+ return Voltron('Observer/isVisible', this);
105
+ }).trigger('reveal');
106
+ }
107
+ }
108
+ }
109
+ },
110
+
111
+ getObserver: function(){
112
+ if(_observer === null){
113
+ _observer = new MutationObserver($.proxy(function(mutations){
114
+ // Process all of the elements with mutations
115
+ this.process(mutations);
116
+ }, this));
117
+ }
118
+ return _observer;
119
+ },
120
+
121
+ getMutationElements: function(mutations){
122
+ if($.isFunction($.uniqueSort)){
123
+ // >= jQuery 3
124
+ return $.uniqueSort($.map(mutations, function(mut){
125
+ return $(mut.target).data('_mutation', mut).get(0);
126
+ }));
127
+ }else{
128
+ // < jQuery 3
129
+ return $.unique($.map(mutations, function(mut){
130
+ return $(mut.target).data('_mutation', mut).get(0);
131
+ }));
132
+ }
133
+ },
134
+
135
+ getElements: function(){
136
+ return $.map(Array.prototype.slice.call(arguments, 0), function(dispatch){
137
+ return ['[data-dispatch*="' + dispatch + '"]', Voltron('Dispatch/getSelectors', dispatch)];
138
+ }).flatten().join(', ');
139
+ },
140
+
141
+ isVisible: function(element){
142
+ var i = 0,
143
+ visible = $(element).is(':visible');
144
+
145
+ while(element.tagName !== 'BODY'){
146
+ if($(element).is(':hidden') || $(element).width() == 0 || $(element).height() == 0) return false;
147
+ // Limit to only traversing up 200 DOM elements before we hit the body tag
148
+ // If we go that far and still don't reach the body tag, something's up
149
+ // or the html is beyond crappy.
150
+ if(i++ >= 200) break;
151
+ element = $(element).parent().get(0);
152
+ }
153
+ return true && visible;
154
+ }
155
+ };
156
+ }, true);
@@ -0,0 +1,3 @@
1
+ //= require voltron-ext
2
+ //= require voltron-core
3
+ //= require voltron-dispatch
@@ -0,0 +1,11 @@
1
+ module VoltronHelper
2
+
3
+ def voltron_include_tag
4
+ javascript_tag "Voltron.initialize(#{voltron_config_json});", type: 'text/javascript'
5
+ end
6
+
7
+ def voltron_config_json
8
+ Voltron.config.to_h.merge({ controller: controller_name, action: action_name }).to_json.html_safe
9
+ end
10
+
11
+ end