pakyow 0.11.3 → 1.0.0.rc1

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.
Files changed (55) hide show
  1. checksums.yaml +5 -5
  2. data/LICENSE +3 -19
  3. data/README.md +96 -23
  4. data/lib/pakyow/all.rb +12 -0
  5. data/lib/pakyow/version.rb +5 -1
  6. metadata +49 -98
  7. data/bin/pakyow +0 -4
  8. data/lib/generators/pakyow/app/app_generator.rb +0 -93
  9. data/lib/generators/pakyow/app/templates/Gemfile +0 -18
  10. data/lib/generators/pakyow/app/templates/README.md +0 -28
  11. data/lib/generators/pakyow/app/templates/Rakefile +0 -3
  12. data/lib/generators/pakyow/app/templates/app/lib/bindings.rb +0 -3
  13. data/lib/generators/pakyow/app/templates/app/lib/helpers.rb +0 -3
  14. data/lib/generators/pakyow/app/templates/app/lib/routes.rb +0 -7
  15. data/lib/generators/pakyow/app/templates/app/setup.rb +0 -24
  16. data/lib/generators/pakyow/app/templates/app/views/_templates/default.html +0 -33
  17. data/lib/generators/pakyow/app/templates/app/views/index.html +0 -96
  18. data/lib/generators/pakyow/app/templates/config.ru +0 -5
  19. data/lib/generators/pakyow/app/templates/env +0 -1
  20. data/lib/generators/pakyow/app/templates/env.example +0 -3
  21. data/lib/generators/pakyow/app/templates/gitignore +0 -8
  22. data/lib/generators/pakyow/app/templates/public/apple-touch-icon-precomposed.png +0 -0
  23. data/lib/generators/pakyow/app/templates/public/apple-touch-icon.png +0 -0
  24. data/lib/generators/pakyow/app/templates/public/favicon.ico +0 -0
  25. data/lib/generators/pakyow/app/templates/public/scripts/ring/LICENSE +0 -20
  26. data/lib/generators/pakyow/app/templates/public/scripts/ring/components/fastlink.js +0 -14
  27. data/lib/generators/pakyow/app/templates/public/scripts/ring/components/fastlink.min.js +0 -1
  28. data/lib/generators/pakyow/app/templates/public/scripts/ring/components/ga.js +0 -34
  29. data/lib/generators/pakyow/app/templates/public/scripts/ring/components/ga.min.js +0 -1
  30. data/lib/generators/pakyow/app/templates/public/scripts/ring/components/loader.js +0 -9
  31. data/lib/generators/pakyow/app/templates/public/scripts/ring/components/loader.min.js +0 -1
  32. data/lib/generators/pakyow/app/templates/public/scripts/ring/components/modal.js +0 -98
  33. data/lib/generators/pakyow/app/templates/public/scripts/ring/components/modal.min.js +0 -1
  34. data/lib/generators/pakyow/app/templates/public/scripts/ring/components/mutable.js +0 -70
  35. data/lib/generators/pakyow/app/templates/public/scripts/ring/components/mutable.min.js +0 -1
  36. data/lib/generators/pakyow/app/templates/public/scripts/ring/components/navigator.js +0 -154
  37. data/lib/generators/pakyow/app/templates/public/scripts/ring/components/navigator.min.js +0 -1
  38. data/lib/generators/pakyow/app/templates/public/scripts/ring/components/notifier.js +0 -29
  39. data/lib/generators/pakyow/app/templates/public/scripts/ring/components/notifier.min.js +0 -1
  40. data/lib/generators/pakyow/app/templates/public/scripts/ring/pakyow.js +0 -1936
  41. data/lib/generators/pakyow/app/templates/public/scripts/ring/pakyow.min.js +0 -1
  42. data/lib/generators/pakyow/app/templates/public/styles/pakyow-css/LICENSE +0 -20
  43. data/lib/generators/pakyow/app/templates/public/styles/pakyow-css/VERSION +0 -1
  44. data/lib/generators/pakyow/app/templates/public/styles/pakyow-css/reset.css +0 -2
  45. data/lib/generators/pakyow/app/templates/public/styles/pakyow-css/structure.css +0 -2
  46. data/lib/generators/pakyow/app/templates/public/styles/pakyow-css/syntax.css +0 -2
  47. data/lib/generators/pakyow/app/templates/public/styles/pakyow-css/theme.css +0 -2
  48. data/lib/generators/pakyow/app/templates/rspec +0 -3
  49. data/lib/generators/pakyow/app/templates/spec/integration/app_spec.rb +0 -17
  50. data/lib/generators/pakyow/app/templates/spec/spec_helper.rb +0 -7
  51. data/lib/pakyow.rb +0 -12
  52. data/lib/pakyow/command_line_interface.rb +0 -79
  53. data/lib/pakyow/commands/console.rb +0 -27
  54. data/lib/pakyow/commands/console_methods.rb +0 -10
  55. data/lib/pakyow/commands/server.rb +0 -38
