1 /**
  2  * @fileOverview
  3  * widget.enchant.js
  4  * @version 0.2.0
  5  * @require enchant.js v0.6.0+
  6  * @author UEI Corporation
  7  *
  8  * @description
  9  * Library for making mobile webpage-style UIs in enchant.js.
 10  */
 11 
 12 (function() {
 13 
 14     /**
 15      * @type {Object}
 16      */
 17     enchant.widget = {
 18         assets: [
 19             'listItemBg.png',
 20             'iconMenuBg.png',
 21             'button.png',
 22             'buttonPushed.png',
 23             'dialog.png',
 24             'navigationBar.png'
 25         ],
 26         _env: {
 27             // default font
 28             font: '12px helvetica',
 29             buttonFont: '23px helvetica',
 30             navigationBarFont: '16px helvetica',
 31             textareaFont: '8px monospace',
 32             listItemMargin: 4,
 33             dialogMargin: 24,
 34             itemHeight: 48,
 35             buttonWidth: 64,
 36             buttonHeight: 36,
 37             dialogWidth: 300,
 38             dialogHeight: 225,
 39             inputMinHeight: 160,
 40             inputMaxHeight: 240,
 41             acceptName: 'OK',
 42             cancelName: 'NO',
 43             HOLDTIME: 300,
 44             DBLLIMIT: 300,
 45             FLINGVEL: 3
 46         },
 47 
 48         /**
 49          * Return objects that cannot be displayed as units in string or enchant.Surface in displayable form.
 50          * @param {*} content Data you wish to display.
 51          * @return {*} enchant Entity object.
 52          */
 53         parseContent: function(content, font, color) {
 54             var en, metrics;
 55             if (typeof content === 'undefined') {
 56                 content = '';
 57             }
 58             if (typeof content === 'number') {
 59                 content = '' + content;
 60             }
 61             if (content instanceof enchant.Entity) {
 62             } else if (content instanceof enchant.Surface) {
 63                 en = new enchant.Sprite(content.width, content.height);
 64                 en.image = content;
 65                 content = en;
 66             } else if (typeof content == 'string') {
 67                 en = new enchant.Label(content);
 68                 if (font) {
 69                     en.font = font;
 70                 } else {
 71                     en.font = enchant.widget._env.font;
 72                 }
 73                 if (color) {
 74                     en.color = color;
 75                 }
 76                 metrics = en.getMetrics();
 77                 en.width = metrics.width;
 78                 en.height = metrics.height;
 79                 content = en;
 80             }
 81             return content;
 82         }
 83     };
 84 
 85     /**
 86      * Events occurring during Scene beginning.
 87      * Issued when ended {@link enchant.Core#transitionPush} animation.
 88      * @type {String}
 89      */
 90     enchant.Event.TRANSITIONENTER = 'transitionenter';
 91 
 92     /**
 93      * Events occurring during Scene end.
 94      * Issued when ended {@link enchant.Core#transitionPop} animation.
 95      * @type {String}
 96      */
 97     enchant.Event.TRANSITIONEXIT = 'transitionexit';
 98 
 99     /**
100      * Event issued when positive button in enchant.widget.Confirm is pushed.
101      * @type {String}
102      */
103     enchant.Event.ACCEPT = 'accept';
104 
105     /**
106      * Event issued when negative button in enchant.widget.Confirm is pushed.
107      * @type {String}
108      */
109     enchant.Event.CANCEL = 'cancel';
110 
111     /**
112      * Event issued when form object content is changed.
113      * @type {String}
114      */
115     enchant.Event.CHANGE = 'change';
116 
117     /**
118      * Event issued when tap is detected.
119      * Detected when touch ends without movement, and when double touch has ended.
120      * @type {String}
121      */
122     enchant.Event.TAP = 'tap';
123 
124     /**
125      * Event issued when double tap is detected.
126      * Detected when two taps are detected within a set time and distance.
127      * @type {String}
128      */
129     enchant.Event.DOUBLETAP = 'doubletap';
130 
131     /**
132      * Event issued when hold is detected.
133      * Detected when touch continues for a set time without movement.
134      * @type {String}
135      */
136     enchant.Event.HOLD = 'hold';
137 
138     /**
139      * Event issued when drag is detected.
140      * Detected when touch position changes during hold.
141      * @type {String}
142      */
143     enchant.Event.DRAG = 'drag';
144 
145     /**
146      * Event issued when release is detected.
147      * Detected when touch ends during hold.
148      * @type {String}
149      */
150     enchant.Event.RELEASE = 'release';
151 
152     /**
153      * Event issued when slip is detected.
154      * Detected when touch position changes without holding.
155      * @type {String}
156      */
157     enchant.Event.SLIP = 'slip';
158 
159     /**
160      * Event issued when fling is detected.
161      * Detected when touch ends and position moves faster than set speed.
162      * @type {String}
163      */
164     enchant.Event.FLING = 'fling';
165     
166     
167     /**
168      * Event which will be dispatched when additional content should be loaded for a view.
169      */
170     enchant.Event.CONTENT_REQUESTED = 'contentRequested';
171 
172     var NOTOUCH = 0;
173     var WAITDBL = 1;
174     var NOMOVE = 2;
175     var NOMOVEDBL = 3;
176     var MOVED = 4;
177     var HOLD = 5;
178 
179     var getElementMetrics = function(string, font) {
180         var e = document.createElement('div');
181         var cvs = document.createElement('canvas');
182         var ctx = cvs.getContext('2d');
183         var arr, str, w;
184         var width = 0;
185         var height = 0;
186         if (!font) {
187             font = enchant.widget._env.font;
188         }
189         ctx.font = font;
190         e.style.font = font;
191         string = string || '';
192         string = string.replace(/<(br|BR) ?\/?>/g, '<br>');
193         arr = string.split('<br>');
194         for (var i = 0, l = arr.length; i < l; i++) {
195             str = arr[i];
196             w = ctx.measureText(str).width;
197             if (width < w) {
198                 width = w;
199             }
200         }
201 
202         e.innerHTML = string;
203 
204         if (document.body) {
205             document.body.appendChild(e);
206             height = parseInt(getComputedStyle(e).height, 10);
207             e.style.position = 'absolute';
208             width = parseInt(getComputedStyle(e).width, 10);
209             document.body.removeChild(e);
210         } else {
211             height = 14 * arr.length;
212         }
213 
214         return {
215             width: width + 1,
216             height: height + 1
217         };
218     };
219 
220     var calcLeastPosition = function(margin) {
221         margin |= 0;
222         return margin;
223     };
224 
225     var calcMostPosition = function(child, parent, margin) {
226         margin |= 0;
227         return parent - margin - child;
228     };
229 
230     var calcCenteredPosition = function(child, parent) {
231         return ~~(parent / 2) - ~~(child / 2);
232     };
233 
234     var getScaleOffest = function(length, scale) {
235         var half = ~~(length / 2);
236         scale = scale || 1;
237         return half - ~~(half * scale);
238     };
239 
240     var distribute = function(value, div) {
241         if (typeof div == 'array') {
242             var ratio = div;
243             var ret = new Array(ratio.length);
244             var retSum = 0;
245             var maxi = 0;
246             var max = 0;
247             var sum = 0;
248             var quo;
249 
250             ratio.forEach(function(n) {
251                 sum += n;
252             });
253             quo = value / sum;
254 
255             for (var i = 0, l = ret.length; i < l; i++) {
256                 ret[i] = Math.round(quo * ratio[i]);
257                 if (ratio[i] < max) {
258                     maxi = i;
259                     max = ratio[i];
260                 }
261             }
262 
263             ret.forEach(function(n) {
264                 retSum += n;
265             });
266 
267             ret[maxi] += value - retSum;
268         } else if (typeof div == 'number') {
269             var ret = new Array(div);
270             var quo = ~~(value / div);
271             var rem = ~~(value % div);
272             for (var i = 0, l = div; i < l; i++) {
273                 ret[i] = quo;
274             }
275             for (var i = 0, l = rem; i < l; i++) {
276                 ret[i % div] += 1;
277             }
278         }
279         return ret;
280     };
281 
282     var Adjust = {
283         fitToX: function(parent, margin) {
284             var l = parent.width;
285             var s = Math.min(
286                 (l - margin * 2) / this.width,
287                 (l - margin * 2) / this.height
288             );
289             if (this instanceof enchant.Sprite) {
290                 this.scaleX = s;
291                 this.scaleY = s;
292             } else {
293                 this.width = ~~(this.width * s);
294                 this.height = ~~(this.height * s);
295             }
296         },
297         fitToY: function(parent, margin) {
298             var l = parent.height;
299             var s = Math.min(
300                 (l - margin * 2) / this.width,
301                 (l - margin * 2) / this.height
302             );
303             if (this instanceof enchant.Sprite) {
304                 this.scaleX = s;
305                 this.scaleY = s;
306             } else {
307                 this.width = ~~(this.width * s);
308                 this.height = ~~(this.height * s);
309             }
310         },
311         fillX: function(parent, margin) {
312             var s = (parent.width - margin * 2) / this.width;
313             if (this instanceof enchant.Sprite) {
314                 this.scaleX = s;
315                 this.scaleY = s;
316             } else {
317                 this.width = ~~(this.width * s);
318                 this.height = ~~(this.height * s);
319             }
320         },
321         fillY: function(parent, margin) {
322             var s = (parent.height - margin * 2) / this.height;
323             if (this instanceof enchant.Sprite) {
324                 this.scaleX = s;
325                 this.scaleY = s;
326             } else {
327                 this.width = ~~(this.width * s);
328                 this.height = ~~(this.height * s);
329             }
330         }
331     };
332 
333     var Effect = {
334         transitForwardIn: function(time) {
335             var core = enchant.Core.instance;
336             var child;
337             this.x = core.width;
338             var e = new enchant.Event(enchant.Event.RENDER);
339             for (var i = 0, l = this.childNodes.length; i < l; i++) {
340                 child = this.childNodes[i];
341                 child.dispatchEvent(e);
342             }
343             this.tl
344                 .moveTo(0, 0, time, enchant.Easing.QUAD_EASEINOUT);
345         },
346         transitForwardOut: function(time) {
347             var core = enchant.Core.instance;
348             this.x = 0;
349             this.tl
350                 .moveTo(-core.width, 0, time, enchant.Easing.QUAD_EASEINOUT);
351         },
352         transitBackIn: function(time) {
353             var core = enchant.Core.instance;
354             this.x = -core.width;
355             this.tl
356                 .moveTo(0, 0, time, enchant.Easing.QUAD_EASEINOUT);
357         },
358         transitBackOut: function(time) {
359             var core = enchant.Core.instance;
360             this.x = 0;
361             this.tl
362                 .moveTo(core.width, 0, time, enchant.Easing.QUAD_EASEINOUT);
363         },
364         popup: function() {
365             this.scaleX = 0.1;
366             this.scaleY = 0.1;
367             this.opacity = 0.1;
368             this.tl
369                 .fadeTo(0.8, 3, enchant.Easing.QUAD_EASEOUT)
370                 .and()
371                 .scaleTo(1, 3, enchant.Easing.BOUNCE_EASEOUT);
372         },
373         popdown: function() {
374             this.tl
375                 .fadeTo(0.1, 3, enchant.Easing.QUAD_EASEOUT)
376                 .and()
377                 .scaleTo(0.1, 3, enchant.Easing.BOUNCE_EASEOUT);
378         },
379         resizeTo: function(width, height, time, easing) {
380             return this.tl.tween({
381                 width: width,
382                 height: height,
383                 time: time,
384                 easing: easing
385             });
386         }
387     };
388 
389     var Align = {
390 
391         /**
392          * @scope enchant.Entity
393          */
394 
395         /**
396          * Moves to left side of specified object.
397          * @param {*} another Object that becomes standard.
398          * @param {Number} margin Number of pixels shifted.
399          * @requires widget.enchant.js
400          */
401         alignLeftOf: function(another, margin) {
402             margin |= 0;
403             var anotherScaleOffset = getScaleOffest(another.width, another.scaleX);
404             var scaleOffset = getScaleOffest(this.width, this.scaleX);
405             this.x = another.x + anotherScaleOffset - scaleOffset - this.width - margin;
406             return this;
407         },
408         /**
409          * Moves to right side of specified object.
410          * @param {*} another Object that becomes standard.
411          * @param {Number} margin Number of pixels shifted.
412          * @requires widget.enchant.js
413          */
414         alignRightOf: function(another, margin) {
415             margin |= 0;
416             var anotherScaleOffset = getScaleOffest(another.width, another.scaleX);
417             var scaleOffset = getScaleOffest(this.width, this.scaleX);
418             this.x = another.x + another.width - anotherScaleOffset - scaleOffset + margin;
419             return this;
420         },
421         /**
422          * Moves to upper side of specified object.
423          * @param {*} another Object that becomes standard.
424          * @param {Number} margin Number of pixels shifted.
425          * @requires widget.enchant.js
426          */
427         alignTopOf: function(another, margin) {
428             margin |= 0;
429             var anotherScaleOffset = getScaleOffest(another.height, another.scaleY);
430             var scaleOffset = getScaleOffest(this.height, this.scaleY);
431             this.y = another.y + anotherScaleOffset - scaleOffset - this.height - margin;
432             return this;
433         },
434         /**
435          * Moves to lower side of specified object.
436          * @param {*} another Object that becomes standard.
437          * @param {Number} margin Number of pixels shifted.
438          * @requires widget.enchant.js
439          */
440         alignBottomOf: function(another, margin) {
441             margin |= 0;
442             var anotherScaleOffset = getScaleOffest(another.height, another.scaleY);
443             var scaleOffset = getScaleOffest(this.height, this.scaleY);
444             this.y = another.y + another.height - anotherScaleOffset - scaleOffset + margin;
445             return this;
446         },
447         /**
448          * Performs leftwards movement within specified object.
449          * @param {*} another Object that becomes standard.
450          * @param {Number} margin Number of pixels shifted.
451          * @requires widget.enchant.js
452          */
453         alignLeftIn: function(another, margin) {
454             var scaleOffset = getScaleOffest(this.width, this.scaleX);
455             this.x = calcLeastPosition(margin) - scaleOffset;
456             return this;
457         },
458         /**
459          * Performs rightwards movement within specified object.
460          * @param {*} another Object that becomes standard.
461          * @param {Number} margin Number of pixels shifted.
462          * @requires widget.enchant.js
463          */
464         alignRightIn: function(another, margin) {
465             var scaleOffset = getScaleOffest(this.width, this.scaleX);
466             this.x = calcMostPosition(this.width, another.width, margin) + scaleOffset;
467             return this;
468         },
469         /**
470          * Performs upwards movement within specified object.
471          * @param {*} another Object that becomes standard.
472          * @param {Number} margin Number of pixels shifted.
473          * @requires widget.enchant.js
474          */
475         alignTopIn: function(another, margin) {
476             var scaleOffset = getScaleOffest(this.height, this.scaleY);
477             this.y = calcLeastPosition(margin) - scaleOffset;
478             return this;
479         },
480         /**
481          * Performs downwards movement within specified object.
482          * @param {*} another Object that becomes standard.
483          * @param {Number} margin Number of pixels shifted.
484          * @requires widget.enchant.js
485          */
486         alignBottomIn: function(another, margin) {
487             var scaleOffset = getScaleOffest(this.height, this.scaleY);
488             this.y = calcMostPosition(this.height, another.height, margin) + scaleOffset;
489             return this;
490         },
491         /**
492          * Performs central movement along x axis within specified object.
493          * @param {*} another Object that becomes standard.
494          * @param {Number} margin Number of pixels shifted.
495          * @requires widget.enchant.js
496          */
497         alignHorizontalCenterIn: function(another) {
498             this.x = calcCenteredPosition(this.width, another.width);
499             return this;
500         },
501         /**
502          * Performs central movement along y axis within specified object.
503          * @param {*} another object that becomes standard.
504          * @param {Number} margin Number of pictures shifted.
505          * @requires widget.enchant.js
506          */
507         alignVerticalCenterIn: function(another) {
508             this.y = calcCenteredPosition(this.height, another.height);
509             return this;
510         }
511     };
512 
513     for (var prop in Align) {
514         enchant.Entity.prototype[prop] = Align[prop];
515     }
516 
517     var _transitionLock = false;
518 
519     /**
520      * @scope enchant.Core
521      */
522 
523     /**
524      * Perform pushScene with transition animation.
525      * @param {enchant.Scene} inScene New scene transitioned to.
526      * @return {enchant.Scene} New scene
527      * @requires widget.enchant.js
528      */
529     enchant.Core.prototype.transitionPush = function(inScene) {
530         if (_transitionLock) return null;
531         _transitionLock = true;
532         var time = 15;
533         var c = 0;
534         var outScene = this.currentScene;
535         Effect.transitForwardIn.call(inScene, time);
536         Effect.transitForwardOut.call(outScene, time);
537         this.addEventListener(enchant.Event.ENTER_FRAME, function(e) {
538             outScene.dispatchEvent(e);
539             if (c > time) {
540                 _transitionLock = false;
541                 this.removeEventListener(enchant.Event.ENTER_FRAME, arguments.callee);
542                 inScene.dispatchEvent(new enchant.Event(enchant.Event.TRANSITIONENTER));
543                 outScene.dispatchEvent(new enchant.Event(enchant.Event.TRANSITIONEXIT));
544             }
545             c++;
546         });
547         return this.pushScene(inScene);
548     };
549 
550     /**
551      * Perform popScene with transition animation.
552      * @return {enchant.Scene} Finished scene.
553      * @requires widget.enchant.js
554      */
555     enchant.Core.prototype.transitionPop = function() {
556         if (_transitionLock) return null;
557         if (this.currentScene == this.rootScene) return null;
558         _transitionLock = true;
559         var time = 15;
560         var c = 0;
561         var outScene = this.currentScene;
562         var inScene = this._scenes[this._scenes.length - 2];
563         this.addEventListener(enchant.Event.ENTER_FRAME, function(e) {
564             inScene.dispatchEvent(e);
565             if (c > time) {
566                 _transitionLock = false;
567                 this.removeEventListener(enchant.Event.ENTER_FRAME, arguments.callee);
568                 this.popScene();
569                 outScene.dispatchEvent(new enchant.Event(enchant.Event.TRANSITIONEXIT));
570                 inScene.dispatchEvent(new enchant.Event(enchant.Event.TRANSITIONENTER));
571             }
572             c++;
573         });
574         Effect.transitBackIn.call(inScene, time);
575         Effect.transitBackOut.call(outScene, time);
576         return this._scenes[this._scenes.length - 1];
577     };
578 
579     /**
580      * @scope enchant.widget.GestureDetector
581      */
582     enchant.widget.GestureDetector = enchant.Class.create(enchant.EventTarget, {
583         /**
584          * Issue event after detecting several gestures.
585          * Can detect tap, double tap, hold, drag, flick, and more.
586          * @param {enchant.Entity} target Object for which you wish to detect input.
587          * @constructs
588          * @extends enchant.EventTarget
589          */
590         initialize: function(target) {
591             var core = enchant.Core.instance;
592             enchant.EventTarget.call(this);
593             this._target;
594             this._startX = 0;
595             this._startY = 0;
596             this._lastX = 0;
597             this._lastY = 0;
598             this._touchElapsed = 0;
599             this._releaseElapsed = 0;
600             this._state = NOTOUCH;
601             this._velobase = (core.width > core.height) ? core.height : core.width;
602 
603             var detector = this;
604             this._handler = function(e) {
605                 detector.dispatchEvent(e);
606             };
607 
608             this._types = [
609                 enchant.Event.TOUCH_START,
610                 enchant.Event.TOUCH_MOVE,
611                 enchant.Event.TOUCH_END,
612                 enchant.Event.ENTER_FRAME
613             ];
614 
615             if (target) {
616                 this.attach(target);
617             }
618         },
619         attach: function(target) {
620             this._target = target;
621             this._types.forEach(function(event) {
622                 this._target.addEventListener(event, this._handler);
623             }, this);
624         },
625         detach: function() {
626             this._types.forEach(function(event) {
627                 this._target.removeEventListener(event, this._handler);
628             }, this);
629             this._target = null;
630         },
631         ontouchstart: function(e) {
632             var core = enchant.Core.instance;
633             this._startFrame = core.frame;
634             this._startX = this._lastX = e.x;
635             this._startY = this._lastY = e.y;
636             if (this._state == WAITDBL) {
637                 this._state = NOMOVEDBL;
638             } else if (this._state == NOTOUCH) {
639                 this._state = NOMOVE;
640             }
641         },
642         ontouchmove: function(e) {
643             var dx = e.x - this._lastX;
644             var dy = e.y - this._lastY;
645             this._lastX = e.x;
646             this._lastY = e.y;
647             switch (this._state) {
648                 case NOMOVE:
649                 case NOMOVEDBL:
650                     this._state = MOVED;
651                 case MOVED:
652                     var evt = new enchant.Event(enchant.Event.SLIP);
653                     evt.x = this._lastX;
654                     evt.y = this._lastY;
655                     evt.dx = dx;
656                     evt.dy = dy;
657                     this._target.dispatchEvent(evt);
658                     break;
659                 case HOLD:
660                     var evt = new enchant.Event(enchant.Event.DRAG);
661                     evt.x = this._lastX;
662                     evt.y = this._lastY;
663                     evt.dx = dx;
664                     evt.dy = dy;
665                     this._target.dispatchEvent(evt);
666                     break;
667                 default:
668                     break;
669             }
670         },
671         ontouchend: function(e) {
672             var core = enchant.Core.instance;
673             switch (this._state) {
674                 case MOVED:
675                     velocityX = (this._lastX - this._startX) / this._velobase / this._touchElapsed * 1000;
676                     velocityY = (this._lastY - this._startY) / this._velobase / this._touchElapsed * 1000;
677                     if (velocityX > enchant.widget._env.FLINGVEL || velocityY > enchant.widget._env.FLINGVEL) {
678                         var evt = new enchant.Event(enchant.Event.FLING);
679                         evt.x = this._startX;
680                         evt.y = this._startY;
681                         evt.ex = this._lastX;
682                         evt.ey = this._lastY;
683                         evt.velocityX = velocityX;
684                         evt.velocityY = velocityY;
685                         this._target.dispatchEvent(evt);
686                     }
687                     this._state = NOTOUCH;
688                     break;
689                 case HOLD:
690                     var evt = new enchant.Event(enchant.Event.RELEASE);
691                     evt.x = this._lastX;
692                     evt.y = this._lastY;
693                     this._target.dispatchEvent(evt);
694                     this._state = NOTOUCH;
695                     break;
696                 case NOMOVEDBL:
697                     var evt = new enchant.Event(enchant.Event.DOUBLETAP);
698                     evt.x = this._lastX;
699                     evt.y = this._lastY;
700                     this._target.dispatchEvent(evt);
701                     this._state = NOTOUCH;
702                     this._releaseElapsed = 0;
703                     break;
704                 case NOMOVE:
705                     this._state = WAITDBL;
706                     break;
707                 default:
708                     this._state = NOTOUCH;
709                     break;
710             }
711             this._touchElapsed = 0;
712             this._startX = 0;
713             this._startY = 0;
714         },
715         onenterframe: function(e) {
716             var elapsed = e.elapsed;
717             switch (this._state) {
718                 case WAITDBL:
719                     this._releaseElapsed += elapsed;
720                     if (this._releaseElapsed >= enchant.widget._env.DBLLIMIT) {
721                         var evt = new enchant.Event(enchant.Event.TAP);
722                         evt.x = this._lastX;
723                         evt.y = this._lastY;
724                         this._lastX = 0;
725                         this._lastY = 0;
726                         this._target.dispatchEvent(evt);
727                         this._state = NOTOUCH;
728                         this._releaseElapsed = 0;
729                     }
730                     break;
731                 case NOMOVEDBL:
732                     this._releaseElapsed += elapsed;
733                     if (this._releaseElapsed >= enchant.widget._env.DBLLIMIT) {
734                         this._state = NOMOVE;
735                         this._releaseElapsed = 0;
736                     }
737                 case NOMOVE:
738                     this._touchElapsed += elapsed;
739                     if (this._touchElapsed >= enchant.widget._env.HOLDTIME) {
740                         var evt = new enchant.Event(enchant.Event.HOLD);
741                         evt.x = this._lastX;
742                         evt.y = this._lastY;
743                         this._target.dispatchEvent(evt);
744                         this._state = HOLD;
745                         this._touchElapsed = 0;
746                     }
747                     break;
748                 case MOVED:
749                     this._touchElapsed += elapsed;
750                     break;
751                 case NOTOUCH:
752                 case HOLD:
753                 default:
754                     break;
755             }
756         }
757     });
758     enchant.widget.GestureDetector.gestureEvents = [
759         enchant.Event.ACCEPT,
760         enchant.Event.CANCEL,
761         enchant.Event.TAP,
762         enchant.Event.DOUBLETAP,
763         enchant.Event.HOLD,
764         enchant.Event.DRAG,
765         enchant.Event.RELEASE,
766         enchant.Event.SLIP,
767         enchant.Event.FLING
768     ];
769 
770     /**
771      * @scope enchant.widget.Ninepatch
772      */
773     enchant.widget.Ninepatch = enchant.Class.create(enchant.Surface, {
774         /**
775          * Surface corresponding to 9patch.
776          * Does not respond to settings in content area.
777          * @param {Number} width Surface width.
778          * @param {Number} height Surface height.
779          * @constructs
780          * @extends enchant.Surface
781          */
782         initialize: function(width, height) {
783             enchant.Surface.call(this, width, height);
784 
785             this._horScStore = [];
786             this._horNoScStore = [];
787             this._verScStore = [];
788             this._verNoScStore = [];
789             this._src;
790         },
791         /**
792          * 9patch source.
793          * @type {enchant.Surface}
794          */
795         src: {
796             get: function() {
797                 return this._src;
798             },
799             set: function(surface) {
800                 if (surface == this._src || !(surface instanceof enchant.Surface)) {
801                     return;
802                 }
803                 this._slicedraw(surface);
804                 this._src = surface;
805             }
806         },
807         _detect: function(img) {
808             this._horScStore = [];
809             this._horNoScStore = [];
810             this._verScStore = [];
811             this._verNoScStore = [];
812             var elem = img._element;
813             var cvs = document.createElement('canvas');
814             var width = cvs.width = img.width;
815             var height = cvs.height = img.height;
816             var ctx = cvs.getContext('2d');
817             ctx.drawImage(elem, 0, 0, width, height);
818             var pixels = ctx.getImageData(0, 0, width, height);
819 
820             var isBlack = function(i) {
821                 return pixels.data[i] == 0 && pixels.data[i + 1] == 0 && pixels.data[i + 2] == 0 && pixels.data[i + 3] == 255;
822             };
823 
824             var last = false;
825             var tmp = [];
826             var scalable = [];
827             var noscalable = [];
828 
829             for (var i = 1, l = width - 1; i < l; i++) {
830                 last = isBlack(i * 4);
831                 if (last) {
832                     if (scalable.length == 0) {
833                         scalable.push(i);
834                     }
835                     if (noscalable.length == 1) {
836                         noscalable.push(i - 1);
837                         this._horNoScStore.push(noscalable);
838                         noscalable = [];
839                     }
840                 } else {
841                     if (noscalable.length == 0) {
842                         noscalable.push(i);
843                     }
844                     if (scalable.length == 1) {
845                         scalable.push(i - 1);
846                         this._horScStore.push(scalable);
847                         scalable = [];
848                     }
849                 }
850             }
851             if (scalable.length == 1) {
852                 scalable.push(i - 1);
853                 this._horScStore.push(scalable);
854             }
855             if (noscalable.length == 1) {
856                 noscalable.push(i - 1);
857                 this._horNoScStore.push(noscalable);
858             }
859             scalable = [];
860             noscalable = [];
861 
862             for (var i = 1, l = height - 1; i < l; i++) {
863                 last = isBlack(i * width * 4);
864                 if (last) {
865                     if (scalable.length == 0) {
866                         scalable.push(i);
867                     }
868                     if (noscalable.length == 1) {
869                         noscalable.push(i - 1);
870                         this._verNoScStore.push(noscalable);
871                         noscalable = [];
872                     }
873                 } else {
874                     if (noscalable.length == 0) {
875                         noscalable.push(i);
876                     }
877                     if (scalable.length == 1) {
878                         scalable.push(i - 1);
879                         this._verScStore.push(scalable);
880                         scalable = [];
881                     }
882                 }
883             }
884             if (scalable.length == 1) {
885                 scalable.push(i - 1);
886                 this._verScStore.push(scalable);
887             }
888             if (noscalable.length == 1) {
889                 noscalable.push(i - 1);
890                 this._verNoScStore.push(noscalable);
891             }
892         },
893         _slicedraw: function(img) {
894             this._detect(img);
895             var elem = img._element;
896             var w = img.width;
897             var h = img.height;
898             var width = this.width;
899             var height = this.height;
900             var ctx = this.context;
901 
902             var getSum = function(store) {
903                 var s;
904                 var sum = 0;
905                 for (var i = 0, l = store.length; i < l; i++) {
906                     s = store[i];
907                     sum += s[1] - s[0] + 1;
908                 }
909                 return sum;
910             };
911             var getRatio = function(array) {
912                 var a, ret = [];
913                 for (var i = 0, l = array.length; i < l; i++) {
914                     a = array[i];
915                     ret.push(a[1] - a[0] + 1);
916                 }
917                 return ret;
918             };
919             var fix = function(array, fix) {
920                 var a;
921                 for (var i = 0, l = array.length; i < l; i++) {
922                     a = array[i];
923                     a.fix = fix[i];
924                 }
925             };
926             var distribute = function(value, ratio) {
927                 var ret = new Array(ratio.length);
928                 var retSum = 0;
929                 var maxi = 0;
930                 var max = 0;
931                 var sum = 0;
932                 var quo;
933 
934                 ratio.forEach(function(n) {
935                     sum += n;
936                 });
937                 quo = value / sum;
938 
939                 for (var i = 0, l = ret.length; i < l; i++) {
940                     ret[i] = Math.round(quo * ratio[i]);
941                     if (ratio[i] < max) {
942                         maxi = i;
943                         max = ratio[i];
944                     }
945                 }
946 
947                 ret.forEach(function(n) {
948                     retSum += n;
949                 });
950 
951                 ret[maxi] += value - retSum;
952 
953                 return ret;
954             };
955 
956             var ratioH = getRatio(this._horScStore);
957             var valueH = width - getSum(this._horNoScStore);
958             var scaledW = distribute(valueH, ratioH);
959 
960             var ratioV = getRatio(this._verScStore);
961             var valueV = height - getSum(this._verNoScStore);
962             var scaledH = distribute(valueV, ratioV);
963 
964             fix(this._horScStore, scaledW);
965             fix(this._verScStore, scaledH);
966 
967             var verQueue = this._verScStore.concat(this._verNoScStore).sort(function(a, b) {
968                 return a[0] - b[0];
969             });
970             var horQueue = this._horScStore.concat(this._horNoScStore).sort(function(a, b) {
971                 return a[0] - b[0];
972             });
973 
974             var verQ;
975             var horQ;
976             var sx, sy, sw, sh, dw, dh;
977             var dx = 0;
978             var dy = 0;
979 
980             ctx.clearRect(0, 0, this.width, this.height);
981             for (var i = 0, l = horQueue.length; i < l; i++) {
982                 horQ = horQueue[i];
983                 sx = horQ[0];
984                 sw = horQ[1] - horQ[0] + 1;
985                 dw = (typeof horQ.fix == 'number') ? horQ.fix : sw;
986                 dy = 0;
987                 for (var j = 0, ll = verQueue.length; j < ll; j++) {
988                     verQ = verQueue[j];
989                     sy = verQ[0];
990                     sh = verQ[1] - verQ[0] + 1;
991                     dh = (typeof verQ.fix == 'number') ? verQ.fix : sh;
992                     ctx.drawImage(elem, sx, sy, sw, sh, dx, dy, dw, dh);
993                     dy += dh;
994                 }
995                 dx += dw;
996             }
997         },
998         /**
999          * @type {Number}
1000          */
1001         width: {
1002             get: function() {
1003                 return this._width;
1004             },
1005             set: function(width) {
1006                 this._width = width;
1007                 if (this._element) {
1008                     this._element.width = width;
1009                 }
1010                 if (this._src instanceof enchant.Surface) {
1011                     this._slicedraw(this._src);
1012                 }
1013             }
1014         },
1015         /**
1016          * @type {Number}
1017          */
1018         height: {
1019             get: function() {
1020                 return this._height;
1021             },
1022             set: function(height) {
1023                 this._height = height;
1024                 if (this._element) {
1025                     this._element.height = height;
1026                 }
1027                 if (this._src instanceof enchant.Surface) {
1028                     this._slicedraw(this._src);
1029                 }
1030             }
1031         },
1032         /**
1033          * Recreate set size.
1034          * @param {Number} width New width.
1035          * @param {Number} height New height.
1036          */
1037         resize: function(width, height) {
1038             this._width = width;
1039             this._height = height;
1040             this._element.width = width;
1041             this._element.height = height;
1042             if (this._src instanceof enchant.Surface) {
1043                 this._slicedraw(this._src);
1044             }
1045         }
1046     });
1047 
1048     /**
1049      * @scope enchant.widget.EntityGroup
1050      */
1051     enchant.widget.EntityGroup = enchant.Class.create(enchant.Entity, {
1052         /**
1053          * Entity that can have children.
1054          * @param {Number} width Entity width.
1055          * @param {Number} height Entity height.
1056          * @constructs
1057          * @extends enchant.Entity
1058          */
1059         initialize: function(width, height) {
1060             enchant.Entity.call(this);
1061             this.childNodes = [];
1062             this._background;
1063             this.width = width;
1064             this.height = height;
1065 
1066             [ enchant.Event.ADDED_TO_SCENE, enchant.Event.REMOVED_FROM_SCENE ]
1067                 .forEach(function(event) {
1068                     this.addEventListener(event, function(e) {
1069                         this.childNodes.slice().forEach(function(child) {
1070                             child.scene = this.scene;
1071                             child.dispatchEvent(e);
1072                         }, this);
1073                     });
1074                 }, this);
1075         },
1076         /**
1077          * @type {Number}
1078          */
1079         width: {
1080             get: function() {
1081                 return this._width;
1082             },
1083             set: function(width) {
1084                 this._width = width;
1085                 this._dirty = true;
1086                 if (this.background instanceof enchant.widget.Ninepatch) {
1087                     this.background.width = this.width;
1088                 }
1089             }
1090         },
1091         /**
1092          * @type {Number}
1093          */
1094         height: {
1095             get: function() {
1096                 return this._height;
1097             },
1098             set: function(height) {
1099                 this._height = height;
1100                 this._dirty = true;
1101                 if (this.background instanceof enchant.widget.Ninepatch) {
1102                     this.background.height = this.height;
1103                 }
1104             }
1105         },
1106         /**
1107          * Surface used as background.
1108          * @type {enchant.Surface}
1109          */
1110         background: {
1111             get: function() {
1112                 return this._background;
1113             },
1114             set: function(surface) {
1115                 if (surface instanceof enchant.Surface) {
1116                     this._background = surface;
1117                     if (surface._css) {
1118                         this._style['background-image'] = surface._css;
1119                     }
1120                 }
1121             }
1122         },
1123         /**
1124          * Add Node to EntityGroup.
1125          * @param {enchant.Node} child Node to be added.
1126          */
1127         addChild: enchant.Group.prototype.addChild,
1128         /**
1129          * Insert Node to EntityGroup.
1130          * @param {enchant.Node} child Node to be inserted.
1131          * @param {enchant.Node} reference Node before insertion postion.
1132          */
1133         insertBefore: enchant.Group.prototype.insertBefore,
1134         /**
1135          * Delete Node from EntityGroup.
1136          * @param {enchant.Node} child Node to delete.
1137          */
1138         removeChild: enchant.Group.prototype.removeChild,
1139         /**
1140          * First child Node.
1141          * @type {enchant.Node}
1142          */
1143         firstChild: Object.getOwnPropertyDescriptor(enchant.Group.prototype, 'firstChild'),
1144         /**
1145          * Last child Node.
1146          * @type {enchant.Node}
1147          */
1148         lastChild: Object.getOwnPropertyDescriptor(enchant.Group.prototype, 'lastChild'),
1149         _dirty: Object.getOwnPropertyDescriptor(enchant.Group.prototype, '_dirty'),
1150         cvsRender: function(ctx) {
1151             if (this.background &&
1152                 this.background._element.width > 0 &&
1153                 this.background._element.height > 0) {
1154                 ctx.drawImage(this.background._element, 0, 0, this.width, this.height);
1155             }
1156         }
1157     });
1158 
1159     /**
1160      * @scope enchant.widget.Modal
1161      */
1162     enchant.widget.Modal = enchant.Class.create(enchant.Scene, {
1163         /**
1164          * Model scene.
1165          * @constructs
1166          * @extends enchant.Scene
1167          */
1168         initialize: function() {
1169             enchant.Scene.call(this);
1170             var core = enchant.Core.instance;
1171             var shade = new enchant.Sprite(core.width, core.height);
1172             shade.backgroundColor = 'rgba(0, 0, 0, 0.1)';
1173             this.addChild(shade);
1174             this.addEventListener(enchant.Event.ENTER, function() {
1175                 shade.tl.fadeTo(0.7, 5, enchant.Easing.QUAD_EASEOUT);
1176             });
1177         }
1178     });
1179 
1180     /**
1181      * @scope enchant.widget.Button.prototype
1182      */
1183     enchant.widget.Button = enchant.Class.create(enchant.widget.EntityGroup, {
1184         /**
1185          * Button.
1186          * Set normal background and background when pushed down.
1187          * @param {*} Button content.
1188          * @constructs
1189          * @extends enchant.widget.EntityGroup
1190          */
1191         initialize: function(content) {
1192             var core = enchant.Core.instance;
1193             content = content || '';
1194             var minwidth = enchant.widget._env.buttonWidth;
1195             var minheight = enchant.widget._env.buttonHeight;
1196             enchant.widget.EntityGroup.call(this, minwidth, minheight);
1197             this._image;
1198             this._pushedimage;
1199             this._content;
1200             this._rawContent;
1201             var bg1 = new enchant.widget.Ninepatch(minwidth, minheight);
1202             bg1.src = core.assets['button.png'];
1203             this.image = bg1;
1204 
1205             var bg2 = new enchant.widget.Ninepatch(minwidth, minheight);
1206             bg2.src = core.assets['buttonPushed.png'];
1207             this.pushedimage = bg2;
1208 
1209             this.content = content;
1210             this.width = Math.max(this._content.width, minwidth);
1211             this.height = Math.max(this._content.height, minheight);
1212             this.addEventListener(enchant.Event.TOUCH_START, function() {
1213                 if (!this._pushedimage) {
1214                     return;
1215                 }
1216                 this.background = this._pushedimage;
1217             });
1218             this.addEventListener(enchant.Event.TOUCH_END, function() {
1219                 if (!this._pushedimage) {
1220                     return;
1221                 }
1222                 this.background = this._image;
1223             });
1224         },
1225         refresh: function() {
1226             if (this._content) {
1227                 this._content.alignHorizontalCenterIn(this).alignVerticalCenterIn(this);
1228             }
1229         },
1230         /**
1231          * width of button
1232          * @type number
1233          */
1234         width: {
1235             get: function() {
1236                 return this._width;
1237             },
1238             set: function(width) {
1239                 this._style.width = (this._width = width) + 'px';
1240                 if (this._image instanceof enchant.widget.Ninepatch) {
1241                     this._image.width = width;
1242                 }
1243                 if (this._pushedimage instanceof enchant.widget.Ninepatch) {
1244                     this._pushedimage.width = width;
1245                 }
1246                 this.refresh();
1247             }
1248         },
1249         /**
1250          * height of button
1251          * @type number
1252          */
1253         height: {
1254             get: function() {
1255                 return this._height;
1256             },
1257             set: function(height) {
1258                 this._style.height = (this._height = height) + 'px';
1259                 if (this._image instanceof enchant.widget.Ninepatch) {
1260                     this._image.height = height;
1261                 }
1262                 if (this._pushedimage instanceof enchant.widget.Ninepatch) {
1263                     this._pushedimage.height = height;
1264                 }
1265                 this.refresh();
1266             }
1267         },
1268         /**
1269          * Button background.
1270          * @type {enchant.Surface}
1271          */
1272         image: {
1273             get: function() {
1274                 return this._image;
1275             },
1276             set: function(image) {
1277                 if (image == this._image) {
1278                     return;
1279                 }
1280                 this.background = image;
1281                 this._image = image;
1282             }
1283         },
1284         /**
1285          * Background when button is pushed.
1286          * @type {enchant.Surface}
1287          */
1288         pushedimage: {
1289             get: function() {
1290                 return this._pushedimage;
1291             },
1292             set: function(image) {
1293                 if (image == this._pushedimage) {
1294                     return;
1295                 }
1296                 this._pushedimage = image;
1297             }
1298         },
1299         /**
1300          * Button content
1301          * @type {String}
1302          */
1303         content: {
1304             get: function() {
1305                 return this._rawContent;
1306             },
1307             set: function(content) {
1308                 this._rawContent = content;
1309                 var font = enchant.widget._env.buttonFont;
1310                 content = enchant.widget.parseContent(content, font);
1311                 if (this._content) {
1312                     this.removeChild(this._content);
1313                 }
1314                 this.addChild(content);
1315                 this._content = content;
1316                 this.refresh();
1317             }
1318         }
1319     });
1320 
1321     /**
1322      * @scope enchant.widget.Alert
1323      */
1324     enchant.widget.Alert = enchant.Class.create(enchant.widget.EntityGroup, {
1325         /**
1326          * Alert dialog.
1327          * Use from normal {@link enchant.widget.AlertScene}.
1328          * @param {*} content Content to display.
1329          * @param {String} ac Label for acceptance button.
1330          * @see enchant.widget.AlertScene
1331          * @constructs
1332          * @extends enchant.widget.EntityGroup
1333          */
1334         initialize: function(content, ac) {
1335             var core = enchant.Core.instance;
1336             var dialogwidth = enchant.widget._env.dialogWidth;
1337             var dialogheight = enchant.widget._env.dialogHeight;
1338             enchant.widget.EntityGroup.call(this, dialogwidth, dialogheight);
1339             var margin = enchant.widget._env.dialogMargin;
1340 
1341             content = enchant.widget.parseContent(content);
1342             content.alignHorizontalCenterIn(this).alignTopIn(this, margin);
1343 
1344             var accept = new enchant.widget.Button(ac);
1345             accept.alignHorizontalCenterIn(this).alignBottomIn(this, margin);
1346 
1347             var that = this;
1348             accept.addEventListener(enchant.Event.TOUCH_END, function() {
1349                 that.dispatchEvent(new enchant.Event(enchant.Event.ACCEPT));
1350             });
1351 
1352             var np = new enchant.widget.Ninepatch(this.width, this.height);
1353             np.src = core.assets['dialog.png'];
1354             this.background = np;
1355 
1356             this._content = content;
1357             this._accept = accept;
1358 
1359             this.addChild(content);
1360             this.addChild(accept);
1361         },
1362         /**
1363          * Function executed when agreement button is pushed.
1364          * @type {Function}
1365          */
1366         onaccept: function() {
1367         }
1368     });
1369 
1370     /**
1371      * @scope enchant.widget.Confirm
1372      */
1373     enchant.widget.Confirm = enchant.Class.create(enchant.widget.EntityGroup, {
1374         /**
1375          * Confirm dialog.
1376          * Use from normal {@link enchant.widget.ConfirmScene}.
1377          * @param {*} content Content to display.
1378          * @param {String} ac Label for agreement button.
1379          * @param {String} ig Label for cancel button.
1380          * @see enchant.widget.ConfirmScene
1381          * @constructs
1382          * @extends enchant.widget.EntityGroup
1383          */
1384         initialize: function(content, ac, ig) {
1385             var core = enchant.Core.instance;
1386             var dialogwidth = enchant.widget._env.dialogWidth;
1387             var dialogheight = enchant.widget._env.dialogHeight;
1388             enchant.widget.EntityGroup.call(this, dialogwidth, dialogheight);
1389             var margin = enchant.widget._env.dialogMargin;
1390 
1391             var content = enchant.widget.parseContent(content);
1392             content.alignHorizontalCenterIn(this).alignTopIn(this, margin);
1393 
1394             var cancel = new enchant.widget.Button(ig);
1395             cancel.alignLeftIn(this, margin).alignBottomIn(this, margin);
1396 
1397             var accept = new enchant.widget.Button(ac);
1398             accept.alignRightIn(this, margin).alignBottomIn(this, margin);
1399 
1400             var that = this;
1401             cancel.addEventListener(enchant.Event.TOUCH_END, function() {
1402                 that.dispatchEvent(new enchant.Event(enchant.Event.CANCEL));
1403             });
1404             accept.addEventListener(enchant.Event.TOUCH_END, function() {
1405                 that.dispatchEvent(new enchant.Event(enchant.Event.ACCEPT));
1406             });
1407 
1408             var np = new enchant.widget.Ninepatch(this.width, this.height);
1409             np.src = core.assets['dialog.png'];
1410             this.background = np;
1411 
1412             this._content = content;
1413             this._cancel = cancel;
1414             this._accept = accept;
1415 
1416             this.addChild(content);
1417             this.addChild(cancel);
1418             this.addChild(accept);
1419         },
1420         /**
1421          * Function executed when cancel button is pushed.
1422          * @type {Function}
1423          */
1424         oncancel: function() {
1425         },
1426         /**
1427          * Function executed when agreement button is pushed.
1428          */
1429         onaccept: function() {
1430         }
1431     });
1432 
1433     /**
1434      * @scope enchant.widget.Prompt
1435      */
1436     enchant.widget.Prompt = enchant.Class.create(enchant.widget.Confirm, {
1437         /**
1438          * Prompt dialog.
1439          * Use from normal {@link enchant.widget.PromptScene}.
1440          * @param {*} content Content to display.
1441          * @param {String} ac Label for agreement label.
1442          * @param {String} ig Label for cancel button.
1443          * @see enchant.widget.PromptScene
1444          * @constructs
1445          * @extends enchant.widget.Confirm
1446          */
1447         initialize: function(content, ac, ig, placeholder) {
1448             enchant.widget.Confirm.call(this, content, ac, ig);
1449             var margin = enchant.widget._env.dialogMargin;
1450             var input = this._input = new enchant.widget.InputTextBox();
1451             input.width = this.width / 4 * 3;
1452             input.placeholder = placeholder;
1453             input.alignHorizontalCenterIn(this).alignBottomOf(this._content, margin);
1454             this.addChild(input);
1455         },
1456         /**
1457          * content of prompt.
1458          */
1459         value: {
1460             get: function() {
1461                 return this._input.value;
1462             },
1463             set: function(value) {
1464                 this._input.value = value;
1465             }
1466         }
1467     });
1468 
1469     /**
1470      * @scope enchant.widget.Input
1471      */
1472     enchant.widget.Input = enchant.Class.create(enchant.Entity, {
1473         /**
1474          * Entity containing <input>.
1475          * @param {String} type <input>のtype.
1476          * @constructs
1477          * @extends enchant.Entity
1478          */
1479         initialize: function(type) {
1480             enchant.Entity.call(this);
1481             if (!type) {
1482                 type = 'input';
1483             }
1484             var that = this;
1485             this._input = document.createElement(type);
1486 
1487             this._input.addEventListener('change', function(e) {
1488                 that.dispatchEvent(new enchant.Event(enchant.Event.CHANGE));
1489             });
1490 
1491             this._element = document.createElement('div');
1492             this._element.appendChild(this._input);
1493         },
1494         /**
1495          * Determine whether or not to allow input.
1496          * @type {Boolean}
1497          */
1498         disabled: {
1499             get: function() {
1500                 return this._input.disbaled;
1501             },
1502             set: function(value) {
1503                 this._input.disabled = !!value;
1504             }
1505         }
1506     });
1507 
1508     /**
1509      * @scope enchant.widget.InputTextBox
1510      */
1511     enchant.widget.InputTextBox = enchant.Class.create(enchant.widget.Input, {
1512         /**
1513          * Text box.
1514          * @constructs
1515          * @extends enchant.widget.Input
1516          */
1517         initialize: function() {
1518             enchant.widget.Input.call(this);
1519             this._input.type = 'text';
1520 
1521             var metrics = getElementMetrics(this._element.innerHTML);
1522             this.width = metrics.width;
1523             this.height = metrics.height;
1524 
1525             var that = this;
1526             this._focused = false;
1527 
1528             this._input.addEventListener('focus', function() {
1529                 that._focused = true;
1530             });
1531 
1532             this._input.addEventListener('blur', function() {
1533                 that._focused = false;
1534             });
1535         },
1536         /**
1537          * @type {Number}
1538          */
1539         selectionStart: {
1540             get: function() {
1541                 return this._input.selectionStart;
1542             },
1543             set: function(n) {
1544                 this._input.selectionStart = n;
1545             }
1546         },
1547         /**
1548          * @type {Number}
1549          */
1550         selectionEnd: {
1551             get: function() {
1552                 return this._input.selectionEnd;
1553             },
1554             set: function(n) {
1555                 this._input.selectionEnd = n;
1556             }
1557         },
1558         /**
1559          * @type {Boolean}
1560          */
1561         focused: {
1562             get: function() {
1563                 return this._focused;
1564             },
1565             set: function(bool) {
1566                 this._focused = bool;
1567                 if (bool) {
1568                     this._input.focus();
1569                 } else {
1570                     this._input.blur();
1571                 }
1572             }
1573         },
1574         /**
1575          * Place holder.
1576          * @type {String}
1577          */
1578         placeholder: {
1579             get: function() {
1580                 return this._input.placeholder;
1581             },
1582             set: function(value) {
1583                 this._input.placeholder = value;
1584             }
1585         },
1586         /**
1587          * Level input into text box.
1588          * @type {String}
1589          */
1590         value: {
1591             get: function() {
1592                 return this._input.value;
1593             },
1594             set: function(value) {
1595                 this._input.value = value;
1596             }
1597         },
1598         /**
1599          * Text box width.
1600          * @type {Number}
1601          */
1602         width: {
1603             get: function() {
1604                 return this._width;
1605             },
1606             set: function(width) {
1607                 this._width = width;
1608                 this._style.width = width + 'px';
1609                 this._input.style.width = width + 'px';
1610             }
1611         },
1612         /**
1613          * Text box height.
1614          * @type {Number}
1615          */
1616         height: {
1617             get: function() {
1618                 return this._height;
1619             },
1620             set: function(height) {
1621                 this._height = height;
1622                 this._style.height = height + 'px';
1623                 this._input.style.height = height + 'px';
1624             }
1625         }
1626     });
1627 
1628     /**
1629      * @scope enchant.widget.InputSelectBox
1630      */
1631     enchant.widget.InputSelectBox = enchant.Class.create(enchant.widget.Input, {
1632         /**
1633          * Select box.
1634          * @param {*} option Levels set in options.
1635          * @example
1636          *   var option = {
1637          *       male: 'Man',
1638          *       female: 'Woman'
1639          *   };
1640          *   var selectbox = new InputSelectBox(option);
1641          *
1642          * @constructs
1643          * @extends enchant.widget.Input
1644          */
1645         initialize: function(table) {
1646             enchant.widget.Input.call(this, 'select');
1647             var content;
1648             for (var prop in table) {
1649                 content = table[prop];
1650                 opt = document.createElement('option');
1651                 opt.value = prop;
1652                 opt.textContent = content;
1653                 this._input.appendChild(opt);
1654             }
1655 
1656             this._input.addEventListener('mousedown', function(e) {
1657                 e.stopPropagation();
1658             });
1659 
1660             var metrics = getElementMetrics(this._element.innerHTML);
1661             this.width = metrics.width;
1662             this.height = metrics.height;
1663         },
1664         /**
1665          * Level selected.
1666          * @type {String}
1667          */
1668         selected: {
1669             get: function() {
1670                 return this._input.options[this._input.selectedIndex].value;
1671             },
1672             set: function(value) {
1673                 var opt;
1674                 for (var i = 0, l = this._input.options.length; i < l; i++) {
1675                     opt = this._input.options[i];
1676                     if (opt.getAttribute('value') == value) {
1677                         opt.selected = true;
1678                     } else {
1679                         opt.selected = false;
1680                     }
1681                 }
1682                 return value;
1683             }
1684         }
1685     });
1686 
1687     /**
1688      * @scope enchant.widget.InputCheckBox
1689      */
1690     enchant.widget.InputCheckBox = enchant.Class.create(enchant.widget.Input, {
1691         /**
1692          * Checkbox.
1693          * @param {String} value Level.
1694          * @param {String} text Label text.
1695          * @param {Boolean} checked Whether or not it is checked.
1696          * @constructs
1697          * @extends enchant.widget.Input
1698          */
1699         initialize: function(value, text, checked) {
1700             enchant.widget.Input.call(this);
1701             this._input.type = 'checkbox';
1702             var label = document.createDocumentFragment();
1703             label.textContent = text;
1704             this._element.appendChild(label);
1705             this.checked = checked;
1706             var metrics = getElementMetrics(this._element.innerHTML);
1707             this.width = metrics.width;
1708             this.height = metrics.height;
1709         },
1710         /**
1711          * Whether or not it is checked.
1712          * @type {Boolean}
1713          */
1714         checked: {
1715             get: function() {
1716                 return this._input.checked;
1717             },
1718             set: function(value) {
1719                 this._input.checked = !!value;
1720             }
1721         }
1722     });
1723 
1724     /**
1725      * @scope enchant.widget.InputTextArea
1726      */
1727     enchant.widget.InputTextArea = enchant.Class.create(enchant.Entity, {
1728         /**
1729          * Text area.
1730          * @constructs
1731          * @extends enchant.Entity
1732          */
1733         initialize: function() {
1734             enchant.Entity.call(this);
1735             var textarea = this._textarea = document.createElement('textarea');
1736             textarea.style.resize = 'none';
1737             textarea.style.font = enchant.widget._env.textareaFont;
1738             this._element = document.createElement('div');
1739             this._element.appendChild(textarea);
1740             var that = this;
1741             this._focused = false;
1742             this._next = null;
1743             this._prev = null;
1744 
1745             var that = this;
1746             this.addEventListener(enchant.Event.TOUCH_END, function() {
1747                 this._updateVerticalDist();
1748             });
1749             this._textarea.addEventListener('input', function() {
1750                 that._updateVerticalDist();
1751             });
1752             this._textarea.addEventListener('focus', function() {
1753                 that._focused = true;
1754             });
1755             this._textarea.addEventListener('blur', function() {
1756                 that._focused = false;
1757             });
1758             this._textarea.addEventListener('change', function(e) {
1759                 that.dispatchEvent(new enchant.Event(enchant.Event.CHANGE));
1760             });
1761         },
1762         _updateVerticalDist: function() {
1763             var w = this.value.split('\n');
1764             var n = this.selectionStart;
1765             var s = 0;
1766             for (var i = 0, l = w.length; i < l; i++) {
1767                 n -= w[i].length + 1;
1768                 if (n < 0) {
1769                     break;
1770                 }
1771                 s += w[i].length + 1;
1772             }
1773             var ind = this.selectionStart - s;
1774             if (0 < i) {
1775                 this._prev = -Math.max(w[i - 1].length, ind) - 1;
1776             } else {
1777                 this._prev = -ind;
1778             }
1779             if (i < l - 1) {
1780                 this._next = w[i].length - ind + Math.min(ind, w[i + 1].length) + 1;
1781             } else {
1782                 this._next = w[i].length - ind;
1783             }
1784         },
1785         /**
1786          * @type {Number}
1787          */
1788         selectionStart: {
1789             get: function() {
1790                 return this._textarea.selectionStart;
1791             },
1792             set: function(n) {
1793                 this._textarea.selectionStart = n;
1794             }
1795         },
1796         /**
1797          * @type {Number}
1798          */
1799         selectionEnd: {
1800             get: function() {
1801                 return this._textarea.selectionEnd;
1802             },
1803             set: function(n) {
1804                 this._textarea.selectionEnd = n;
1805             }
1806         },
1807         /**
1808          * @type {Boolean}
1809          */
1810         focused: {
1811             get: function() {
1812                 return this._focused;
1813             },
1814             set: function(bool) {
1815                 this._focused = bool;
1816                 if (bool) {
1817                     this._textarea.focus();
1818                 } else {
1819                     this._textarea.blur();
1820                 }
1821             }
1822         },
1823         /**
1824          [lang]ja]
1825          * プレースホルダ.
1826          [/lang]
1827          * Placeholder.
1828          * @type {String}
1829          */
1830         placeholder: {
1831             get: function() {
1832                 return this._textarea.placeholder;
1833             },
1834             set: function(value) {
1835                 this._textarea.placeholder = value;
1836             }
1837         },
1838         /**
1839          * Level input into text area.
1840          * @type {String}
1841          */
1842         value: {
1843             get: function() {
1844                 return this._textarea.value;
1845             },
1846             set: function(value) {
1847                 this._textarea.value = value;
1848             }
1849         },
1850         /**
1851          * Text area width.
1852          * @type {Number}
1853          */
1854         width: {
1855             get: function() {
1856                 return this._width;
1857             },
1858             set: function(width) {
1859                 this._width = width;
1860                 this._style.width = width + 'px';
1861                 this._textarea.style.width = width + 'px';
1862             }
1863         },
1864         /**
1865          * Text area height.
1866          * @type {Number}
1867          */
1868         height: {
1869             get: function() {
1870                 return this._height;
1871             },
1872             set: function(height) {
1873                 this._height = height;
1874                 this._style.height = height + 'px';
1875                 this._textarea.style.height = height + 'px';
1876             }
1877         }
1878     });
1879 
1880     /**
1881      * @scope enchant.widget.AlertScene
1882      */
1883     enchant.widget.AlertScene = enchant.Class.create(enchant.widget.Modal, {
1884         /**
1885          * Alert scene.
1886          * Interrupt other input, display alert.
1887          * @param {*} content Content to display.
1888          * @param {String} acceptName Label for acceptance button.
1889          * @example
1890          *     var alert = new ConfirmScene('Not possible', 'OK');
1891          *     alert.callback = function() {
1892          *     };
1893          *     alert.onaccept = function() {
1894          *     };
1895          * @constructs
1896          * @extends enchant.widget.Modal
1897          */
1898         initialize: function(content, acceptName) {
1899             var core = enchant.Core.instance;
1900             enchant.widget.Modal.call(this);
1901             this._onaccept = function() {
1902             };
1903             this.callback = function() {
1904             };
1905             acceptName = acceptName || enchant.widget._env.acceptName;
1906 
1907             var alert = new enchant.widget.Alert(content, acceptName);
1908             this.addChild(alert);
1909             alert.alignHorizontalCenterIn(this).alignVerticalCenterIn(this);
1910 
1911             var scene = this;
1912 
1913             alert.onaccept = function() {
1914                 core.popScene();
1915                 scene._onaccept.apply(this, arguments);
1916             };
1917             alert.addEventListener(enchant.Event.ACCEPT, function() {
1918                 scene.callback();
1919             });
1920             this.addEventListener(enchant.Event.ENTER, function() {
1921                 Effect.popup.call(alert);
1922             });
1923         },
1924         /**
1925          * @type {Function}
1926          */
1927         onaccept: {
1928             get: function() {
1929                 return this._onaccept;
1930             },
1931             set: function(func) {
1932                 this._onaccept = func;
1933             }
1934         }
1935     });
1936 
1937     /**
1938      * @scope enchant.widget.ConfirmScene
1939      */
1940     enchant.widget.ConfirmScene = enchant.Class.create(enchant.widget.Modal, {
1941         /**
1942          * Confirm scene.
1943          * Interrupt other input, display selection screen.
1944          * @param {*} content Content to display.
1945          * @param {String} acceptName Label for agreement button.
1946          * @param {String} cancelName Label for cancel button.
1947          * @example
1948          *     var confirm = new ConfirmScene('Okay?', 'OK', 'NO');
1949          *     confirm.callback = function(bool) {
1950          *        // true will return for accept, false will return for cancel.
1951          *     };
1952          *     // Processing for cancel and accept can be set separately.
1953          *     confirm.oncancel = function() {
1954          *     };
1955          *     confirm.onaccept = function() {
1956          *     };
1957          * @constructs
1958          * @extends enchant.widget.Modal
1959          */
1960         initialize: function(content, acceptName, cancelName) {
1961             var core = enchant.Core.instance;
1962             enchant.widget.Modal.call(this);
1963             this._oncancel = function() {
1964             };
1965             this._onaccept = function() {
1966             };
1967             this.callback = function() {
1968             };
1969             cancelName = cancelName || enchant.widget._env.cancelName;
1970             acceptName = acceptName || enchant.widget._env.acceptName;
1971 
1972             var confirm = new enchant.widget.Confirm(content, acceptName, cancelName);
1973             this.addChild(confirm);
1974             confirm.alignHorizontalCenterIn(this).alignVerticalCenterIn(this);
1975             var scene = this;
1976 
1977             confirm.oncancel = function() {
1978                 core.popScene();
1979                 scene._oncancel.apply(this, arguments);
1980             };
1981             confirm.onaccept = function() {
1982                 core.popScene();
1983                 scene._onaccept.apply(this, arguments);
1984             };
1985             confirm.addEventListener(enchant.Event.CANCEL, function() {
1986                 scene.callback(false);
1987             });
1988             confirm.addEventListener(enchant.Event.ACCEPT, function() {
1989                 scene.callback(true);
1990             });
1991             this.addEventListener(enchant.Event.ENTER, function() {
1992                 Effect.popup.call(confirm);
1993             });
1994         },
1995         /**
1996          * @type {Function}
1997          */
1998         oncancel: {
1999             get: function() {
2000                 return this._oncancel;
2001             },
2002             set: function(func) {
2003                 this._oncancel = func;
2004             }
2005         },
2006         /**
2007          * @type {Function}
2008          */
2009         onaccept: {
2010             get: function() {
2011                 return this._onaccept;
2012             },
2013             set: function(func) {
2014                 this._onaccept = func;
2015             }
2016         }
2017     });
2018 
2019     /**
2020      * @scope enchant.widget.PromptScene
2021      */
2022     enchant.widget.PromptScene = enchant.Class.create(enchant.widget.Modal, {
2023         /**
2024          * Confirm scene.
2025          * Interrupt other input and display input screen.
2026          * When you wish to allow input to multiple lines, use {@link enchant.widget.InputScene}.
2027          * @param {*} content Content to display.
2028          * @param {String} acceptName Label for agreement button.
2029          * @param {String} cancelName Label for cancel button.
2030          * @param {String} placeholder Placeholder.
2031          * @example
2032          *     var confirm = new PromptScene('Input name', 'OK', 'cancel');
2033          *     confirm.placeholder = 'Name';
2034          *     confirm.callback = function(text) {
2035          *         // Input array will be returned for accept, whereas null will be returned for cancel.
2036          *     };
2037          *     // Processing for cancel and accept can be set separately.
2038          *     confirm.oncancel = function() {
2039          *     };
2040          *     confirm.onaccept = function(text) {
2041          *     };
2042          * @see enchant.widget.InputScene
2043          * @constructs
2044          * @extends enchant.widget.Modal
2045          */
2046         initialize: function(content, acceptName, cancelName, placeholder) {
2047             var core = enchant.Core.instance;
2048             var margin = enchant.widget._env.dialogMargin;
2049             enchant.widget.Modal.call(this);
2050             cancelName = cancelName || enchant.widget._env.cancelName;
2051             acceptName = acceptName || enchant.widget._env.acceptName;
2052             this.callback = function() {
2053             };
2054             this._oncancel = function() {
2055             };
2056             this._onaccept = function() {
2057             };
2058             placeholder = placeholder || '';
2059 
2060             var prompt = this._prompt = new enchant.widget.Prompt(content, acceptName, cancelName, placeholder);
2061             prompt.alignHorizontalCenterIn(this).alignVerticalCenterIn(this);
2062             this.addChild(prompt);
2063             var scene = this;
2064 
2065             prompt.oncancel = function() {
2066                 core.popScene();
2067                 scene._oncancel.apply(this, arguments);
2068             };
2069             prompt.onaccept = function() {
2070                 core.popScene();
2071                 scene._onaccept.apply(this, arguments);
2072             };
2073             prompt.addEventListener(enchant.Event.CANCEL, function() {
2074                 scene.callback(null);
2075             });
2076             prompt.addEventListener(enchant.Event.ACCEPT, function() {
2077                 scene.callback(prompt.value);
2078             });
2079             this.addEventListener(enchant.Event.ENTER, function() {
2080                 Effect.popup.call(prompt);
2081             });
2082             this.addEventListener(enchant.Event.UP_BUTTON_DOWN, function() {
2083                 if (prompt._input.focused) {
2084                     prompt._input.selectionStart = 0;
2085                     prompt._input.selectionEnd = 0;
2086                 }
2087             });
2088             this.addEventListener(enchant.Event.DOWN_BUTTON_DOWN, function() {
2089                 if (prompt._input.focused) {
2090                     prompt._input.selectionStart = prompt._input.value.length;
2091                     prompt._input.selectionEnd = prompt._input.value.length;
2092                 }
2093             });
2094             this.addEventListener(enchant.Event.LEFT_BUTTON_DOWN, function() {
2095                 if (prompt._input.focused) {
2096                     prompt._input.selectionStart -= 1;
2097                     prompt._input.selectionEnd -= 1;
2098                 }
2099             });
2100             this.addEventListener(enchant.Event.RIGHT_BUTTON_DOWN, function() {
2101                 if (prompt._input.focused) {
2102                     prompt._input.selectionStart += 1;
2103                 }
2104             });
2105         },
2106         /**
2107          * content of prompt
2108          * @type {String}
2109          */
2110         value: {
2111             get: function() {
2112                 return this._prompt.value;
2113 
2114             },
2115             set: function(value) {
2116                 this._prompt.value = value;
2117             }
2118         },
2119         /**
2120          * @type {Function}
2121          */
2122         oncancel: {
2123             get: function() {
2124                 return this._oncancel;
2125             },
2126             set: function(func) {
2127                 this._oncancel = func;
2128             }
2129         },
2130         /**
2131          * @type {Function}
2132          */
2133         onaccept: {
2134             get: function() {
2135                 return this._onaccept;
2136             },
2137             set: function(func) {
2138                 this._onaccept = func;
2139             }
2140         }
2141     });
2142 
2143     /**
2144      * @scope enchant.widget.InputScene
2145      */
2146     enchant.widget.InputScene = enchant.Class.create(enchant.widget.Modal, {
2147         /**
2148          * Information.
2149          * Interrupts other input and displays input screen.
2150          * Unlike {@link enchant.widget.PromptScene}, you can input to multiple lines.
2151          * @param {*} content Content to display.
2152          * @param {String} acceptName Label for agreement button.
2153          * @param {String} cancelName Label for cancel button.
2154          * @param {String} placeholder Placeholder.
2155          * @example
2156          *     var input = new InputScene('New Tweet', 'Tweet', 'Stop', '@twitter ');
2157          *     input.callback = function(text) {
2158          *         // Input array will be returned for accept, and null for cancel.
2159          *     };
2160          *     // Processing for cancel and accept can be set separately.
2161          *     input.oncancel = function() {
2162          *     };
2163          *     input.onaccept = function(text) {
2164          *     };
2165          * @constructs
2166          * @extends enchant.widget.Modal
2167          */
2168         initialize: function(text, acceptName, cancelName, placeholder) {
2169             var core = enchant.Core.instance;
2170             var minheight = enchant.widget._env.inputMinHeight;
2171             var maxheight = enchant.widget._env.inputMaxHeight;
2172             var dh = maxheight - minheight;
2173             this.callback = function() {
2174             };
2175             this._oncancel = function() {
2176             };
2177             this._onaccept = function() {
2178             };
2179             this._menu = null;
2180             cancelName = cancelName || enchant.widget._env.cancelName;
2181             acceptName = acceptName || enchant.widget._env.acceptName;
2182             placeholder = placeholder || '';
2183 
2184             enchant.widget.Modal.call(this);
2185             var scene = this;
2186 
2187             var cancel = new enchant.widget.Button(cancelName);
2188             var accept = new enchant.widget.Button(acceptName);
2189             var bar = new enchant.widget.NavigationBar(text, cancel, accept);
2190             this.addChild(bar);
2191             var textarea = this._textarea = new enchant.widget.InputTextArea();
2192             textarea.y += bar.height;
2193             textarea.width = core.width;
2194             textarea.height = maxheight;
2195             textarea.placeholder = placeholder;
2196             textarea.oncancel = function() {
2197                 core.popScene();
2198                 scene._oncancel.apply(this, arguments);
2199             };
2200             textarea.onaccept = function() {
2201                 core.popScene();
2202                 scene._onaccept.apply(this, arguments);
2203             };
2204             this.addChild(textarea);
2205 
2206             var _area = textarea._textarea;
2207             _area.onfocus = function() {
2208                 Effect.resizeTo.call(textarea, core.width, minheight, 5, enchant.Easing.QUAD_EASEOUT);
2209                 if (scene._menu != null) {
2210                     scene._menu.tl.moveBy(0, -dh, 5, enchant.Easing.QUAD_EASEOUT);
2211                 }
2212             };
2213             _area.onblur = function() {
2214                 Effect.resizeTo.call(textarea, core.width, maxheight, 5, enchant.Easing.QUAD_EASEOUT);
2215                 if (scene._menu != null) {
2216                     scene._menu.tl.moveBy(0, dh, 5, enchant.Easing.QUAD_EASEOUT);
2217                 }
2218             };
2219             cancel.addEventListener(enchant.Event.TOUCH_END, function() {
2220                 textarea.dispatchEvent(new enchant.Event(enchant.Event.CANCEL));
2221                 scene.callback(null);
2222             });
2223             accept.addEventListener(enchant.Event.TOUCH_END, function() {
2224                 textarea.dispatchEvent(new enchant.Event(enchant.Event.ACCEPT));
2225                 scene.callback(textarea.value);
2226             });
2227             this.addEventListener(enchant.Event.UP_BUTTON_DOWN, function() {
2228                 if (textarea.focused) {
2229                     textarea.selectionStart += textarea._prev;
2230                     textarea.selectionEnd += textarea._prev;
2231                     textarea._updateVerticalDist();
2232                 }
2233             });
2234             this.addEventListener(enchant.Event.DOWN_BUTTON_DOWN, function() {
2235                 if (textarea.focused) {
2236                     textarea.selectionStart += textarea._next;
2237                     textarea._updateVerticalDist();
2238                 }
2239             });
2240             this.addEventListener(enchant.Event.LEFT_BUTTON_DOWN, function() {
2241                 if (textarea.focused) {
2242                     textarea.selectionStart -= 1;
2243                     textarea.selectionEnd -= 1;
2244                     textarea._updateVerticalDist();
2245                 }
2246             });
2247             this.addEventListener(enchant.Event.RIGHT_BUTTON_DOWN, function() {
2248                 if (textarea.focused) {
2249                     textarea.selectionStart += 1;
2250                     textarea._updateVerticalDist();
2251                 }
2252             });
2253         },
2254         /**
2255          * @type {*}
2256          */
2257         menu: {
2258             get: function() {
2259                 return this._menu;
2260             },
2261             set: function(menu) {
2262                 if (this._menu) {
2263                     this.removeChild(this._menu);
2264                 }
2265                 this.x = 0;
2266                 this.y = enchant.widget._env.itemHeight + enchant.widget._env.inputMaxHeight;
2267                 this.addChild(menu);
2268                 this._menu = menu;
2269             }
2270         },
2271         /**
2272          * Level input into text area.
2273          * @type {String}
2274          */
2275         value: {
2276             get: function() {
2277                 return this._textarea.value;
2278             },
2279             set: function(value) {
2280                 this._textarea.value = value;
2281             }
2282         },
2283         /**
2284          * @type {String}
2285          */
2286         placeholder: {
2287             get: function() {
2288                 return this._textarea.placeholder;
2289             },
2290             set: function(str) {
2291                 this._textarea.placeholder = str;
2292             }
2293         },
2294         /**
2295          * @type {Function}
2296          */
2297         oncancel: {
2298             get: function() {
2299                 return this._oncancel;
2300             },
2301             set: function(func) {
2302                 this._oncancel = func;
2303             }
2304         },
2305         /**
2306          * @type {Function}
2307          */
2308         onaccept: {
2309             get: function() {
2310                 return this._onaccept;
2311             },
2312             set: function(func) {
2313                 this._onaccept = func;
2314             }
2315         }
2316     });
2317 
2318     /**
2319      * @scope enchant.widget.ListElement
2320      */
2321     enchant.widget.ListElement = enchant.Class.create(enchant.widget.EntityGroup, {
2322         /**
2323          * List items.
2324          * Normally {@link enchant.widget.ListItem} or {@link enchant.widget.ListItemVertical} are used.
2325          * @param {Number} width Element width.
2326          * @param {Number} height Element height.
2327          * @see enchant.widget.ListItem
2328          * @see enchant.widget.ListItemVertical
2329          * @constructs
2330          * @extends enchant.widget.EntityGroup
2331          */
2332         initialize: function(width, height) {
2333             enchant.widget.EntityGroup.call(this, width, height);
2334             this._content;
2335             this._rawContent;
2336         },
2337         /**
2338          * Renew change.
2339          */
2340         refresh: function() {
2341             var content = this._content;
2342             var margin = enchant.widget._env.listItemMargin;
2343             if (content) {
2344                 content.alignLeftIn(this, margin).alignVerticalCenterIn(this);
2345             }
2346             this.background = this._background;
2347         },
2348         /**
2349          * ListElement content.
2350          * @type {enchant.Entity[]}
2351          */
2352         content: {
2353             get: function() {
2354                 return this._rawContent;
2355             },
2356             set: function(content) {
2357                 this._rawContent = content;
2358                 content = enchant.widget.parseContent(content);
2359                 if (this._content) {
2360                     this.removeChild(this._content);
2361                 }
2362                 this.addChild(content);
2363                 this._content = content;
2364                 this.refresh();
2365             }
2366         },
2367         /**
2368          * @type {Number}
2369          */
2370         width: {
2371             get: function() {
2372                 return this._width;
2373             },
2374             set: function(width) {
2375                 this._style.width = (this._width = width) + 'px';
2376                 if (this.background instanceof enchant.widget.Ninepatch) {
2377                     this.background.width = this.width;
2378                 }
2379                 if (this._content) {
2380                     this.refresh();
2381                 }
2382             }
2383         },
2384         /**
2385          * @type {Number}
2386          */
2387         height: {
2388             get: function() {
2389                 return this._height;
2390             },
2391             set: function(height) {
2392                 this._style.height = (this._height = height) + 'px';
2393                 if (this.background instanceof enchant.widget.Ninepatch) {
2394                     this.background.height = this.height;
2395                 }
2396                 if (this._content) {
2397                     this.refresh();
2398                 }
2399             }
2400         }
2401     });
2402 
2403     /**
2404      * @scope enchant.widget.ListItem
2405      */
2406     enchant.widget.ListItem = enchant.Class.create(enchant.widget.ListElement, {
2407         /**
2408          * List elements.
2409          * Icons and leftmost buttons can be set.
2410          * Use {@link enchant.widget.ListItemVertical} to set items lined up vertically.
2411          * @param {Number} width Element width.
2412          * @param {Number} height Element height.
2413          * @param {*} [content] ListItem content.
2414          * @param {enchant.Sprite|enchant.Surface} [icon] ListItem icon.
2415          * @param {enchant.Sprite|enchant.Surface} [icon] ListItem right side icon.
2416          * @see enchant.widget.ListItemVertical
2417          * @constructs
2418          * @extends enchant.widget.ListElement
2419          */
2420         initialize: function(width, height, content, icon, rightIcon) {
2421             var core = enchant.Core.instance;
2422             width = width || core.width;
2423             height = height || enchant.widget._env.itemHeight;
2424             content = content || '';
2425             enchant.widget.ListElement.call(this, width, height);
2426             this._icon;
2427             this._rawIcon;
2428             this._rightIcon;
2429             this._rawRightIcon;
2430             this.content = content;
2431             if (icon) {
2432                 this.icon = icon;
2433             }
2434             if (rightIcon) {
2435                 this.rightIcon = rightIcon;
2436             }
2437             var np = new enchant.widget.Ninepatch(this.width, this.height);
2438             np.src = core.assets['listItemBg.png'];
2439             this.background = np;
2440         },
2441         /**
2442          * Renew changes.
2443          */
2444         refresh: function() {
2445             var icon = this._icon;
2446             var content = this._content;
2447             var right = this._rightIcon;
2448             var margin = enchant.widget._env.listItemMargin;
2449             if (icon) {
2450                 Adjust.fitToY.call(icon, this, margin, margin);
2451                 icon.alignLeftIn(this, margin).alignVerticalCenterIn(this);
2452                 if (content) {
2453                     content.alignRightOf(icon, margin).alignVerticalCenterIn(this);
2454                 }
2455             } else if (content) {
2456                 content.alignLeftIn(this, margin).alignVerticalCenterIn(this);
2457             }
2458             if (right) {
2459                 right.alignRightIn(this, margin).alignVerticalCenterIn(this);
2460             }
2461         },
2462         /**
2463          * Icon.
2464          * It appear on the left.
2465          * @type {enchant.Sprite|enchant.Surface}
2466          */
2467         icon: {
2468             get: function() {
2469                 return this._rawIcon;
2470             },
2471             set: function(icon) {
2472                 this._rawIcon = icon;
2473                 icon = enchant.widget.parseContent(icon);
2474                 if (this._icon) {
2475                     this.removeChild(this._icon);
2476                 }
2477                 this.addChild(icon);
2478                 this._icon = icon;
2479                 this.refresh();
2480             }
2481         },
2482         /**
2483          * Icon on the right.
2484          * It appear on the right.
2485          * @type {enchant.Sprite|enchant.Surface}
2486          */
2487         rightIcon: {
2488             get: function() {
2489                 return this._rawRightIcon;
2490             },
2491             set: function(right) {
2492                 this._rawRightIcon = right;
2493                 right = enchant.widget.parseContent(right);
2494                 if (this._rightIcon) {
2495                     this.removeChild(this._rightIcon);
2496                 }
2497                 this.addChild(right);
2498                 this._rightIcon = right;
2499                 this.refresh();
2500             }
2501         }
2502     });
2503 
2504     /**
2505      * @scope enchant.widget.ListItemVertical
2506      */
2507     enchant.widget.ListItemVertical = enchant.Class.create(enchant.widget.ListElement, {
2508         /**
2509          * List items.
2510          * Header and footer can be set.
2511          * @param {Number} width Element width.
2512          * @param {Number} height Element height.
2513          * @param {*} [content] ListItemVertical content.
2514          * @param {*} [header] ListItemVertical header.
2515          * @param {*} [footer] ListItemVertical footer.
2516          * @constructs
2517          * @extends enchant.widget.ListElement
2518          */
2519         initialize: function(width, height, content, header, footer) {
2520             var core = enchant.Core.instance;
2521             enchant.widget.ListElement.call(this, width, height);
2522             this._header;
2523             this._rawHeader;
2524             this._footer;
2525             this._rawFooter;
2526             if (content) {
2527                 this.content = content;
2528             }
2529             if (header) {
2530                 this.header = header;
2531             }
2532             if (footer) {
2533                 this.footer = footer;
2534             }
2535             this.refresh();
2536             var np = new enchant.widget.Ninepatch(this.width, this.height);
2537             np.src = core.assets['listItemBg.png'];
2538             this.background = np;
2539         },
2540         /**
2541          * Renew change.
2542          */
2543         refresh: function() {
2544             var header = this._header;
2545             var footer = this._footer;
2546             var content = this._content;
2547             var margin = enchant.widget._env.listItemMargin;
2548             if (header) {
2549                 header.alignLeftIn(this, margin).alignTopIn(this, margin);
2550 
2551                 Adjust.fillX.call(content, this, margin);
2552                 if (content) {
2553                     content.alignLeftIn(this, margin).alignBottomOf(header, margin);
2554                 }
2555             } else {
2556                 Adjust.fillX.call(content, this, margin);
2557                 if (content) {
2558                     content.alignLeftIn(this, margin).alignTopIn(this, margin);
2559                 }
2560             }
2561             if (footer) {
2562                 footer.alignLeftIn(this, margin).alignBottomOf(content, margin);
2563             }
2564             var height = 0;
2565             var p;
2566             var scale;
2567             var contents = [ header, content, footer ];
2568             for (prop in contents) {
2569                 p = contents[prop];
2570                 if (p) {
2571                     scale = p.scaleY || 1;
2572                     height += ~~(p.height * scale);
2573                     height += margin * 2;
2574                 }
2575             }
2576             this._style.height = (this._height = height) + 'px';
2577             if (this.background instanceof enchant.widget.Ninepatch) {
2578                 this.background.height = this.height;
2579             }
2580         },
2581         /**
2582          * Header.
2583          * It appear above the content.
2584          * @type {*}
2585          */
2586         header: {
2587             get: function() {
2588                 return this._rawHeader;
2589             },
2590             set: function(header) {
2591                 this._rawHeader = header;
2592                 header = enchant.widget.parseContent(header);
2593                 if (this._header) {
2594                     this.removeChild(this._header);
2595                 }
2596                 this.addChild(header);
2597                 this._header = header;
2598                 this.refresh();
2599             }
2600         },
2601         /**
2602          * Footer.
2603          * It appear below the content.
2604          * @type {*}
2605          */
2606         footer: {
2607             get: function() {
2608                 return this._rawFooter;
2609             },
2610             set: function(footer) {
2611                 this._rawFooter = footer;
2612                 footer = enchant.widget.parseContent(footer);
2613                 if (this._footer) {
2614                     this.removeChild(this._footer);
2615                 }
2616                 this.addChild(footer);
2617                 this._footer = footer;
2618                 this.refresh();
2619             }
2620         }
2621     });
2622 
2623     /**
2624      * @scope enchant.widget.ScrollView
2625      */
2626     enchant.widget.ScrollView = enchant.Class.create(enchant.widget.EntityGroup, {
2627         /**
2628          * Scroll view.
2629          * Scroll is possible for content set.
2630          * @param {Number} width View width.
2631          * @param {Number} height View height.
2632          * @constructs
2633          * @extends enchant.widget.EntityGroup
2634          */
2635         initialize: function(width, height) {
2636             enchant.widget.EntityGroup.call(this, width, height);
2637             this._style.overflow = 'hidden';
2638             this._content;
2639         },
2640         /**
2641          * ScrollView content.
2642          * @type {enchant.Entity}
2643          */
2644         content: {
2645             get: function() {
2646                 return this._content;
2647             },
2648             set: function(content) {
2649                 if (this._content) {
2650                     this.removeChild(this._content);
2651                 }
2652                 this.addChild(content);
2653                 this._content = content;
2654             }
2655         },
2656         /**
2657          * Scroll content.
2658          * Correct level will become upwards scroll.
2659          * @param {Number} dy Scroll level.
2660          */
2661         scroll: function(dy) {
2662             if (!this._content) {
2663                 return;
2664             }
2665             if (this.height >= this._content.height) {
2666                 this._content.y = 0;
2667                 return;
2668             }
2669             var max = 0
2670             var min = this.height - this._content.height
2671 
2672             var sy = this._content.y + dy;
2673             if (sy > max) {
2674                 dy = max - this._content.y;
2675             } else if (sy < min) {
2676                 dy = min - this._content.y;
2677             }
2678             this._content.y += dy;
2679         }
2680     });
2681 
2682     /**
2683      * @scope enchant.widget.ListView
2684      */
2685     enchant.widget.ListView = enchant.Class.create(enchant.widget.ScrollView, {
2686         /**
2687          * @param {Number} width View width.
2688          * @param {Number} height View height.
2689          * @param {Boolean} draggable Sets whether or not item can be dragged.
2690          * @constructs
2691          * @extends enchant.widget.ScrollView
2692          */
2693         initialize: function(width, height, draggable) {
2694             enchant.widget.ScrollView.call(this, width, height);
2695             var detector = new enchant.widget.GestureDetector(this);
2696             this.draggable = !!draggable;
2697             this.content = [];
2698             var dragging = null;
2699             var dy = 0;
2700             var prev = null;
2701             var next = null;
2702             var pthreshold = 0;
2703             var nthreshold = 0;
2704             this._clipping = true;
2705 
2706             enchant.widget.GestureDetector.gestureEvents.forEach(function(type) {
2707                 this.addEventListener(type, function(e) {
2708                     var item = this.getSelectedItem(e);
2709                     if (item != null) {
2710                         item.dispatchEvent(e);
2711                     }
2712                 });
2713             }, this);
2714 
2715             var removeChild = enchant.widget.EntityGroup.prototype.removeChild;
2716             var insertBefore = enchant.widget.EntityGroup.prototype.insertBefore;
2717 
2718             var that = this;
2719             var checkChangePos = function(direction) {
2720                 var y = dragging.y;
2721                 var my = dragging.height;
2722                 var nextSibling;
2723                 if (prev && y <= pthreshold && direction < 0) {
2724                     prev.y += my;
2725                     removeChild.call(that._content, dragging);
2726                     insertBefore.call(that._content, dragging, prev);
2727                     updateHoldStat(dragging);
2728                 } else if (next && nthreshold <= y && direction > 0) {
2729                     next.y -= my;
2730                     removeChild.call(that._content, dragging);
2731                     var nextSibling = that._content.childNodes[that._content.childNodes.indexOf(next) + 1];
2732                     insertBefore.call(that._content, dragging, nextSibling);
2733                     updateHoldStat(dragging);
2734                 }
2735             };
2736 
2737             var updateHoldStat = function(element) {
2738                 var i = that._content.childNodes.indexOf(element);
2739                 if (i > 0) {
2740                     prev = that._content.childNodes[i - 1];
2741                     pthreshold = prev.y + prev.height - element.height / 2;
2742                 } else {
2743                     prev = null;
2744                 }
2745                 if (i < that._content.childNodes.length - 1) {
2746                     next = that._content.childNodes[i + 1];
2747                     nthreshold = next.y - element.height / 2;
2748                 } else {
2749                     next = null;
2750                 }
2751             };
2752             this.addEventListener(enchant.Event.ENTER_FRAME, function() {
2753                 if (dy != 0) {
2754                     var old = this._content.y;
2755                     this.scroll(dy);
2756                     checkChangePos(-dy);
2757                     dragging.y -= this._content.y - old;
2758                 }
2759             });
2760             this.addEventListener(enchant.Event.HOLD, function(e) {
2761                 if (!this.draggable) {
2762                     return;
2763                 }
2764                 dragging = this.getSelectedItem(e);
2765                 if (dragging == null) {
2766                     return;
2767                 }
2768                 dragging.opacity = 0.8;
2769                 dragging._style.zIndex = 2;
2770                 updateHoldStat(dragging);
2771             });
2772             this.addEventListener(enchant.Event.RELEASE, function() {
2773                 if (!this.draggable || dragging == null) {
2774                     return;
2775                 }
2776                 dy = 0;
2777                 if (prev) {
2778                     dragging.y = prev.y + prev.height;
2779                 } else {
2780                     dragging.y = 0;
2781                 }
2782                 dragging.opacity = 1.0;
2783                 dragging._style.zIndex = 0;
2784                 dragging = null;
2785                 prev = null;
2786                 next = null;
2787             });
2788             var spd = 40;
2789             this.addEventListener(enchant.Event.DRAG, function(e) {
2790                 if (!this.draggable || dragging == null) {
2791                     return;
2792                 }
2793                 checkChangePos(e.dy);
2794                 dragging.y += e.dy;
2795                 if (e.localY < spd) {
2796                     dy = spd - e.localY;
2797                 } else if (this.height - spd < e.localY) {
2798                     dy = this.height - spd - e.localY;
2799                 } else {
2800                     dy = 0;
2801                 }
2802             });
2803             this.addEventListener(enchant.Event.SLIP, function(e) {
2804                 this.scroll(e.dy);
2805             });
2806         },
2807         /**
2808          * ListView content.
2809          * @type {enchant.widget.ListElement[]}
2810          */
2811         content: {
2812             get: function() {
2813                 return this._content.childNodes;
2814             },
2815             set: function(content) {
2816                 var addChild = enchant.widget.EntityGroup.prototype.addChild;
2817                 var removeChild = enchant.widget.EntityGroup.prototype.removeChild;
2818                 if (this._content) {
2819                     removeChild.call(this, this._content);
2820                 }
2821                 var list = new List(content);
2822                 list.width = this.width;
2823                 addChild.call(this, list);
2824                 this._content = list;
2825             }
2826         },
2827         /**
2828          * Acquires event target.
2829          * @param {enchant.Event} event
2830          * @return {enchant.widget.ListElement}
2831          */
2832         getSelectedItem: function(e) {
2833             var y = e.localY - this._content.y;
2834             var list = this._content;
2835             var child;
2836             var h = 0;
2837             for (var i = 0, l = list.childNodes.length; i < l; i++) {
2838                 child = list.childNodes[i];
2839                 h += child.height;
2840                 if (h > y) {
2841                     return child;
2842                 }
2843             }
2844             return null;
2845         },
2846         addChild: function(child) {
2847             this._content.addChild(child);
2848         },
2849         removeChild: function(child) {
2850             this._content.removeChild(child);
2851         },
2852         insertBefore: function(child, reference) {
2853             this._content.insertBefore(child, reference);
2854         }
2855     });
2856     
2857     
2858     var _emptyFunction = function(){};
2859 
2860     /**
2861      * @scope enchant.widget.LazyListItem
2862      */
2863     enchant.widget.LazyListItem = enchant.Class.create(enchant.widget.ListItem, {
2864         /**
2865          * LazyListItem is a class which can be used in an enchant.widget.LazyListView instance
2866          * to enable the lazy loading of the content of a list item.
2867          * The LazyListView instance will call the loadResources function of the LazyListItem instance
2868          * when it feels like the content will be required soon.
2869          * Implementors must implement the loadResources function.
2870          * See the LazyListView example of enchant.js (examples/plugins/widget/LazyListView/).
2871          * 
2872          * @see enchant.widget.LazyListView 
2873          * @see enchant.widget.ListItem
2874          * @constructs
2875          * @extends enchant.widget.ListItem
2876          */
2877         initialize: function() {
2878             enchant.widget.ListItem.apply(this, arguments);
2879         },
2880         _loadResources: function() {
2881             if(this.loadResources) {
2882                 this.loadResources();
2883                 this._loadResources = _emptyFunction;
2884             }
2885         }
2886     });
2887     
2888     var _enchantWidgetListViewPrototypeScroll = enchant.widget.ListView.prototype.scroll;
2889     var _enchantWidgetListViewPrototypeAddChild = enchant.widget.ListView.prototype.addChild;
2890     var _enchantWidgetListViewPrototypeRemoveChild = enchant.widget.ListView.prototype.removeChild;
2891     var _enchantWidgetListViewPrototypeInsertBefore = enchant.widget.ListView.prototype.insertBefore;
2892     
2893     /**
2894      * @scope enchant.widget.LazyListView
2895      */
2896     enchant.widget.LazyListView = enchant.Class.create(enchant.widget.ListView, {
2897         /**
2898          * LazyListView is a class which enables a ListView to load the content of it's
2899          * child ListItems lazily by using enchant.widget.LazyListItem (s).
2900          * It is also possible to use regular ListItems which will behave
2901          * like if they where added to a ListView (therefore, no lazy initialization).
2902          * Furthermore, this LazyListView will dispatch an enchant.Event.CONTENT_REQUESTED event
2903          * when the ListView feels like the available content is not sufficient. 
2904          * The enchant.Event.CONTENT_REQUESTED can be used to add new items to the LazyListView.
2905          * See the LazyListView example of enchant.js (examples/plugins/widget/LazyListView/).
2906          * 
2907          * @see enchant.widget.ListView 
2908          * @see enchant.widget.LazyListItem
2909          * @constructs
2910          * @extends enchant.widget.ListView
2911          */
2912         initialize: function() {
2913             enchant.widget.ListView.apply(this, arguments);
2914         },
2915         _updateContent: function() {
2916             var visibleHeight = this.height - this._content.y + 100;
2917             var content = this.content;
2918             var contentLength = content.length;
2919             for(var i = 0; i < contentLength; i++) {
2920                 if(content[i].y <= visibleHeight && content[i]._loadResources) {
2921                     content[i]._loadResources.call(content[i]);
2922                 } else if(content[i].y > visibleHeight) {
2923                     return;
2924                 }
2925             }
2926             
2927             var event = new enchant.Event(enchant.Event.CONTENT_REQUESTED);
2928             this.dispatchEvent(event); // as we did not return in the loop there are not enough list items available, request new content
2929         },
2930         scroll: function() {
2931             _enchantWidgetListViewPrototypeScroll.apply(this, arguments);
2932             this._updateContent();
2933         },
2934         addChild: function(child) {
2935             _enchantWidgetListViewPrototypeAddChild.apply(this, arguments);
2936             this._updateContent();
2937         },
2938         removeChild: function(child) {
2939             _enchantWidgetListViewPrototypeRemoveChild.apply(this, arguments);
2940             this._updateContent();
2941         },
2942         insertBefore: function(child, reference) {
2943             _enchantWidgetListViewPrototypeInsertBefore.apply(this, arguments);
2944             this._updateContent();
2945         }
2946     });
2947 
2948     var List = enchant.Class.create(enchant.widget.EntityGroup, {
2949         initialize: function(array) {
2950             var core = enchant.Core.instance;
2951             enchant.widget.EntityGroup.call(this);
2952             this.width = core.width;
2953             this.height = core.height;
2954             this._itemHeight = 0;
2955             var element;
2956             for (var i = 0, l = array.length; i < l; i++) {
2957                 element = array[i];
2958                 this.addChild(element);
2959             }
2960 
2961             this._dragging = null;
2962             this._pthreshold = 0;
2963             this._nthreshold = 0;
2964             this._index = 0;
2965         },
2966         addChild: function(child) {
2967             var i = this.childNodes.length;
2968             enchant.widget.EntityGroup.prototype.addChild.call(this, child);
2969             this.refresh(i - 1);
2970         },
2971         insertBefore: function(child, reference) {
2972             enchant.widget.EntityGroup.prototype.insertBefore.call(this, child, reference);
2973             var i = this.childNodes.indexOf(child);
2974             this.refresh(i - 1);
2975         },
2976         removeChild: function(child) {
2977             var i = this.childNodes.indexOf(child);
2978             if (i != -1) {
2979                 enchant.widget.EntityGroup.prototype.removeChild.call(this, child);
2980                 this.refresh(i - 1);
2981             }
2982         },
2983         refresh: function(i) {
2984             var i, l, h, start, child;
2985             if (i > 0) {
2986                 start = this.childNodes[i - 1];
2987                 h = start.y + start.height;
2988             } else {
2989                 i = 0;
2990                 h = 0;
2991             }
2992             for (l = this.childNodes.length; i < l; i++) {
2993                 child = this.childNodes[i];
2994                 child.y = h;
2995                 h += child.height;
2996             }
2997             this.height = this._itemHeight = h;
2998         },
2999         _getElementByLocalPosition: function(localX, localY) {
3000             var child;
3001             var h = 0;
3002             for (var i = 0, l = this.childNodes.length; i < l; i++) {
3003                 child = this.childNodes[i];
3004                 h += child.height;
3005                 if (h > localY) {
3006                     break;
3007                 }
3008             }
3009             return child;
3010         }
3011     });
3012 
3013     /**
3014      * @scope enchant.widget.NavigationBar
3015      */
3016     enchant.widget.NavigationBar = enchant.Class.create(enchant.widget.EntityGroup, {
3017         /**
3018          * Navigation bar.
3019          * Items are set for sent, right, and left.
3020          * @param {*} center Object you wish to display in center.
3021          * @param {*} left Item you wish to display to left.
3022          * @param {*} right Item you wish to display to right.
3023          * @constructs
3024          * @extends enchant.widget.EntityGroup
3025          */
3026         initialize: function(center, left, right) {
3027             var core = enchant.Core.instance;
3028             enchant.widget.EntityGroup.call(this, core.width, enchant.widget._env.itemHeight);
3029             this._center;
3030             this._rawCenter;
3031             this._left;
3032             this._rawLeft;
3033             this._right;
3034             this._rawRight;
3035             this.center = center;
3036             if (left) {
3037                 this.left = left;
3038             }
3039             if (right) {
3040                 this.right = right;
3041             }
3042             this.refresh();
3043 
3044             var np = new enchant.widget.Ninepatch(this.width, this.height);
3045             np.src = core.assets['navigationBar.png'];
3046             this.background = np;
3047         },
3048         /**
3049          * Renew change.
3050          */
3051         refresh: function() {
3052             var center = this._center;
3053             var left = this._left;
3054             var right = this._right;
3055             var margin = enchant.widget._env.listItemMargin;
3056             if (center) {
3057                 center.alignHorizontalCenterIn(this).alignVerticalCenterIn(this);
3058             }
3059             if (left) {
3060                 left.alignLeftIn(this, margin).alignVerticalCenterIn(this);
3061             }
3062             if (right) {
3063                 right.alignRightIn(this, margin).alignVerticalCenterIn(this);
3064             }
3065         },
3066         /**
3067          * Central content.
3068          * @type {*}
3069          */
3070         center: {
3071             get: function() {
3072                 return this._rawCenter;
3073             },
3074             set: function(center) {
3075                 this._rawCenter = center;
3076                 center = enchant.widget.parseContent(center, enchant.widget._env.navigationBarFont);
3077                 if (this._center) {
3078                     this.removeChild(this._center);
3079                 }
3080                 this.addChild(center);
3081                 this._center = center;
3082                 this.refresh();
3083             }
3084         },
3085         /**
3086          * Left content.
3087          * It appear in left aligned position.
3088          * @type {*}
3089          */
3090         left: {
3091             get: function() {
3092                 return this._rawLeft;
3093             },
3094             set: function(left) {
3095                 this._rawLeft = left;
3096                 left = enchant.widget.parseContent(left);
3097                 if (this._left) {
3098                     this.removeChild(this._left);
3099                 }
3100                 this.addChild(left);
3101                 this._left = left;
3102                 this.refresh();
3103             }
3104         },
3105         /**
3106          * Right content.
3107          * It appear in right aligned position.
3108          * @type {*}
3109          */
3110         right: {
3111             get: function() {
3112                 return this._rawRight;
3113             },
3114             set: function(right) {
3115                 this._rawRight = right;
3116                 right = enchant.widget.parseContent(right);
3117                 if (this._right) {
3118                     this.removeChild(this._right);
3119                 }
3120                 this.addChild(right);
3121                 this._right = right;
3122                 this.refresh();
3123             }
3124         }
3125     });
3126 
3127     enchant.widget.Icon = enchant.Class.create(enchant.widget.EntityGroup, {
3128         initialize: function(icon, text) {
3129             enchant.widget.EntityGroup.call(this, 44, 44);
3130             icon = enchant.widget.parseContent(icon);
3131             text = enchant.widget.parseContent(text, enchant.widget._env.font);
3132             var sx = 32 / icon.width;
3133             var sy = 32 / icon.height;
3134             icon.scaleX = icon.scaleY = Math.min(sx, sy);
3135             icon.alignHorizontalCenterIn(this).alignTopIn(this);
3136             text.alignHorizontalCenterIn(this).alignBottomOf(icon, -7);
3137             this.addChild(icon);
3138             this.addChild(text);
3139         }
3140     });
3141 
3142     /**
3143      * @scope enchant.widget.IconMenu
3144      */
3145     enchant.widget.IconMenu = enchant.Class.create(enchant.widget.EntityGroup, {
3146         /**
3147          * Menu with items lined up horizontally.
3148          * @param {enchant.Entity[]} Array of button.
3149          * @constructs
3150          * @extends enchant.widget.EntityGroup
3151          */
3152         initialize: function(buttons) {
3153             var core = enchant.Core.instance;
3154             if (!(buttons instanceof Array)) {
3155                 buttons = Array.prototype.slice.call(arguments);
3156             }
3157             enchant.widget.EntityGroup.call(this, core.width, enchant.widget._env.itemHeight);
3158             this._bgs = [];
3159             this._icons = [];
3160             this.content = buttons;
3161             this.refresh();
3162             this._bgs.forEach(function(bg) {
3163                 var width = bg.width;
3164                 var height = bg.height;
3165                 var np = new enchant.widget.Ninepatch(width, height);
3166                 np.src = core.assets['iconMenuBg.png'];
3167                 bg.image = np;
3168             });
3169         },
3170         /**
3171          * Renew change.
3172          */
3173         getSelectedItem: function(e) {
3174             var x = e.localX;
3175             var list = this._bgs;
3176             var child;
3177             var w = 0;
3178             for (var i = 0, l = list.childNodes.length; i < l; i++) {
3179                 child = list.childNodes[i];
3180                 w += child.width;
3181                 if (w > x) {
3182                     return this._icons[i];
3183                 }
3184             }
3185             return null;
3186         },
3187         refresh: function() {
3188             var icon, bg, bgwidth;
3189             var margin = enchant.widget._env.listItemMargin;
3190             var arr = distribute(this.width, this._icons.length);
3191             var _width = 0;
3192             var menu = this;
3193 
3194             for (var i = 0, l = this._icons.length; i < l; i++) {
3195                 bgwidth = arr[i];
3196                 icon = this._icons[i];
3197                 bg = this._bgs[i];
3198                 bg.width = bgwidth;
3199                 bg.height = this.height;
3200                 bg.image.resize(bg.width, bg.height);
3201                 bg.x = _width;
3202 
3203                 icon.addEventListener(enchant.Event.TOUCH_END, (function(bg) {
3204                     return function(e) {
3205                         bg.dispatchEvent(e);
3206                     };
3207                 })(bg));
3208                 bg.addEventListener(enchant.Event.TOUCH_END, (function(i, elem) {
3209                     return function(e) {
3210                         var evt = new enchant.Event(enchant.Event.TAP);
3211                         evt.x = e.x;
3212                         evt.y = e.y;
3213                         evt.index = i;
3214                         evt.element = elem;
3215                         menu.dispatchEvent(evt);
3216                     };
3217                 })(i, icon));
3218 
3219                 icon.alignHorizontalCenterIn(bg).alignVerticalCenterIn(bg);
3220                 icon.x += _width;
3221 
3222                 _width += bg.width;
3223             }
3224         },
3225         addChild: function(child) {
3226             var core = enchant.Core.instance;
3227             var addChild = enchant.widget.EntityGroup.prototype.addChild;
3228             var size = enchant.widget._env.itemHeight;
3229             var sp = new enchant.Sprite(size, size);
3230             addChild.call(this, sp);
3231             this._bgs.push(sp);
3232             addChild.call(this, child);
3233             this._icons.push(child);
3234             var np = new enchant.widget.Ninepatch(sp.width, sp.height);
3235             np.src = core.assets['iconMenuBg.png'];
3236             sp.image = np;
3237             this.refresh();
3238         },
3239         insertBefore: function(child, target) {
3240             var core = enchant.Core.instance;
3241             var insertBefore = enchant.widget.EntityGroup.prototype.insertBefore;
3242             var i = this._icons.indexOf(target);
3243             var size = enchant.widget._env.itemHeight;
3244             var sp, np;
3245             if (i != -1) {
3246                 target = this._bgs[i];
3247                 sp = new enchant.Sprite(size, size);
3248                 insertBefore.call(this, sp, target);
3249                 this._bgs.splice(i, 0, sp);
3250                 insertBefore.call(this, child, target);
3251                 this._icons.splice(i, 0, child);
3252                 np = new enchant.widget.Ninepatch(sp.width, sp.height);
3253                 np.src = core.assets['iconMenuBg.png'];
3254                 sp.image = np;
3255                 this.refresh();
3256             }
3257         },
3258         removeChild: function(child) {
3259             var removeChild = enchant.widget.EntityGroup.prototype.removeChild;
3260             var i = this._icons.indexOf(child);
3261             if (i != -1) {
3262                 var bg = this._bgs[this._bgs.length - 1];
3263                 removeChild.call(this, bg);
3264                 this._bgs.pop();
3265                 removeChild.call(this, child);
3266                 this._icons.splice(i, 1);
3267                 this.refresh();
3268             }
3269         },
3270         /**
3271          * Set icon.
3272          * @param {enchant.Entity[]} content Array for object you wish to display.
3273          */
3274         content: {
3275             get: function() {
3276                 return this._icons;
3277             },
3278             set: function(content) {
3279                 var removeChild = enchant.widget.EntityGroup.prototype.removeChild;
3280                 var menu = this;
3281                 if (this.childNodes) {
3282                     this.childNodes.slice().forEach(function(child) {
3283                         removeChild.call(menu, child);
3284                     });
3285                 }
3286                 content.forEach(function(child) {
3287                     menu.addChild(child);
3288                 });
3289             }
3290         }
3291     });
3292 
3293 })();
3294