1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 | (function(){
|
8 | "use strict";
|
9 | var bubbles = {
|
10 |
|
11 | svgCapable: !!document.createElementNS && !!document.createElementNS("http://www.w3.org/2000/svg", "svg").createSVGRect,
|
12 |
|
13 |
|
14 | xhr: function(url, done, post) {
|
15 | var ctx = this, req = new XMLHttpRequest() || new ActiveXObject("Microsoft.XMLHTTP");
|
16 | req.onreadystatechange = function () {
|
17 | if(req.readyState === 4) {
|
18 | done.call(ctx, req.responseText);
|
19 | }
|
20 | };
|
21 |
|
22 | if (post) {
|
23 | post = JSON.stringify(post);
|
24 | req.open("POST", url, true);
|
25 | req.setRequestHeader("Content-type", "application/json");
|
26 | } else {
|
27 | req.open("GET", url, true);
|
28 | }
|
29 |
|
30 | req.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
31 | req.send(post || "");
|
32 | },
|
33 |
|
34 |
|
35 | data: function(url, done, post){
|
36 | bubbles.xhr(url, function(dataStr){
|
37 | try {
|
38 | done(JSON.parse(dataStr));
|
39 | } catch(e) {
|
40 | done({
|
41 | error: e
|
42 | });
|
43 | }
|
44 | }, post);
|
45 | },
|
46 |
|
47 | add: function () {
|
48 | var org = arguments[0];
|
49 | for(var t=1; t<arguments.length; t++){
|
50 | var add = arguments[t];
|
51 | for(var k in add) if(add.hasOwnProperty(k)) {
|
52 | org[k] = add[k];
|
53 | }
|
54 | }
|
55 | return org;
|
56 | },
|
57 |
|
58 |
|
59 | arrayify: function (obj) {
|
60 | if (Array.isArray(obj)) {
|
61 | return obj;
|
62 | } else {
|
63 | return [obj];
|
64 | }
|
65 | },
|
66 |
|
67 |
|
68 | que: function (context) {
|
69 | var self = this;
|
70 | var row = [];
|
71 |
|
72 | var next = function() {
|
73 | if (row.length) {
|
74 | new Promise(function(resolve, reject) {
|
75 | row.shift().call(context || self, resolve, reject);
|
76 | }).then(next);
|
77 | }
|
78 | };
|
79 |
|
80 | this.add = function(step) {
|
81 | if (step && step.call) {
|
82 | row.push(step);
|
83 | } else {
|
84 | console.error("bubbles.que, add: step not a function", step);
|
85 | }
|
86 |
|
87 | return self;
|
88 | };
|
89 |
|
90 | this.then = function(action) {
|
91 | self.add(action);
|
92 | next();
|
93 |
|
94 | return self;
|
95 | };
|
96 |
|
97 | return self;
|
98 | },
|
99 |
|
100 |
|
101 | all: function (context) {
|
102 | var self = this;
|
103 | var list = [];
|
104 |
|
105 | this.add = function(action) {
|
106 | if (action && action.call) {
|
107 | list.push(action);
|
108 | } else {
|
109 | console.error("bubbles.all, add: action not a function", action);
|
110 | }
|
111 |
|
112 | return self;
|
113 | };
|
114 |
|
115 | this.then = function(finalAction) {
|
116 | new Promise(function(resolve) {
|
117 | var count = list.length;
|
118 | if(count) {
|
119 | list.forEach(function(action) {
|
120 | action.call(context || self, function() {
|
121 |
|
122 | count--;
|
123 |
|
124 | if (count === 0) {
|
125 | resolve();
|
126 | }
|
127 | });
|
128 | });
|
129 | } else resolve();
|
130 | }).then(function() {
|
131 | if(finalAction && finalAction.call) {
|
132 | finalAction.call(context || self);
|
133 | }
|
134 | });
|
135 |
|
136 | return self;
|
137 | };
|
138 |
|
139 | return self;
|
140 | },
|
141 |
|
142 | elmApi: [
|
143 |
|
144 | {
|
145 | name: "tg",
|
146 | act: function () {
|
147 | this.domElm = document.createComment(this.elm.tg);
|
148 | this.ctx.tg[this.elm.tg] = this.domElm;
|
149 | }
|
150 | },
|
151 |
|
152 |
|
153 | {
|
154 | name: "svg",
|
155 | act: function () {
|
156 | if(bubbles.svgCapable && bubbles.imgCache.hasOwnProperty(this.elm.svg)){
|
157 | this.domElm = bubbles.imgCache[this.elm.svg].cloneNode(true);
|
158 | this.domElm.removeAttribute("id");
|
159 | var className = this.domElm.getAttribute("class");
|
160 | this.domElm.setAttribute("class", className + " " + this.elm.svg);
|
161 | } else {
|
162 | console.error("Sprite", this.elm.svg);
|
163 | return;
|
164 | }
|
165 | }
|
166 | },
|
167 |
|
168 |
|
169 | {
|
170 | name: "tag",
|
171 | act: function () {
|
172 | this.domElm = document.createElement(this.elm.tag);
|
173 | }
|
174 | },
|
175 |
|
176 |
|
177 | {
|
178 | name: "dom",
|
179 | act: function () {
|
180 | this.ctx.dom[this.elm.dom] = this.domElm;
|
181 | }
|
182 | },
|
183 |
|
184 |
|
185 | {
|
186 | name: "str",
|
187 | act: function () {
|
188 | this.domElm.classList.add("pre");
|
189 | this.domElm.appendChild(document.createTextNode(this.elm.str));
|
190 | }
|
191 | },
|
192 |
|
193 |
|
194 | {
|
195 | name: "html",
|
196 | act: function () {
|
197 | this.domElm.innerHTML = this.ctx[this.elm.html];
|
198 | }
|
199 | },
|
200 |
|
201 |
|
202 | {
|
203 | name: "css",
|
204 | act: function () {
|
205 | bubbles.arrayify(this.elm.css).forEach(function (css) {
|
206 | this.domElm.classList.add(css);
|
207 | }, this);
|
208 | }
|
209 | },
|
210 |
|
211 |
|
212 | {
|
213 | name: "style",
|
214 | act: function () {
|
215 | bubbles.add(this.domElm.style, this.elm.style);
|
216 | }
|
217 | },
|
218 |
|
219 |
|
220 | {
|
221 | name: "att",
|
222 | act: function () {
|
223 | for (var key in this.elm.att) {
|
224 | this.domElm.setAttribute(key, this.elm.att[key]);
|
225 | }
|
226 | }
|
227 | },
|
228 |
|
229 |
|
230 | {
|
231 | name: "blob",
|
232 | act: function () {
|
233 | bubbles.arrayify(this.elm.blob).forEach(function (blobSrc) {
|
234 | if(blobSrc.name) {
|
235 |
|
236 | bubbles.blob(blobSrc.name, this.domElm, blobSrc, function (ctx) {
|
237 | if(blobSrc.ready && blobSrc.ready.call) {
|
238 | blobSrc.ready.call(ctx, blobSrc);
|
239 | }
|
240 | });
|
241 | }
|
242 | }, this);
|
243 | }
|
244 | },
|
245 |
|
246 |
|
247 | {
|
248 | name: "elm",
|
249 | act: function () {
|
250 | bubbles.elm(this.elm.elm, this.domElm, this.ctx);
|
251 | }
|
252 | }
|
253 | ],
|
254 |
|
255 |
|
256 | elm: function (elms, tg, ctx) {
|
257 | var newElms = [];
|
258 | bubbles.arrayify(elms).forEach(function (elm) {
|
259 | var proc = {
|
260 | domElm: undefined,
|
261 | ctx: ctx,
|
262 | elm: elm,
|
263 | };
|
264 |
|
265 |
|
266 | if(!elm.tg && !elm.svg && !elm.tag) {
|
267 | elm.tag = "div";
|
268 | }
|
269 |
|
270 |
|
271 | bubbles.elmApi.forEach(function(elmApi){
|
272 | if(proc.elm.hasOwnProperty(elmApi.name)) {
|
273 | elmApi.act.call(proc);
|
274 | }
|
275 | });
|
276 |
|
277 |
|
278 | if(tg.nodeType === 8 && tg.parentNode) {
|
279 | tg.parentNode.insertBefore(proc.domElm, tg);
|
280 | } else {
|
281 | tg.appendChild(proc.domElm);
|
282 | }
|
283 |
|
284 | newElms.push(proc);
|
285 | });
|
286 |
|
287 | return newElms;
|
288 | },
|
289 |
|
290 |
|
291 | mvc: function(tg, mvc, inherit, ready) {
|
292 |
|
293 | var ctx = {
|
294 | bubbles: bubbles,
|
295 | mvc: mvc,
|
296 | tg: {},
|
297 | dom: {}
|
298 | };
|
299 |
|
300 | var que = new bubbles.que(ctx);
|
301 |
|
302 | var rmDom = function(tgs){
|
303 | var parent = tg.nodeType === 8 ? tg.parentNode : tg;
|
304 | tgs.forEach(function (child) {
|
305 | if (child.domElm.parentNode === parent){
|
306 | parent.removeChild(child.domElm);
|
307 | }
|
308 | });
|
309 | };
|
310 |
|
311 |
|
312 | if(mvc.model && mvc.model.call) {
|
313 | que.add(function(next){
|
314 | mvc.model.call(
|
315 | this,
|
316 | next,
|
317 | ctx,
|
318 | inherit
|
319 | );
|
320 | });
|
321 | }
|
322 |
|
323 |
|
324 | if(mvc.view && mvc.view.call) {
|
325 | que.add(function(next){
|
326 | var root;
|
327 |
|
328 | mvc.view.call(
|
329 | this,
|
330 | next,
|
331 | function(viewElm){
|
332 | root = bubbles.elm(viewElm, tg, ctx);
|
333 | },
|
334 | ctx
|
335 | );
|
336 |
|
337 |
|
338 | var destroyer = mvc.destroy;
|
339 | mvc.destroy = function(){
|
340 | if(root && root.length){
|
341 | if (destroyer && destroyer.call) destroyer.call(ctx, function(){
|
342 | rmDom(root);
|
343 | });
|
344 | else rmDom(root);
|
345 | }
|
346 | };
|
347 | });
|
348 | }
|
349 |
|
350 |
|
351 | if(mvc.controller && mvc.controller.call) {
|
352 | que.add(function(next){
|
353 | mvc.controller.call(
|
354 | this,
|
355 | next,
|
356 | ctx
|
357 | );
|
358 | });
|
359 | }
|
360 |
|
361 |
|
362 | que.then(function(){
|
363 | if(ready && ready.call) ready.call(ctx);
|
364 | });
|
365 | },
|
366 |
|
367 | loadBinScript: function(name) {
|
368 | var elm = document.createElement("script");
|
369 | var url = "./bin/" + name + ".js";
|
370 | var payload;
|
371 | var resolver;
|
372 |
|
373 | if(!window.bubbleSet) window.bubbleSet = {};
|
374 |
|
375 | window.bubbleSet[name] = function(construct) {
|
376 | resolver(construct);
|
377 | delete window.bubbleSet[name];
|
378 | document.head.removeChild(elm);
|
379 | };
|
380 |
|
381 | return new Promise(function(resolve, reject) {
|
382 | resolver = resolve;
|
383 |
|
384 | elm.setAttribute("src", url);
|
385 | elm.setAttribute("type", "text/javascript");
|
386 | elm.setAttribute("charset", "utf-8");
|
387 | elm.setAttributeNode(document.createAttribute("async"));
|
388 |
|
389 | elm.onerror = function(evt) {
|
390 | reject("Error while loading: " + url);
|
391 | };
|
392 |
|
393 | document.head.appendChild(elm);
|
394 | });
|
395 | },
|
396 |
|
397 | blobCache: {},
|
398 |
|
399 | svgCache: {},
|
400 |
|
401 | cssCache: {},
|
402 |
|
403 | imgCache: {},
|
404 |
|
405 | blob: function(name, tg, ctx, ready) {
|
406 | var parse = function (mvcContext) {
|
407 | var blob = new bubbles.all(new mvcContext());
|
408 |
|
409 |
|
410 | blob.add(function(ready){
|
411 | if(this.css && bubbles.cssCache[name] !== true) {
|
412 | var style = document.createElement("link");
|
413 | style.setAttribute("rel", "stylesheet");
|
414 | style.setAttribute("href", "bin/" + name + ".css");
|
415 | style.onload = function(){
|
416 | bubbles.cssCache[name] = true;
|
417 | ready();
|
418 | };
|
419 | document.head.appendChild(style);
|
420 | } else {
|
421 | ready();
|
422 | }
|
423 | });
|
424 |
|
425 |
|
426 | blob.add(function(ready){
|
427 | if(this.svg && bubbles.svgCache[name] !== true) {
|
428 | bubbles.xhr("bin/" + name + ".svg", function(svgData){
|
429 | var imgs = new DOMParser().parseFromString(svgData, "image/svg+xml").getElementsByTagName("svg");
|
430 | for(var i = 0; i < imgs.length; i++){
|
431 | var img = imgs[i];
|
432 | if(img.id) bubbles.imgCache[img.id] = img;
|
433 | }
|
434 | bubbles.svgCache[name] = true;
|
435 | ready();
|
436 | });
|
437 | } else {
|
438 | ready();
|
439 | }
|
440 | });
|
441 |
|
442 | blob.then(function(){
|
443 |
|
444 | if (this.model || this.view || this.controller) {
|
445 | this.name = name;
|
446 | bubbles.mvc(tg, this, ctx, ready);
|
447 | } else if(ready && ready.call) {
|
448 | ready.call(this);
|
449 | }
|
450 | });
|
451 | };
|
452 |
|
453 |
|
454 | if (!bubbles.blobCache.hasOwnProperty(name)) {
|
455 | bubbles.blobCache[name] = bubbles.loadBinScript(name, ctx);
|
456 | }
|
457 |
|
458 | bubbles.blobCache[name].then(parse, function(err){
|
459 | console.log(err);
|
460 | ready();
|
461 | });
|
462 | },
|
463 |
|
464 |
|
465 | acquire: function (coll, done) {
|
466 | var ctx = this, polyFill = 0, isReady = function () {
|
467 | if (!polyFill && done) done();
|
468 | };
|
469 |
|
470 | var newBlob = function(name) {
|
471 | polyFill++;
|
472 | ctx.blob(name, false, ctx, function (blob) {
|
473 | coll[name] = blob;
|
474 | polyFill--;
|
475 | isReady();
|
476 | });
|
477 | };
|
478 |
|
479 | for (var name in coll) {
|
480 | if (coll.hasOwnProperty(name) && !coll[name]) {
|
481 | newBlob(name);
|
482 | }
|
483 | }
|
484 | isReady();
|
485 | },
|
486 | };
|
487 |
|
488 |
|
489 | window.addEventListener("load", function(){
|
490 |
|
491 | bubbles.acquire({
|
492 | classList: "classList" in document.createElement("a"),
|
493 | forEach: Array.prototype.forEach,
|
494 | promise: window.Promise
|
495 | }, function () {
|
496 | bubbles.blob("main", document.body, {
|
497 | bubbles: bubbles
|
498 | });
|
499 | });
|
500 | });
|
501 | })();
|
502 |
|