@@ -1,9 +0,0 @@
1
- pw.component.register('loader', function (view) {
2
- this.listen('socket:loading', function () {
3
- view.node.classList.add('ui-show');
4
- });
5
-
6
- this.listen('socket:loaded', function () {
7
- view.node.classList.remove('ui-show');
8
- });
9
- });
@@ -1 +0,0 @@
1
- pw.component.register("loader",function(o){this.listen("socket:loading",function(){o.node.classList.add("ui-show")}),this.listen("socket:loaded",function(){o.node.classList.remove("ui-show")})});
@@ -1,98 +0,0 @@
1
- pw.component.register('modal', function (view, config, name, id) {
2
- var self = this;
3
- var channel = 'modal:' + id;
4
- var blinder, modal;
5
- var blinderTemplate;
6
-
7
- if (blinderTemplate = document.querySelector('*[data-template="ui-modal-blinder"]')) {
8
- blinderTemplate = blinderTemplate.cloneNode(true);
9
- }
10
-
11
- this.listen(channel + ':navigator:enter', function (response) {
12
- if (!blinder) {
13
- if (blinderTemplate) {
14
- blinder = blinderTemplate.cloneNode(true);
15
- modal = blinder.querySelector('*[data-template="ui-modal-content"]');
16
- document.body.appendChild(blinder);
17
-
18
- blinder.removeAttribute('data-template');
19
- modal.removeAttribute('data-template');
20
- } else {
21
- blinder = document.createElement('DIV');
22
- blinder.classList.add('ui-modal-blinder');
23
-
24
- modal = document.createElement('DIV');
25
- modal.classList.add('ui-modal');
26
-
27
- blinder.appendChild(modal);
28
- document.body.appendChild(blinder);
29
- }
30
-
31
- blinder.addEventListener('click', function (evt) {
32
- if (evt.target === blinder) {
33
- evt.preventDefault();
34
- self.close();
35
-
36
- var uri = window.location.pathname;
37
-
38
- var opts = {
39
- uri: uri
40
- };
41
-
42
- window.history.pushState(opts, uri, uri);
43
- }
44
- });
45
- }
46
-
47
- modal.innerHTML = response.body;
48
- pw.component.findAndInit(blinder);
49
-
50
- blinder.classList.add('ui-appear');
51
- });
52
-
53
- this.listen(channel + ':navigator:exit', function () {
54
- self.close();
55
- });
56
-
57
- this.listen(channel + ':navigator:boot', function (uri) {
58
- self.load(uri);
59
- });
60
-
61
- view.node.addEventListener('click', function (evt) {
62
- evt.preventDefault();
63
- self.load(config.href || this.href);
64
- return false;
65
- });
66
-
67
- this.load = function (uri) {
68
- if (!window.socket) {
69
- document.location = uri;
70
- return;
71
- }
72
-
73
- var opts = {
74
- uri: uri,
75
- context: 'modal:' + id
76
- }
77
-
78
- if (config.container) {
79
- opts.container = config.container;
80
- }
81
-
82
- if (config.partial) {
83
- opts.partial = config.partial;
84
- }
85
-
86
- window.history.pushState(opts, uri, uri);
87
- };
88
-
89
- this.close = function () {
90
- if (!blinder || !modal) {
91
- return;
92
- }
93
-
94
- pw.node.remove(blinder);
95
- blinder = null;
96
- modal = null;
97
- };
98
- });
@@ -1 +0,0 @@
1
- pw.component.register("modal",function(t,e,n,o){var a,i,d,l=this,r="modal:"+o;(d=document.querySelector('*[data-template="ui-modal-blinder"]'))&&(d=d.cloneNode(!0)),this.listen(r+":navigator:enter",function(t){a||(d?(a=d.cloneNode(!0),i=a.querySelector('*[data-template="ui-modal-content"]'),document.body.appendChild(a),a.removeAttribute("data-template"),i.removeAttribute("data-template")):(a=document.createElement("DIV"),a.classList.add("ui-modal-blinder"),i=document.createElement("DIV"),i.classList.add("ui-modal"),a.appendChild(i),document.body.appendChild(a)),a.addEventListener("click",function(t){if(t.target===a){t.preventDefault(),l.close();var e=window.location.pathname,n={uri:e};window.history.pushState(n,e,e)}})),i.innerHTML=t.body,pw.component.findAndInit(a),a.classList.add("ui-appear")}),this.listen(r+":navigator:exit",function(){l.close()}),this.listen(r+":navigator:boot",function(t){l.load(t)}),t.node.addEventListener("click",function(t){return t.preventDefault(),l.load(e.href||this.href),!1}),this.load=function(t){if(!window.socket)return void(document.location=t);var n={uri:t,context:"modal:"+o};e.container&&(n.container=e.container),e.partial&&(n.partial=e.partial),window.history.pushState(n,t,t)},this.close=function(){a&&i&&(pw.node.remove(a),a=null,i=null)}});
@@ -1,70 +0,0 @@
1
- pw.component.register('mutable', function (view, config) {
2
- this.mutation = function (mutation) {
3
- // no socket, just submit the form
4
- if (!window.socket) {
5
- view.node.submit();
6
- return;
7
- }
8
-
9
- var datum = pw.util.dup(mutation);
10
- delete datum.__nested;
11
- delete datum.scope;
12
- delete datum.id;
13
-
14
- var message = {
15
- action: 'call-route'
16
- };
17
-
18
- if (view.node.tagName === 'FORM') {
19
- if (view.node.querySelector('input[type="file"]')) {
20
- // file uploads over websocket are not supported
21
- view.node.submit();
22
- return;
23
- }
24
-
25
- var method;
26
- var $methodOverride = view.node.querySelector('input[name="_method"]');
27
- if ($methodOverride) {
28
- method = $methodOverride.value;
29
- } else {
30
- method = view.node.getAttribute('method');
31
- }
32
-
33
- message.method = method;
34
- message.uri = view.node.getAttribute('action');
35
- message.input = pw.node.serialize(view.node);
36
- } else {
37
- //TODO deduce uri / method
38
-
39
- var input = {};
40
- input[mutation.scope] = datum;
41
- message.input = input;
42
- }
43
-
44
- var self = this;
45
- window.socket.send(message, function (res) {
46
- if (res.status === 302) {
47
- var dest = res.headers.Location;
48
-
49
- if (dest == window.location.pathname && (!window.context || window.context.name !== 'default')) {
50
- history.pushState({ uri: dest }, dest, dest);
51
- } else {
52
- //TODO trigger a response:redirect instead and let navigator subscribe
53
- history.pushState({ uri: dest }, dest, dest);
54
- }
55
- } else if (res.status === 400) {
56
- // bad request
57
- pw.component.broadcast('response:received', { response: res });
58
- return;
59
- } else {
60
- self.state.rollback();
61
- }
62
-
63
- pw.component.broadcast('response:received', { response: res });
64
-
65
- if (config.revert !== 'false') {
66
- self.revert();
67
- }
68
- });
69
- }
70
- });
@@ -1 +0,0 @@
1
- pw.component.register("mutable",function(e,t){this.mutation=function(o){if(!window.socket)return void e.node.submit();var n=pw.util.dup(o);delete n.__nested,delete n.scope,delete n.id;var i={action:"call-route"};if("FORM"===e.node.tagName){if(e.node.querySelector('input[type="file"]'))return void e.node.submit();var r,s=e.node.querySelector('input[name="_method"]');r=s?s.value:e.node.getAttribute("method"),i.method=r,i.uri=e.node.getAttribute("action"),i.input=pw.node.serialize(e.node)}else{var a={};a[o.scope]=n,i.input=a}var d=this;window.socket.send(i,function(e){if(302===e.status){var o=e.headers.Location;o!=window.location.pathname||window.context&&"default"===window.context.name?history.pushState({uri:o},o,o):history.pushState({uri:o},o,o)}else{if(400===e.status)return void pw.component.broadcast("response:received",{response:e});d.state.rollback()}pw.component.broadcast("response:received",{response:e}),"false"!==t.revert&&d.revert()})}});
@@ -1,154 +0,0 @@
1
- function boot() {
2
- if (!window.socket) {
3
- setTimeout(boot, 100);
4
- return;
5
- }
6
-
7
- if (window.location.hash) {
8
- var arr = window.location.hash.split('#:')[1].split('/');
9
- var context = arr.shift();
10
- var uri = arr.join('/');
11
-
12
- pw.component.broadcast(context + ':navigator:boot', uri);
13
- }
14
- }
15
-
16
- (function(history) {
17
- pw.init.register(boot);
18
-
19
- if (history) {
20
- var hasPushed = false;
21
- var pushState = history.pushState;
22
-
23
- history.pushState = function(state, title, uri) {
24
- hasPushed = true;
25
-
26
- if (typeof history.onpushstate == "function") {
27
- history.onpushstate({ state: state });
28
- }
29
-
30
- if (uri == window.location.pathname) {
31
- pw.component.broadcast(window.context.name + ':navigator:exit');
32
-
33
- window.context = {
34
- _state: state,
35
- name: 'default',
36
- uri: window.location.href
37
- };
38
-
39
- state.r_uri = uri;
40
- } else {
41
- handleState(state, 'forward');
42
- }
43
-
44
- return pushState.apply(history, [state, title, state.r_uri]);
45
- }
46
-
47
- window.onpopstate = function (evt) {
48
- if (!hasPushed) {
49
- return;
50
- }
51
-
52
- var state = evt.state;
53
-
54
- if (!state) {
55
- state = {};
56
- }
57
-
58
- if (!state.uri) {
59
- state.uri = window.context.uri;
60
- }
61
-
62
- handleState(state, 'back');
63
- }
64
- } else {
65
- // unsupported
66
- }
67
- })(window.history);
68
-
69
- window.context = {
70
- name: 'default',
71
- uri: window.location.href
72
- };
73
-
74
- function handleState(state, direction) {
75
- var uri = state.uri || state.url;
76
-
77
- // socket isn't ready, so just send 'em to the url
78
- if (!window.socket) {
79
- document.location = uri;
80
- return;
81
- }
82
-
83
- uri = uri.replace(document.location.origin, '');
84
- pw.component.broadcast('navigator:change', { uri: uri });
85
-
86
- if (state.context) {
87
- state.r_uri = document.location.pathname + '#:' + state.context + '/' + uri;
88
-
89
- window.context = {
90
- _state: state,
91
- name: state.context,
92
- uri: state.r_uri,
93
- container: state.container,
94
- partial: state.partial
95
- };
96
- } else {
97
- state.r_uri = uri;
98
-
99
- if (window.context.name !== 'default') {
100
- if (direction === 'back') {
101
- // we are leaving a context
102
- pw.component.broadcast(window.context.name + ':navigator:exit');
103
-
104
- window.context = {
105
- name: 'default',
106
- uri: state.uri
107
- };
108
-
109
- return;
110
- } else {
111
- // navigate in context
112
- state.r_uri = document.location.pathname + '#:' + window.context.name + '/' + uri;
113
- state.context = window.context.name;
114
- state.container = window.context.container;
115
- state.partial = window.context.partial;
116
- }
117
- }
118
- }
119
-
120
- var opts = {
121
- uri: uri,
122
- action: 'call-route',
123
- method: 'get'
124
- };
125
-
126
- if (state.container) {
127
- opts.container = state.container;
128
- }
129
-
130
- if (state.partial) {
131
- opts.partial = state.partial;
132
- }
133
-
134
- window.socket.send(opts, function (payload) {
135
- if (state.context) {
136
- pw.component.broadcast(state.context + ':navigator:enter', payload);
137
- } else {
138
- var body = payload.body[0];
139
-
140
- if (body.match(/<title>/)) {
141
- var title = body.split(/<title>/)[1].split('</title>')[0];
142
- document.querySelector('title').innerHTML = title;
143
- }
144
-
145
- var doc = document.documentElement.cloneNode();
146
- doc.innerHTML = body;
147
-
148
- document.body.innerHTML = doc.querySelector('body').innerHTML;
149
- pw.component.findAndInit(document.querySelectorAll('body')[0]);
150
-
151
- document.body.scrollTop = document.documentElement.scrollTop = 0;
152
- }
153
- });
154
- }
@@ -1 +0,0 @@
1
- function boot(){if(!window.socket)return void setTimeout(boot,100);if(window.location.hash){var t=window.location.hash.split("#:")[1].split("/"),n=t.shift(),o=t.join("/");pw.component.broadcast(n+":navigator:boot",o)}}function handleState(t,n){var o=t.uri||t.url;if(!window.socket)return void(document.location=o);if(o=o.replace(document.location.origin,""),pw.component.broadcast("navigator:change",{uri:o}),t.context)t.r_uri=document.location.pathname+"#:"+t.context+"/"+o,window.context={_state:t,name:t.context,uri:t.r_uri,container:t.container,partial:t.partial};else if(t.r_uri=o,"default"!==window.context.name){if("back"===n)return pw.component.broadcast(window.context.name+":navigator:exit"),void(window.context={name:"default",uri:t.uri});t.r_uri=document.location.pathname+"#:"+window.context.name+"/"+o,t.context=window.context.name,t.container=window.context.container,t.partial=window.context.partial}var e={uri:o,action:"call-route",method:"get"};t.container&&(e.container=t.container),t.partial&&(e.partial=t.partial),window.socket.send(e,function(n){if(t.context)pw.component.broadcast(t.context+":navigator:enter",n);else{var o=n.body[0];if(o.match(/<title>/)){var e=o.split(/<title>/)[1].split("</title>")[0];document.querySelector("title").innerHTML=e}var i=document.documentElement.cloneNode();i.innerHTML=o,document.body.innerHTML=i.querySelector("body").innerHTML,pw.component.findAndInit(document.querySelectorAll("body")[0]),document.body.scrollTop=document.documentElement.scrollTop=0}})}!function(t){if(pw.init.register(boot),t){var n=!1,o=t.pushState;t.pushState=function(e,i,a){return n=!0,"function"==typeof t.onpushstate&&t.onpushstate({state:e}),a==window.location.pathname?(pw.component.broadcast(window.context.name+":navigator:exit"),window.context={_state:e,name:"default",uri:window.location.href},e.r_uri=a):handleState(e,"forward"),o.apply(t,[e,i,e.r_uri])},window.onpopstate=function(t){if(n){var o=t.state;o||(o={}),o.uri||(o.uri=window.context.uri),handleState(o,"back")}}}}(window.history),window.context={name:"default",uri:window.location.href};
@@ -1,29 +0,0 @@
1
- pw.component.register('notifier', function (view, config) {
2
- var that = this;
3
-
4
- this.listen('notification:published', function (payload) {
5
- that.show(payload.notification);
6
- });
7
-
8
- this.listen('response:received', function (payload) {
9
- //TODO support notification type and add a class based on it for styling
10
- var notification = payload.response.headers['Pakyow-Notify'];
11
-
12
- if (notification) {
13
- that.show(notification);
14
- }
15
- });
16
-
17
- view.node.addEventListener('click', function (evt) {
18
- view.node.classList.add('hide');
19
- });
20
-
21
- this.message = function (channel, payload) {
22
- that.show(payload.notification);
23
- };
24
-
25
- this.show = function (notification) {
26
- view.node.innerText = notification;
27
- view.node.classList.remove('hide');
28
- };
29
- });
@@ -1 +0,0 @@
1
- pw.component.register("notifier",function(i,n){var e=this;this.listen("notification:published",function(i){e.show(i.notification)}),this.listen("response:received",function(i){var n=i.response.headers["Pakyow-Notify"];n&&e.show(n)}),i.node.addEventListener("click",function(n){i.node.classList.add("hide")}),this.message=function(i,n){e.show(n.notification)},this.show=function(n){i.node.innerText=n,i.node.classList.remove("hide")}});
@@ -1,1936 +0,0 @@
1
- var pw = {
2
- version: '0.2.4'
3
- };
4
-
5
- (function() {
6
- pw.util = {
7
- guid: function () {
8
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
9
- var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
10
- return v.toString(16);
11
- });
12
- },
13
-
14
- dup: function (object) {
15
- return JSON.parse(JSON.stringify(object));
16
- }
17
- };
18
- var fns = [];
19
-
20
- pw.init = {
21
- register: function (fn) {
22
- fns.push(fn);
23
- }
24
- };
25
-
26
- document.addEventListener("DOMContentLoaded", function() {
27
- fns.forEach(function (fn) {
28
- fn();
29
- });
30
- });
31
- var sigAttrs = ['data-scope', 'data-prop'];
32
- var valuelessTags = ['SELECT'];
33
- var selfClosingTags = ['AREA', 'BASE', 'BASEFONT', 'BR', 'HR', 'INPUT', 'IMG', 'LINK', 'META'];
34
-
35
- pw.node = {
36
- // returns the value of the node
37
- value: function (node) {
38
- if (node.tagName === 'INPUT') {
39
- if (node.type === 'checkbox') {
40
- if (node.checked) {
41
- return node.value ? node.value : true;
42
- } else {
43
- return false;
44
- }
45
- }
46
-
47
- return node.value;
48
- } else if (node.tagName === 'TEXTAREA') {
49
- return node.value;
50
- } else if (node.tagName === 'SELECT') {
51
- return node.value;
52
- }
53
-
54
- return node.textContent.trim();
55
- },
56
-
57
- /*
58
- Returns a representation of the node's state. Example:
59
-
60
- <div data-scope="list" data-id="1">
61
- <div data-scope="task" data-id="1">
62
- <label data-prop="desc">
63
- foo
64
- </label>
65
- </div>
66
- </div>
67
-
68
- [ [ { node: ..., id: '1', scope: 'list' }, [ { node: ..., id: '1', scope: 'task' }, [ { node: ..., prop: 'body' } ] ] ] ]
69
- */
70
-
71
- significant: function(node, arr) {
72
- if(node === document) {
73
- node = document.getElementsByTagName('body')[0];
74
- }
75
-
76
- if(arr === undefined) {
77
- arr = [];
78
- }
79
-
80
- var sig, nArr;
81
-
82
- if(sig = pw.node.isSignificant(node)) {
83
- nArr = [];
84
- arr.push([{ node: sig[0], type: sig[1] }, nArr]);
85
- } else {
86
- nArr = arr;
87
- }
88
-
89
- pw.node.toA(node.children).forEach(function (child) {
90
- pw.node.significant(child, nArr);
91
- });
92
-
93
- return arr;
94
- },
95
-
96
- // returns node and an indication of it's significance
97
- // (e.g value of scope/prop); returns false otherwise
98
- isSignificant: function(node) {
99
- var attr = sigAttrs.find(function (a) {
100
- return node.hasAttribute(a);
101
- });
102
-
103
- if (attr) {
104
- return [node, attr.split('-')[1]];
105
- } else {
106
- return false;
107
- }
108
- },
109
-
110
- mutable: function (node) {
111
- pw.node.significant(node).flatten().filter(function (n) {
112
- return pw.node.isMutable(n.node);
113
- }).map(function (n) {
114
- return n.node;
115
- });
116
- },
117
-
118
- // returns true if the node can mutate via interaction
119
- isMutable: function(node) {
120
- var tag = node.tagName;
121
- return tag === 'FORM' || (tag === 'INPUT' && !node.disabled);
122
- },
123
-
124
- // triggers event name on node with data
125
- trigger: function (evtName, node, data) {
126
- var evt = document.createEvent('Event');
127
- evt.initEvent(evtName, true, true);
128
-
129
- node._evtData = data;
130
- node.dispatchEvent(evt);
131
- },
132
-
133
- // replaces an event listener's callback for node by name
134
- replaceEventListener: function (eventName, node, cb) {
135
- node.removeEventListener(eventName);
136
- node.addEventListener(eventName, cb);
137
- },
138
-
139
- inForm: function (node) {
140
- if (node.tagName === 'FORM') {
141
- return true;
142
- }
143
-
144
- var next = node.parentNode;
145
- if (next && next !== document) {
146
- return pw.node.inForm(next);
147
- }
148
- },
149
-
150
- // finds and returns component for node
151
- component: function (node) {
152
- if (node.getAttribute('data-ui')) {
153
- return node;
154
- }
155
-
156
- var next = node.parentNode;
157
- if (next && next !== document) {
158
- return pw.node.component(next);
159
- }
160
- },
161
-
162
- // finds and returns scope for node
163
- scope: function (node) {
164
- if (node.getAttribute('data-scope')) {
165
- return node;
166
- }
167
-
168
- var next = node.parentNode;
169
- if (next && next !== document) {
170
- return pw.node.scope(next);
171
- }
172
- },
173
-
174
- // returns the name of the scope for node
175
- scopeName: function (node) {
176
- if (node.getAttribute('data-scope')) {
177
- return node.getAttribute('data-scope');
178
- }
179
-
180
- var next = node.parentNode;
181
- if (next && next !== document) {
182
- return pw.node.scopeName(next);
183
- }
184
- },
185
-
186
- // finds and returns prop for node
187
- prop: function (node) {
188
- if (node.getAttribute('data-prop')) {
189
- return node;
190
- }
191
-
192
- var next = node.parentNode;
193
- if (next && next !== document) {
194
- return pw.node.prop(next);
195
- }
196
- },
197
-
198
- // returns the name of the prop for node
199
- propName: function (node) {
200
- if (node.getAttribute('data-prop')) {
201
- return node.getAttribute('data-prop');
202
- }
203
-
204
- var next = node.parentNode;
205
- if (next && next !== document) {
206
- return pw.node.propName(next);
207
- }
208
- },
209
-
210
- // returns the name of the version for node
211
- versionName: function (node) {
212
- if (node.hasAttribute('data-version')) {
213
- return node.getAttribute('data-version');
214
- }
215
- },
216
-
217
- // creates a context in which view manipulations can occur
218
- invoke: function(node, cb) {
219
- cb.call(node);
220
- },
221
-
222
- invokeWithData: function(node, data, cb) {
223
- if (pw.node.isNodeList(node)) {
224
- node = pw.node.toA(node);
225
- }
226
-
227
- node = Array.ensure(node);
228
- data = Array.ensure(data);
229
-
230
- node.forEach(function (e, i) {
231
- cb.call(e, data[i]);
232
- });
233
- },
234
-
235
- match: function(node, data) {
236
- if (pw.node.isNodeList(node)) {
237
- node = pw.node.toA(node);
238
- }
239
-
240
- node = Array.ensure(node);
241
- data = Array.ensure(data);
242
-
243
- var collection = data.reduce(function (c, dm, i) {
244
- // get the view, or if we're out just use the last one
245
- var v = n[i] || n[n.length - 1];
246
-
247
- var dv = v.cloneNode(true);
248
- v.parentNode.insertBefore(dv);
249
- return c.concat([dv])
250
- }, []);
251
-
252
- node.forEach(function (o) {
253
- o.parentNode.removeChild(o);
254
- });
255
-
256
- return collection;
257
- },
258
-
259
- repeat: function(node, data, cb) {
260
- pw.node.invokeWithData(pw.node.match(node, data), data, cb);
261
- },
262
-
263
- // binds an object to a node
264
- bind: function (data, node, cb) {
265
- var scope = pw.node.findBindings(node)[0];
266
-
267
- pw.node.invokeWithData(node, data, function(dm) {
268
- if (!dm) {
269
- return;
270
- }
271
-
272
- if(dm.id) {
273
- this.setAttribute('data-id', dm.id);
274
- }
275
-
276
- pw.node.bindDataToScope(dm, scope, node);
277
-
278
- if(!(typeof cb === 'undefined')) {
279
- cb.call(this, dm);
280
- }
281
- });
282
- },
283
-
284
- apply: function (data, node, cb) {
285
- var c = pw.node.match(node, data);
286
- pw.node.bind(data, c, cb);
287
- return c;
288
- },
289
-
290
- findBindings: function (node) {
291
- var bindings = [];
292
- pw.node.breadthFirst(node, function() {
293
- var o = this;
294
-
295
- var scope = o.getAttribute('data-scope');
296
-
297
- if(!scope) {
298
- return;
299
- }
300
-
301
- var props = [];
302
- pw.node.breadthFirst(o, function() {
303
- var so = this;
304
-
305
- // don't go into deeper scopes
306
- if(o != so && so.getAttribute('data-scope')) {
307
- return;
308
- }
309
-
310
- var prop = so.getAttribute('data-prop');
311
-
312
- if(!prop) {
313
- return;
314
- }
315
-
316
- props.push({
317
- prop: prop,
318
- doc: so
319
- });
320
- });
321
-
322
- bindings.push({
323
- scope: scope,
324
- props: props,
325
- doc: o,
326
- });
327
- });
328
-
329
- return bindings;
330
- },
331
-
332
- bindDataToScope: function (data, scope, node) {
333
- if(!data || !scope) {
334
- return;
335
- }
336
-
337
- scope['props'].forEach(function (p) {
338
- k = p['prop'];
339
- v = data[k];
340
-
341
- if(!v) {
342
- v = '';
343
- }
344
-
345
- if(typeof v === 'object') {
346
- pw.node.bindValueToNode(v['__content'], p['doc']);
347
- pw.node.bindAttributesToNode(v['__attrs'], p['doc']);
348
- } else {
349
- pw.node.bindValueToNode(v, p['doc']);
350
- }
351
- });
352
- },
353
-
354
- bindAttributesToNode: function (attrs, node) {
355
- var nAtrs = pw.attrs.init(pw.view.init(node));
356
-
357
- for(var attr in attrs) {
358
- var v = attrs[attr];
359
- if(typeof v === 'function') {
360
- v = v.call(node.getAttribute(attr));
361
- }
362
-
363
- if (v) {
364
- if (v instanceof Array) {
365
- v.forEach(function (attrInstruction) {
366
- nAtrs[attrInstruction[0]](attr, attrInstruction[1]);
367
- });
368
- } else {
369
- nAtrs.set(attr, v);
370
- }
371
- } else {
372
- nAtrs.remove(attr);
373
- }
374
- }
375
- },
376
-
377
- bindValueToNode: function (value, node) {
378
- if(pw.node.isTagWithoutValue(node)) {
379
- return;
380
- }
381
-
382
- //TODO handle other form fields (port from pakyow-presenter)
383
- if (node.tagName === 'INPUT' && node.type === 'checkbox') {
384
- if (value === true || (node.value && value === node.value)) {
385
- node.checked = true;
386
- } else {
387
- node.checked = false;
388
- }
389
- } else if (node.tagName === 'TEXTAREA' || pw.node.isSelfClosingTag(node)) {
390
- node.value = value;
391
- } else {
392
- if (value) {
393
- node.innerHTML = value;
394
- }
395
- }
396
- },
397
-
398
- isTagWithoutValue: function(node) {
399
- return valuelessTags.indexOf(node.tagName) != -1 ? true : false;
400
- },
401
-
402
- isSelfClosingTag: function(node) {
403
- return selfClosingTags.indexOf(node.tagName) != -1 ? true : false;
404
- },
405
-
406
- breadthFirst: function (node, cb) {
407
- var queue = [node];
408
- while (queue.length > 0) {
409
- var subNode = queue.shift();
410
- if (!subNode) continue;
411
- if(typeof subNode == "object" && "nodeType" in subNode && subNode.nodeType === 1 && subNode.cloneNode) {
412
- cb.call(subNode);
413
- }
414
-
415
- var children = subNode.childNodes;
416
- if (children) {
417
- for(var i = 0; i < children.length; i++) {
418
- queue.push(children[i]);
419
- }
420
- }
421
- }
422
- },
423
-
424
- isNodeList: function(nodes) {
425
- return typeof nodes.length !== 'undefined';
426
- },
427
-
428
- byAttr: function (node, attr, value) {
429
- return pw.node.all(node).filter(function (o) {
430
- var ov = o.getAttribute(attr);
431
- return ov !== null && ((typeof value) === 'undefined' || ov == value);
432
- });
433
- },
434
-
435
- setAttr: function (node, attr, value) {
436
- if (attr === 'style') {
437
- value.pairs().forEach(function (kv) {
438
- node.style[kv[0]] = kv[1];
439
- });
440
- } else {
441
- if (attr === 'class') {
442
- value = value.join(' ');
443
- }
444
-
445
- if (attr === 'checked') {
446
- if (value) {
447
- value = 'checked';
448
- } else {
449
- value = '';
450
- }
451
-
452
- node.checked = value;
453
- }
454
-
455
- node.setAttribute(attr, value);
456
- }
457
- },
458
-
459
- all: function (node) {
460
- var arr = [];
461
-
462
- if (!node) {
463
- return arr;
464
- }
465
-
466
- if(document !== node) {
467
- arr.push(node);
468
- }
469
-
470
- return arr.concat(pw.node.toA(node.getElementsByTagName('*')));
471
- },
472
-
473
- before: function (node, newNode) {
474
- node.parentNode.insertBefore(newNode, node);
475
- },
476
-
477
- after: function (node, newNode) {
478
- node.parentNode.insertBefore(newNode, this.nextSibling);
479
- },
480
-
481
- replace: function (node, newNode) {
482
- node.parentNode.replaceChild(newNode, node);
483
- },
484
-
485
- append: function (node, newNode) {
486
- node.appendChild(newNode);
487
- },
488
-
489
- prepend: function (node, newNode) {
490
- node.insertBefore(newNode, node.firstChild);
491
- },
492
-
493
- remove: function (node) {
494
- node.parentNode.removeChild(node);
495
- },
496
-
497
- clear: function (node) {
498
- while (node.firstChild) {
499
- pw.node.remove(node.firstChild);
500
- }
501
- },
502
-
503
- title: function (node, value) {
504
- var titleNode;
505
- if (titleNode = node.getElementsByTagName('title')[0]) {
506
- titleNode.innerText = value;
507
- }
508
- },
509
-
510
- toA: function (nodeSet) {
511
- return Array.prototype.slice.call(nodeSet);
512
- },
513
-
514
- serialize: function (node) {
515
- var json = {};
516
- var working;
517
- var value;
518
- var split, last;
519
- var previous, previous_name;
520
- node.querySelectorAll('input, select, textarea').forEach(function (input) {
521
- working = json;
522
- split = input.name.split('[');
523
- last = split[split.length - 1];
524
- split.forEach(function (name) {
525
- value = pw.node.value(input);
526
-
527
- if (name == ']') {
528
- if (!(previous[previous_name] instanceof Array)) {
529
- previous[previous_name] = [];
530
- }
531
-
532
- if (value) {
533
- previous[previous_name].push(value);
534
- }
535
- }
536
-
537
- if (name != last) {
538
- value = {};
539
- }
540
-
541
- name = name.replace(']', '');
542
-
543
- if (name == '' || name == '_method') {
544
- return;
545
- }
546
-
547
- if (!working[name]) {
548
- working[name] = value;
549
- }
550
-
551
- previous = working;
552
- previous_name = name;
553
- working = working[name];
554
- });
555
- });
556
-
557
- return json;
558
- }
559
- };
560
- pw.attrs = {
561
- init: function (v_or_vs) {
562
- return new pw_Attrs(pw.collection.init(v_or_vs));
563
- }
564
- };
565
-
566
- var attrTypes = {
567
- hash: ['style'],
568
- bool: ['selected', 'checked', 'disabled', 'readonly', 'multiple'],
569
- mult: ['class']
570
- };
571
-
572
- var pw_Attrs = function (collection) {
573
- this.views = collection.views;
574
- };
575
-
576
- pw_Attrs.prototype = {
577
- findType: function (attr) {
578
- if (attrTypes.hash.indexOf(attr) > -1) return 'hash';
579
- if (attrTypes.bool.indexOf(attr) > -1) return 'bool';
580
- if (attrTypes.mult.indexOf(attr) > -1) return 'mult';
581
- return 'text';
582
- },
583
-
584
- findValue: function (view, attr) {
585
- switch (attr) {
586
- case 'class':
587
- return view.node.classList;
588
- case 'style':
589
- return view.node.style;
590
- }
591
-
592
- if (this.findType(attr) === 'bool') {
593
- return view.node.hasAttribute(attr);
594
- } else {
595
- return view.node.getAttribute(attr);
596
- }
597
- },
598
-
599
- set: function (attr, value) {
600
- this.views.forEach(function (view) {
601
- pw.node.setAttr(view.node, attr, value);
602
- });
603
- },
604
-
605
- remove: function (attr) {
606
- this.views.forEach(function (view) {
607
- view.node.removeAttribute(attr);
608
- });
609
- },
610
-
611
- ensure: function (attr, value) {
612
- this.views.forEach(function (view) {
613
- var currentValue = this.findValue(view, attr);
614
-
615
- if (attr === 'class') {
616
- if (!currentValue.contains(value)) {
617
- currentValue.add(value);
618
- }
619
- } else if (attr === 'style') {
620
- value.pairs().forEach(function (kv) {
621
- view.node.style[kv[0]] = kv[1];
622
- });
623
- } else if (this.findType(attr) === 'bool') {
624
- if (!view.node.hasAttribute(attr)) {
625
- pw.node.setAttr(view.node, attr, attr);
626
- }
627
- } else { // just a text attr
628
- var currentValue = view.node.getAttribute(attr) || '';
629
- if (!currentValue.match(value)) {
630
- pw.node.setAttr(view.node, attr, currentValue + value);
631
- }
632
- }
633
- }, this);
634
- },
635
-
636
- deny: function (attr, value) {
637
- this.views.forEach(function (view) {
638
- var currentValue = this.findValue(view, attr);
639
- if (attr === 'class') {
640
- if (currentValue.contains(value)) {
641
- currentValue.remove(value);
642
- }
643
- } else if (attr === 'style') {
644
- value.pairs().forEach(function (kv) {
645
- view.node.style[kv[0]] = view.node.style[kv[0]].replace(kv[1], '');
646
- });
647
- } else if (this.findType(attr) === 'bool') {
648
- if (view.node.hasAttribute(attr)) {
649
- view.node.removeAttribute(attr);
650
- }
651
- } else { // just a text attr
652
- pw.node.setAttr(view.node, attr, view.node.getAttribute(attr).replace(value, ''));
653
- }
654
- }, this);
655
- },
656
-
657
- insert: function (attr, value) {
658
- this.views.forEach(function (view) {
659
- var currentValue = this.findValue(view, attr);
660
-
661
- switch (attr) {
662
- case 'class':
663
- currentValue.add(value);
664
- break;
665
- default:
666
- pw.node.setAttr(view.node, attr, currentValue + value);
667
- break;
668
- }
669
- }, this);
670
- }
671
- };
672
- /*
673
- State related functions.
674
- */
675
-
676
- pw.state = {
677
- build: function (sigArr, parentObj) {
678
- var nodeState;
679
- return sigArr.reduce(function (acc, sig) {
680
- if (nodeState = pw.state.buildForNode(sig, parentObj)) {
681
- acc.push(nodeState);
682
- }
683
-
684
- return acc;
685
- }, []);
686
- },
687
-
688
- buildForNode: function (sigTuple, parentObj) {
689
- var sig = sigTuple[0];
690
- var obj = {};
691
-
692
- if (sig.type === 'scope') {
693
- obj.id = sig.node.getAttribute('data-id');
694
- obj.scope = sig.node.getAttribute('data-scope');
695
- } else if (sig.type === 'prop' && parentObj) {
696
- parentObj[sig.node.getAttribute('data-prop')] = pw.node.value(sig.node);
697
- return;
698
- }
699
-
700
- obj['__nested'] = pw.state.build(sigTuple[1], obj);
701
-
702
- return obj;
703
- },
704
-
705
- // creates and returns a new pw_State for the document or node
706
- init: function (node, observer) {
707
- return new pw_State(node, observer);
708
- }
709
- };
710
-
711
-
712
- /*
713
- pw_State represents the state for a document or node.
714
- */
715
-
716
- var pw_State = function (node) {
717
- this.node = node;
718
- //FIXME storing diffs is probably better than full snapshots
719
- this.snapshots = [];
720
- this.update();
721
- }
722
-
723
- pw_State.prototype = {
724
- update: function () {
725
- this.snapshots.push(pw.state.build(pw.node.significant(this.node)));
726
- },
727
-
728
- // gets the current represented state from the node and diffs it with the current state
729
- diffNode: function (node) {
730
- if (node.hasAttribute('data-ui')) {
731
- return {
732
- '__nested': pw.state.build(pw.node.significant(node))
733
- };
734
- }
735
-
736
- return pw.state.build(pw.node.significant(pw.node.scope(node)))[0];
737
- },
738
-
739
- revert: function () {
740
- var initial = pw.util.dup(this.snapshots[0]);
741
- this.snapshots = [initial];
742
- return initial;
743
- },
744
-
745
- rollback: function () {
746
- this.snapshots.pop();
747
- return this.current();
748
- },
749
-
750
- // returns the current state for a node
751
- node: function (nodeState) {
752
- return this.current.flatten().find(function (state) {
753
- return state.scope === nodeState.scope && state.id === nodeState.id;
754
- });
755
- },
756
-
757
- append: function (state) {
758
- var copy = this.copy();
759
- copy.push(state);
760
- this.snapshots.push(copy);
761
- },
762
-
763
- prepend: function (state) {
764
- var copy = this.copy();
765
- copy.unshift(state);
766
- this.snapshots.push(copy);
767
- },
768
-
769
- remove: function (state) {
770
- var copy = this.copy();
771
- var match = copy.find(function (s) {
772
- return s.id === state.id;
773
- });
774
-
775
- if (match) {
776
- copy.splice(copy.indexOf(match), 1);
777
- this.snapshots.push(copy);
778
- }
779
- },
780
-
781
- copy: function () {
782
- return pw.util.dup(this.current());
783
- },
784
-
785
- current: function () {
786
- return this.snapshots[this.snapshots.length - 1];
787
- },
788
-
789
- initial: function () {
790
- return this.snapshots[0];
791
- }
792
- };
793
- /*
794
- View related functions.
795
- */
796
-
797
- pw.view = {
798
- // creates and returns a new pw_View for the document or node
799
- init: function (node) {
800
- return new pw_View(node);
801
- },
802
-
803
- fromStr: function (str) {
804
- var nodeType = 'div';
805
-
806
- if (str.match(/^<tr/) || str.match(/^<tbody/)) {
807
- nodeType = 'table';
808
- }
809
-
810
- var e = document.createElement(nodeType);
811
- e.innerHTML = str;
812
-
813
- return pw.view.init(e.childNodes[0]);
814
- }
815
- };
816
-
817
- /*
818
- pw_View contains a document with state. It watches for
819
- interactions with the document that trigger mutations
820
- in state. It can also apply state to the view.
821
- */
822
-
823
- var pw_View = function (node) {
824
- this.node = node;
825
- }
826
-
827
- pw_View.prototype = {
828
- clone: function () {
829
- return pw.view.init(this.node.cloneNode(true));
830
- },
831
-
832
- // pakyow api
833
-
834
- title: function (value) {
835
- pw.node.title(this.node, value);
836
- },
837
-
838
- text: function (value) {
839
- this.node.innerText = value;
840
- },
841
-
842
- html: function (value) {
843
- this.node.innerHTML = value
844
- },
845
-
846
- component: function (name) {
847
- return pw.collection.init(
848
- pw.node.byAttr(this.node, 'data-ui', name).reduce(function (views, node) {
849
- return views.concat(pw.view.init(node));
850
- }, []), this);
851
- },
852
-
853
- attrs: function () {
854
- return pw.attrs.init(this);
855
- },
856
-
857
- invoke: function (cb) {
858
- pw.node.invoke(this.node, cb);
859
- },
860
-
861
- match: function (data) {
862
- pw.node.match(this.node, data);
863
- },
864
-
865
- invokeWithData: function (data, cb) {
866
- pw.node.invokeWithData(this.node, data, cb);
867
- },
868
-
869
- repeat: function (data, cb) {
870
- pw.node.repeat(this.node, data, cb);
871
- },
872
-
873
- bind: function (data, cb) {
874
- pw.node.bind(data, this.node, cb);
875
- },
876
-
877
- apply: function (data, cb) {
878
- pw.node.apply(data, this.node, cb);
879
- },
880
-
881
- use: function (version, cb) {
882
- var self = this;
883
-
884
- if (this.node.getAttribute('data-version') != version) {
885
- this.node.setAttribute('data-version', version);
886
-
887
- var lookup = {
888
- scope: this.node.getAttribute('data-scope'),
889
- version: version
890
- };
891
-
892
- window.socket.fetchView(lookup, function (view) {
893
- view.node.setAttribute('data-channel', self.node.getAttribute('data-channel'));
894
- pw.node.replace(self.node, view.node);
895
- self.node = view.node;
896
- cb();
897
- });
898
- } else {
899
- cb();
900
- }
901
- },
902
-
903
- setEndpoint: function (endpoint) {
904
- this.endpoint = endpoint;
905
- return this;
906
- },
907
-
908
- first: function () {
909
- return this;
910
- },
911
-
912
- length: function () {
913
- return 1;
914
- }
915
- };
916
-
917
- // pass through lookups
918
- ['scope', 'prop'].forEach(function (method) {
919
- pw_View.prototype[method] = function (name) {
920
- return pw.collection.init(
921
- pw.node.byAttr(this.node, 'data-' + method, name).reduce(function (views, node) {
922
- return views.concat(pw.view.init(node));
923
- }, []), this, name);
924
- };
925
- });
926
-
927
- // pass through functions without view
928
- ['remove', 'clear', 'versionName'].forEach(function (method) {
929
- pw_View.prototype[method] = function () {
930
- return pw.node[method](this.node);
931
- };
932
- });
933
-
934
- // pass through functions with view
935
- ['after', 'before', 'replace', 'append', 'prepend', 'insert'].forEach(function (method) {
936
- pw_View.prototype[method] = function (view) {
937
- return pw.node[method](this.node, view.node);
938
- };
939
- });
940
- pw.collection = {
941
- init: function (view_or_views, parent, scope) {
942
- if (view_or_views instanceof pw_Collection) {
943
- return view_or_views
944
- } else if (view_or_views.constructor !== Array) {
945
- view_or_views = [view_or_views];
946
- }
947
-
948
- return new pw_Collection(view_or_views, parent, scope);
949
- },
950
-
951
- fromNodes: function (nodes, parent, scope) {
952
- return pw.collection.init(nodes.map(function (node) {
953
- return pw.view.init(node);
954
- }), parent, scope);
955
- }
956
- };
957
-
958
- var pw_Collection = function (views, parent, scope) {
959
- this.views = views;
960
- this.parent = parent;
961
- this._scope = scope;
962
- };
963
-
964
- pw_Collection.prototype = {
965
- clone: function () {
966
- return pw.collection.init(this.views.map(function (view) {
967
- return view.clone();
968
- }));
969
- },
970
-
971
- last: function () {
972
- return this.views[this.length() - 1];
973
- },
974
-
975
- first: function () {
976
- return this.views[0];
977
- },
978
-
979
- removeView: function(view) {
980
- var index = this.views.indexOf(view);
981
-
982
- if (index > -1) {
983
- this.views.splice(index, 1)[0].remove();
984
- }
985
- },
986
-
987
- addView: function(view_or_views) {
988
- var views = [];
989
-
990
- if (view_or_views instanceof pw_Collection) {
991
- views = view_or_views.views;
992
- } else {
993
- views.push(view_or_views);
994
- }
995
-
996
- if (this.length() > 0) {
997
- views.forEach(function (view) {
998
- pw.node.after(this.last().node, view.node);
999
- }, this);
1000
- } else if (this.parent) {
1001
- views.forEach(function (view) {
1002
- this.parent.append(view);
1003
- }, this);
1004
- }
1005
-
1006
- this.views = this.views.concat(views);
1007
- },
1008
-
1009
- order: function (orderedIds) {
1010
- orderedIds.forEach(function (id) {
1011
- if (!id) {
1012
- return;
1013
- }
1014
-
1015
- var match = this.views.find(function (view) {
1016
- return view.node.getAttribute('data-id') == id.toString();
1017
- });
1018
-
1019
- if (match) {
1020
- match.node.parentNode.appendChild(match.node);
1021
-
1022
- // also reorder the list of views
1023
- var i = this.views.indexOf(match);
1024
- this.views.splice(i, 1);
1025
- this.views.push(match);
1026
- }
1027
- }, this);
1028
- },
1029
-
1030
- length: function () {
1031
- return this.views.length;
1032
- },
1033
-
1034
- // pakyow api
1035
-
1036
- attrs: function () {
1037
- return pw.attrs.init(this.views);
1038
- },
1039
-
1040
- append: function (data) {
1041
- data = Array.ensure(data);
1042
-
1043
- var last = this.last();
1044
- this.views.push(last.append(data));
1045
- return last;
1046
- },
1047
-
1048
- prepend: function(data) {
1049
- data = Array.ensure(data);
1050
-
1051
- var prependedViews = data.map(function (datum) {
1052
- var view = this.first().prepend(datum);
1053
- this.views.push(view);
1054
- return view;
1055
- }, this);
1056
-
1057
- return pw.collection.init(prependedViews);
1058
- },
1059
-
1060
- invoke: function (cb) {
1061
- pw.node.invoke(this.views, cb);
1062
- },
1063
-
1064
- invokeWithData: function(data, fn) {
1065
- data = Array.ensure(data);
1066
-
1067
- this.views.forEach(function (view, i) {
1068
- fn.call(view, data[i]);
1069
- });
1070
- },
1071
-
1072
- match: function (data, fn) {
1073
- data = Array.ensure(data);
1074
-
1075
- if (data.length === 0) {
1076
- this.remove();
1077
- return fn.call(this);
1078
- } else {
1079
- var firstView;
1080
- var firstParent;
1081
-
1082
- if (this.views[0]) {
1083
- firstView = this.views[0].clone();
1084
- firstParent = this.views[0].node.parentNode;
1085
- }
1086
-
1087
- this.views.slice(0).forEach(function (view) {
1088
- var id = view.node.getAttribute('data-id');
1089
-
1090
- if (!id && data[0].id) {
1091
- this.removeView(view);
1092
- return;
1093
- } else if (id) {
1094
- if (!data.find(function (datum) { return datum.id && datum.id.toString() === id })) {
1095
- this.removeView(view);
1096
- }
1097
- }
1098
- }, this);
1099
-
1100
- if (data.length > this.length()) {
1101
- var self = this;
1102
- this.endpoint.template(this, function (view) {
1103
- if (!view) {
1104
- view = firstView.clone();
1105
- self.parent = pw.view.init(firstParent);
1106
- }
1107
-
1108
- data.forEach(function (datum) {
1109
- if (!self.views.find(function (view) {
1110
- return view.node.getAttribute('data-id') === (datum.id || '').toString()
1111
- })) {
1112
- var viewToAdd = view.clone();
1113
-
1114
- if (viewToAdd instanceof pw_Collection) {
1115
- viewToAdd = viewToAdd.views[0];
1116
- }
1117
-
1118
- viewToAdd.node.setAttribute('data-id', datum.id);
1119
- self.addView(viewToAdd);
1120
-
1121
- pw.component.findAndInit(viewToAdd.node);
1122
- }
1123
- }, self);
1124
-
1125
- return fn.call(self);
1126
- });
1127
- } else {
1128
- return fn.call(this);
1129
- }
1130
- }
1131
-
1132
- return this;
1133
- },
1134
-
1135
- repeat: function (data, fn) {
1136
- this.match(data, function () {
1137
- this.invokeWithData(data, fn);
1138
- });
1139
- },
1140
-
1141
- bind: function (data, fn) {
1142
- this.invokeWithData(data, function(datum) {
1143
- this.bind(datum);
1144
-
1145
- if(!(typeof fn === 'undefined')) {
1146
- fn.call(this, datum);
1147
- }
1148
- });
1149
-
1150
- return this;
1151
- },
1152
-
1153
- apply: function (data, fn) {
1154
- this.match(data, function () {
1155
- var id;
1156
-
1157
- this.order(data.map(function (datum) {
1158
- if (id = datum.id) {
1159
- return id.toString();
1160
- }
1161
- }));
1162
-
1163
- this.bind(data, fn);
1164
- });
1165
- },
1166
-
1167
- version: function (data, fn) {
1168
- var self = this;
1169
- this.match(data, function () {
1170
- this.invokeWithData(data, fn);
1171
- });
1172
- },
1173
-
1174
- setEndpoint: function (endpoint) {
1175
- this.endpoint = endpoint;
1176
- return this;
1177
- }
1178
- };
1179
-
1180
- // lookup functions
1181
- ['scope', 'prop', 'component'].forEach(function (method) {
1182
- pw_Collection.prototype[method] = function (name) {
1183
- return pw.collection.init(
1184
- this.views.reduce(function (views, view) {
1185
- return views.concat(view[method](name).views);
1186
- }, [])
1187
- );
1188
- };
1189
- });
1190
-
1191
- // pass through functions
1192
- ['remove', 'clear', 'text', 'html'].forEach(function (method) {
1193
- pw_Collection.prototype[method] = function (arg) {
1194
- this.views.forEach(function (view) {
1195
- view[method](arg);
1196
- });
1197
- };
1198
- });
1199
- /*
1200
- Component init.
1201
- */
1202
-
1203
- pw.init.register(function () {
1204
- pw.component.findAndInit(document.querySelectorAll('body')[0]);
1205
- });
1206
-
1207
- /*
1208
- Component related functions.
1209
- */
1210
-
1211
- // stores component functions by name
1212
- var components = {};
1213
-
1214
- // stores component instances by channel
1215
- var channelComponents = {};
1216
- var channelBroadcasts = {};
1217
-
1218
- // component instances
1219
- var componentInstances = {};
1220
-
1221
- pw.component = {
1222
- init: function (view, config) {
1223
- return new pw_Component(view, config);
1224
- },
1225
-
1226
- resetChannels: function () {
1227
- channelComponents = {};
1228
- },
1229
-
1230
- findAndInit: function (node) {
1231
- pw.node.byAttr(node, 'data-ui').forEach(function (uiNode) {
1232
- if (uiNode._ui) {
1233
- return;
1234
- }
1235
-
1236
- var name = uiNode.getAttribute('data-ui');
1237
- var cfn = components[name] || pw.component.init;
1238
-
1239
- if (!componentInstances[name]) {
1240
- componentInstances[name] = [];
1241
- }
1242
-
1243
- var channel = uiNode.getAttribute('data-channel');
1244
- var config = uiNode.getAttribute('data-config');
1245
- var view = pw.view.init(uiNode);
1246
- var id = componentInstances[name].length;
1247
-
1248
- var component = new cfn(view, pw.component.buildConfigObject(config), name, id);
1249
- component.init(view, config, name);
1250
-
1251
- pw.component.registerForChannel(component, channel);
1252
- componentInstances[name].push(component);
1253
-
1254
- uiNode._ui = true;
1255
- });
1256
- },
1257
-
1258
- push: function (packet) {
1259
- var channel = packet.channel;
1260
- var payload = packet.payload;
1261
- var instruct = payload.instruct;
1262
-
1263
- (channelComponents[channel] || []).forEach(function (component) {
1264
- if (instruct) {
1265
- component.instruct(channel, instruct);
1266
- } else {
1267
- component.message(channel, payload);
1268
- }
1269
- });
1270
- },
1271
-
1272
- register: function (name, fn) {
1273
- var proto = pw_Component.prototype;
1274
-
1275
- Object.getOwnPropertyNames(proto).forEach(function (method) {
1276
- fn.prototype[method] = proto[method];
1277
- });
1278
-
1279
- components[name] = fn;
1280
- },
1281
-
1282
- buildConfigObject: function(configString) {
1283
- if (!configString) {
1284
- return {};
1285
- }
1286
-
1287
- return configString.split(';').reduce(function (config, option) {
1288
- var kv = option.trim().split(':');
1289
- config[kv[0].trim()] = kv[1].trim();
1290
- return config;
1291
- }, {});
1292
- },
1293
-
1294
- registerForChannel: function (component, channel) {
1295
- // store component instance by channel for messaging
1296
- if (!channelComponents[channel]) {
1297
- channelComponents[channel] = [];
1298
- }
1299
-
1300
- channelComponents[channel].push(component);
1301
- },
1302
-
1303
- registerForBroadcast: function (channel, cb, component) {
1304
- if (!channelBroadcasts[channel]) {
1305
- channelBroadcasts[channel] = [];
1306
- }
1307
-
1308
- channelBroadcasts[channel].push([cb, component]);
1309
- },
1310
-
1311
- deregisterForBroadcast: function (channel, component) {
1312
- var components = channelBroadcasts[channel];
1313
-
1314
- var instanceTuple = components.find(function (tuple) {
1315
- return tuple[1] == component;
1316
- });
1317
-
1318
- var i = components.indexOf(instanceTuple);
1319
- components.splice(i, 1);
1320
- },
1321
-
1322
- broadcast: function (channel, payload) {
1323
- (channelBroadcasts[channel] || []).forEach(function (cbTuple) {
1324
- cbTuple[0].call(cbTuple[1], payload);
1325
- });
1326
- }
1327
- };
1328
-
1329
- /*
1330
- pw_Component makes it possible to build custom controls.
1331
- */
1332
-
1333
- var pw_Component = function (view, config, name) {
1334
- // placeholder
1335
- };
1336
-
1337
- pw_Component.prototype = {
1338
- init: function (view, config, name) {
1339
- var node = view.node;
1340
- this.view = view;
1341
- this.node = node;
1342
- this.config = config;
1343
- this.name = name;
1344
- this.templates = {};
1345
- var self = this;
1346
-
1347
- // setup templates
1348
- pw.node.toA(node.querySelectorAll(':scope > *[data-template]')).forEach(function (templateNode) {
1349
- var cloned = templateNode.cloneNode(true);
1350
- pw.node.remove(templateNode);
1351
-
1352
- var scope = cloned.getAttribute('data-scope');
1353
-
1354
- if (this.templates[scope]) {
1355
- this.templates[scope].views.push(pw.view.init(cloned));
1356
- } else {
1357
- this.templates[scope] = pw.collection.init(pw.view.init(cloned));
1358
- }
1359
-
1360
- cloned.removeAttribute('data-template');
1361
- }, this);
1362
-
1363
- // setup our initial state
1364
- this.state = pw.state.init(this.node);
1365
-
1366
- // register as a dependent to the parent component
1367
- if (this.dCb) {
1368
- var parentComponent = pw.node.component(this.node.parentNode);
1369
-
1370
- if (parentComponent) {
1371
- parentComponent.addEventListener('mutated', function (evt) {
1372
- self.transform(self.dCb(evt.target._evtData));
1373
- });
1374
-
1375
- self.transform(self.dCb(pw.state.init(parentComponent).current()));
1376
- }
1377
- }
1378
-
1379
- // make it mutable
1380
- var mutableCb = function (evt) {
1381
- var scope = pw.node.scope(evt.target);
1382
-
1383
- if (scope) {
1384
- evt.preventDefault();
1385
- self.mutated(scope);
1386
- }
1387
- };
1388
-
1389
- node.addEventListener('submit', mutableCb);
1390
- node.addEventListener('change', function (evt) {
1391
- if (!pw.node.inForm(evt.target)) {
1392
- mutableCb(evt);
1393
- }
1394
- });
1395
-
1396
- //TODO define other mutable things
1397
-
1398
- if (this.inited) {
1399
- this.inited();
1400
- }
1401
- },
1402
-
1403
- listen: function (channel, cb) {
1404
- pw.component.registerForBroadcast(channel, cb, this);
1405
- },
1406
-
1407
- ignore: function (channel) {
1408
- pw.component.deregisterForBroadcast(channel, this);
1409
- },
1410
-
1411
- // Bubbles an event up to a parent component. Intended to be used
1412
- // as an alternative to `broadcast` in cases where child components
1413
- // have an impact on their parents.
1414
- bubble: function (channel, payload) {
1415
- var parentComponent = pw.node.component(this.node.parentNode);
1416
-
1417
- (channelBroadcasts[channel] || []).forEach(function (cbTuple) {
1418
- if (cbTuple[1].node == parentComponent) {
1419
- cbTuple[0].call(cbTuple[1], payload);
1420
- }
1421
- });
1422
- },
1423
-
1424
- // Trickles an event down to child components. Intended to be used
1425
- // as an alternative to `broadcast` in cases where parent components
1426
- // have an impact on their children.
1427
- trickle: function (channel, payload) {
1428
- var channels = (channelBroadcasts[channel] || []);
1429
- pw.node.toA(this.node.getElementsByTagName('*')).forEach(function (node) {
1430
- channels.forEach(function (cbTuple) {
1431
- if (cbTuple[1].node == node) {
1432
- cbTuple[0].call(cbTuple[1], payload);
1433
- }
1434
- });
1435
- })
1436
- },
1437
-
1438
- //TODO this is pretty similary to processing instructions
1439
- // for views in that we also have to handle the empty case
1440
- //
1441
- // there might be an opportunity for some refactoring
1442
- instruct: function (channel, instructions) {
1443
- this.endpoint = pw.instruct;
1444
-
1445
- var current = this.state.current();
1446
- if (current.length === 1) {
1447
- var view = this.view.scope(current[0].scope);
1448
- var node = view.views[0].node;
1449
- if (node.getAttribute('data-version') === 'empty') {
1450
- var self = this;
1451
- pw.instruct.template(view, function (rview) {
1452
- var parent = node.parentNode;
1453
- parent.replaceChild(rview.node, node);
1454
-
1455
- instructions.forEach(function (instruction) {
1456
- self[instruction[0]](instruction[1]);
1457
- });
1458
- });
1459
-
1460
- return;
1461
- }
1462
- }
1463
-
1464
- instructions.forEach(function (instruction) {
1465
- this[instruction[0]](instruction[1]);
1466
- }, this);
1467
- },
1468
-
1469
- message: function (channel, payload) {
1470
- // placeholder
1471
- },
1472
-
1473
- mutated: function (node) {
1474
- this.mutation(this.state.diffNode(node));
1475
- this.state.update();
1476
-
1477
- pw.node.trigger('mutated', this.node, this.state.current());
1478
- },
1479
-
1480
- mutation: function (mutation) {
1481
- // placeholder
1482
- },
1483
-
1484
- transform: function (state) {
1485
- this._transform(state);
1486
- },
1487
-
1488
- _transform: function (state) {
1489
- if (!state) {
1490
- return;
1491
- }
1492
-
1493
- if (state.length > 0) {
1494
- this.view.scope(state[0].scope).setEndpoint(this.endpoint || this).apply(state);
1495
- } else {
1496
- pw.node.breadthFirst(this.view.node, function () {
1497
- if (this.hasAttribute('data-scope')) {
1498
- pw.node.remove(this);
1499
- }
1500
- });
1501
- }
1502
-
1503
- pw.node.trigger('mutated', this.node, this.state.current());
1504
- },
1505
-
1506
- revert: function () {
1507
- this.transform(this.state.revert());
1508
- },
1509
-
1510
- rollback: function () {
1511
- this.transform(this.state.rollback());
1512
- },
1513
-
1514
- template: function (view, cb) {
1515
- var template;
1516
-
1517
- if (template = this.templates[view.scope]) {
1518
- cb(template);
1519
- }
1520
- },
1521
-
1522
- remove: function (data) {
1523
- this.state.remove(data);
1524
- this.transform(this.state.current());
1525
- },
1526
-
1527
- append: function (data) {
1528
- this.state.append(data);
1529
- this.transform(this.state.current());
1530
- },
1531
-
1532
- prepend: function (data) {
1533
- this.state.prepend(data);
1534
- this.transform(this.state.current());
1535
- },
1536
-
1537
- parent: function () {
1538
- var parent = pw.node.scope(this.node);
1539
-
1540
- if (parent) {
1541
- return pw.state.init(parent).current()[0];
1542
- }
1543
- },
1544
-
1545
- dependent: function (cb) {
1546
- this.dCb = cb;
1547
- }
1548
- };
1549
- /*
1550
- Socket init.
1551
- */
1552
-
1553
- pw.init.register(function () {
1554
- pw.socket.init({
1555
- cb: function (socket) {
1556
- window.socket = socket;
1557
- pw.component.broadcast('socket:available');
1558
- }
1559
- });
1560
- });
1561
-
1562
- /*
1563
- Socket related functions.
1564
- */
1565
-
1566
- pw.socket = {
1567
- init: function (options) {
1568
- return pw.socket.connect(
1569
- options.host,
1570
- options.port,
1571
- options.protocol,
1572
- options.connId,
1573
- options.cb
1574
- );
1575
- },
1576
-
1577
- connect: function (host, port, protocol, connId, cb) {
1578
- if(typeof host === 'undefined') host = window.location.hostname;
1579
- if(typeof port === 'undefined') port = window.location.port;
1580
- if(typeof protocol === 'undefined') protocol = window.location.protocol;
1581
- if(typeof connId === 'undefined') connId = document.getElementsByTagName('body')[0].getAttribute('data-socket-connection-id');
1582
-
1583
- if (!connId) {
1584
- return;
1585
- }
1586
-
1587
- var wsUrl = '';
1588
-
1589
- if (protocol === 'http:') {
1590
- wsUrl += 'ws://';
1591
- } else if (protocol === 'https:') {
1592
- wsUrl += 'wss://';
1593
- }
1594
-
1595
- wsUrl += host;
1596
-
1597
- if (port) {
1598
- wsUrl += ':' + port;
1599
- }
1600
-
1601
- wsUrl += '/?socket_connection_id=' + connId;
1602
-
1603
- return new pw_Socket(wsUrl, cb);
1604
- }
1605
- };
1606
-
1607
- var pw_Socket = function (url, cb) {
1608
- var self = this;
1609
-
1610
- this.callbacks = {};
1611
-
1612
- this.url = url;
1613
- this.initCb = cb;
1614
-
1615
- this.ws = new WebSocket(url);
1616
-
1617
- this.id = url.split('socket_connection_id=')[1];
1618
-
1619
- var pingInterval;
1620
-
1621
- this.ws.onmessage = function (evt) {
1622
- pw.component.broadcast('socket:loaded');
1623
-
1624
- var data = JSON.parse(evt.data);
1625
- if (data.id) {
1626
- var cb = self.callbacks[data.id];
1627
- if (cb) {
1628
- cb.call(this, data);
1629
- return;
1630
- }
1631
- }
1632
-
1633
- self.message(data);
1634
- };
1635
-
1636
- this.ws.onclose = function (evt) {
1637
- console.log('socket closed');
1638
- clearInterval(pingInterval);
1639
- self.reconnect();
1640
- };
1641
-
1642
- this.ws.onopen = function (evt) {
1643
- console.log('socket open');
1644
-
1645
- if(self.initCb) {
1646
- self.initCb(self);
1647
- }
1648
-
1649
- pingInterval = setInterval(function () {
1650
- self.send({ action: 'ping' });
1651
- }, 30000);
1652
- }
1653
- };
1654
-
1655
- pw_Socket.prototype = {
1656
- send: function (message, cb) {
1657
- pw.component.broadcast('socket:loading');
1658
-
1659
- message.id = pw.util.guid();
1660
- if (!message.input) {
1661
- message.input = {};
1662
- }
1663
- message.input.socket_connection_id = this.id;
1664
- this.callbacks[message.id] = cb;
1665
- this.ws.send(JSON.stringify(message));
1666
- },
1667
-
1668
- //TODO handle custom messages (e.g. not pakyow specific)
1669
- message: function (packet) {
1670
- console.log('received message');
1671
- console.log(packet);
1672
-
1673
- var selector = '*[data-channel="' + packet.channel + '"]';
1674
-
1675
- if (packet.channel && packet.channel.split(':')[0] === 'component') {
1676
- pw.component.push(packet);
1677
- return;
1678
- }
1679
-
1680
- var nodes = pw.node.toA(document.querySelectorAll(selector));
1681
-
1682
- if (nodes.length === 0) {
1683
- //TODO decide how to handle this condition; there are times where this
1684
- // is going to be the case and not an error; at one point we were simply
1685
- // reloading the page, but that doesn't work in all cases
1686
- return;
1687
- }
1688
-
1689
- pw.instruct.process(pw.collection.fromNodes(nodes, selector), packet, this);
1690
- },
1691
-
1692
- reconnect: function () {
1693
- var self = this;
1694
-
1695
- if (!self.wait) {
1696
- self.wait = 100;
1697
- } else {
1698
- self.wait *= 1.25;
1699
- }
1700
-
1701
- console.log('reconnecting socket in ' + self.wait + 'ms');
1702
-
1703
- setTimeout(function () {
1704
- pw.socket.init({ cb: self.initCb });
1705
- }, self.wait);
1706
- },
1707
-
1708
- fetchView: function (lookup, cb) {
1709
- var uri;
1710
-
1711
- if (window.location.hash) {
1712
- var arr = window.location.hash.split('#:')[1].split('/');
1713
- arr.shift();
1714
- uri = arr.join('/');
1715
- } else {
1716
- uri = window.location.pathname + window.location.search;
1717
- }
1718
-
1719
- this.send({
1720
- action: 'fetch-view',
1721
- lookup: lookup,
1722
- uri: uri
1723
- }, function (res) {
1724
- var view = pw.view.fromStr(res.body);
1725
-
1726
- if (view.node) {
1727
- view.node.removeAttribute('data-id');
1728
- cb(view);
1729
- } else {
1730
- cb();
1731
- }
1732
- });
1733
- }
1734
- };
1735
- pw.instruct = {
1736
- process: function (collection, packet, socket) {
1737
- if (collection.length() === 1 && collection.views[0].node.getAttribute('data-version') === 'empty') {
1738
- pw.instruct.fetchView(packet, socket, collection.views[0].node);
1739
- } else {
1740
- pw.instruct.perform(collection, packet.payload);
1741
- }
1742
- },
1743
-
1744
- fetchView: function (packet, socket, node) {
1745
- socket.fetchView({ channel: packet.channel }, function (view) {
1746
- if (view) {
1747
- var parent = node.parentNode;
1748
- parent.replaceChild(view.node, node);
1749
-
1750
- var selector = '*[data-channel="' + packet.channel + '"]';
1751
- var nodes = pw.node.toA(parent.querySelectorAll(selector));
1752
- pw.instruct.perform(pw.collection.fromNodes(nodes, selector), packet.payload);
1753
- } else {
1754
- console.log('trouble fetching view :(');
1755
- }
1756
- });
1757
- },
1758
-
1759
- // TODO: make this smart and cache results, invalidating
1760
- // if the websocket connection reconnects (since that means
1761
- // the server probably restarted)
1762
- template: function (view, cb) {
1763
- var lookup = {};
1764
-
1765
- if (!view || !view.first()) {
1766
- return cb();
1767
- }
1768
-
1769
- var node = view.first().node;
1770
-
1771
- if (node.hasAttribute('data-channel')) {
1772
- lookup.channel = view.first().node.getAttribute('data-channel');
1773
- } else if (node.hasAttribute('data-ui') && node.hasAttribute('data-scope')) {
1774
- lookup.component = pw.node.component(node).getAttribute('data-ui');
1775
- lookup.scope = node.getAttribute('data-scope');
1776
- } else {
1777
- cb();
1778
- return;
1779
- }
1780
-
1781
- window.socket.fetchView(lookup, function (view) {
1782
- cb(view);
1783
- });
1784
- },
1785
-
1786
- perform: function (collection, instructions, cb) {
1787
- var self = this;
1788
- instructions = instructions || [];
1789
-
1790
- function instruct (subject, instruction) {
1791
- var method = instruction[0];
1792
- var value = instruction[1];
1793
- var nested = instruction[2];
1794
-
1795
- // remap instructions to the ring name
1796
- if (method === 'with') {
1797
- method = 'invoke';
1798
- }
1799
-
1800
- if (method === 'for') {
1801
- method = 'invokeWithData';
1802
- }
1803
-
1804
- if (collection[method]) {
1805
- if (method == 'invoke' || method == 'invokeWithData' || method == 'bind' || method == 'repeat' || method == 'apply' || method == 'version') {
1806
- var cbLength = collection.length();
1807
- var cbCount = 0;
1808
- var nestedCb = function () {
1809
- cbCount++;
1810
-
1811
- if (cbCount == cbLength) {
1812
- next();
1813
- }
1814
- }
1815
- collection.setEndpoint(self)[method].call(collection, value, function (datum) {
1816
- pw.instruct.perform(this, nested[value.indexOf(datum)], nestedCb);
1817
- });
1818
- return;
1819
- } else if (method == 'attrs') {
1820
- self.performAttr(collection.attrs(), nested);
1821
- return;
1822
- } else if (method == 'use') {
1823
- collection.setEndpoint(self);
1824
- collection.use(value, next);
1825
- return;
1826
- } else {
1827
- var mutatedViews = collection[method].call(collection, value);
1828
- }
1829
- } else {
1830
- console.log('could not find method named: ' + method);
1831
- return;
1832
- }
1833
-
1834
- if (nested instanceof Array) {
1835
- pw.instruct.perform(mutatedViews, nested, next);
1836
- return;
1837
- } else if (mutatedViews) {
1838
- collection = mutatedViews;
1839
- }
1840
-
1841
- next();
1842
- };
1843
-
1844
- var i = 0;
1845
- function next() {
1846
- if (i < instructions.length) {
1847
- instruct(collection, instructions[i++]);
1848
- } else {
1849
- done();
1850
- }
1851
- };
1852
-
1853
- function done() {
1854
- if (cb) {
1855
- cb();
1856
- } else {
1857
- pw.component.findAndInit(collection.node);
1858
- }
1859
- };
1860
-
1861
- next();
1862
- },
1863
-
1864
- performAttr: function (context, attrInstructions) {
1865
- attrInstructions.forEach(function (attrInstruct) {
1866
- var attr = attrInstruct[0];
1867
- var value = attrInstruct[1];
1868
- var nested = attrInstruct[2];
1869
-
1870
- if (value) {
1871
- context.set(attr, value);
1872
- } else {
1873
- context[nested[0][0]](attr, nested[0][1]);
1874
- }
1875
- });
1876
- }
1877
- };
1878
- if (!Array.prototype.flatten) {
1879
- Array.prototype.flatten = function () {
1880
- return this.reduce(function (flat, toFlatten) {
1881
- return flat.concat(Array.isArray(toFlatten) ? toFlatten.flatten() : toFlatten);
1882
- }, []);
1883
- };
1884
- }
1885
-
1886
- if (!Array.prototype.find) {
1887
- Array.prototype.find = function(predicate) {
1888
- if (this == null) {
1889
- throw new TypeError('Array.prototype.find called on null or undefined');
1890
- }
1891
- if (typeof predicate !== 'function') {
1892
- throw new TypeError('predicate must be a function');
1893
- }
1894
- var list = Object(this);
1895
- var length = list.length >>> 0;
1896
- var thisArg = arguments[1];
1897
- var value;
1898
-
1899
- for (var i = 0; i < length; i++) {
1900
- value = list[i];
1901
- if (predicate.call(thisArg, value, i, list)) {
1902
- return value;
1903
- }
1904
- }
1905
- return undefined;
1906
- };
1907
- }
1908
-
1909
- Array.ensure = function (value) {
1910
- if(!(value instanceof Array)) {
1911
- return [value];
1912
- }
1913
-
1914
- return value
1915
- }
1916
-
1917
- NodeList.prototype.forEach = Array.prototype.forEach;
1918
- if (!Object.prototype.pairs) {
1919
- Object.defineProperty(Object.prototype, "pairs", {
1920
- value: function() {
1921
- return Object.keys(this).map(function (key) {
1922
- return [key, this[key]];
1923
- }, this);
1924
- },
1925
- enumerable: false
1926
- });
1927
- }
1928
-
1929
- if (typeof define === "function" && define.amd) {
1930
- define(pw);
1931
- } else if (typeof module === "object" && module.exports) {
1932
- module.exports = pw;
1933
- } else {
1934
- this.pw = pw;
1935
- }
1936
- })();