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  * enchant.jsでモバイル向けウェブページのようなUIを作成するためのライブラリ.
 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          * 文字列やenchant.Surfaceなど単体で表示できないオブジェクトを表示できる形にして返す.
 50          * @param {*} content 表示させたいデータ.
 51          * @return {*} enchantのEntity系オブジェクト.
 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      * Sceneが開始したとき発生するイベント.
 87      * {@link enchant.Core#transitionPush}のアニメーションが終了した際に発生する.
 88      * @type {String}
 89      */
 90     enchant.Event.TRANSITIONENTER = 'transitionenter';
 91 
 92     /**
 93      * Sceneが終了したとき発生するイベント.
 94      * {@link enchant.Core#transitionPop}のアニメーションが終了した際に発生する.
 95      * @type {String}
 96      */
 97     enchant.Event.TRANSITIONEXIT = 'transitionexit';
 98 
 99     /**
100      * enchant.widget.Confirmで肯定側のボタンが押されたときに発生されるイベント.
101      * @type {String}
102      */
103     enchant.Event.ACCEPT = 'accept';
104 
105     /**
106      * enchant.widget.Confirmで否定側のボタンが押されたときに発生されるイベント.
107      * @type {String}
108      */
109     enchant.Event.CANCEL = 'cancel';
110 
111     /**
112      * form系のオブジェクトで内容が変更されたときに発生されるイベント.
113      * @type {String}
114      */
115     enchant.Event.CHANGE = 'change';
116 
117     /**
118      * タップが検出されたときに発生されるイベント.
119      * 移動なしでタッチが終了し, ダブルタップの判定時間が終了した場合検出される.
120      * @type {String}
121      */
122     enchant.Event.TAP = 'tap';
123 
124     /**
125      * ダブルタップが検出されたときに発生されるイベント.
126      * 一定時間, 一定距離以内に2回タップが検出された場合検出される.
127      * @type {String}
128      */
129     enchant.Event.DOUBLETAP = 'doubletap';
130 
131     /**
132      * ホールドが検出されたときに発生されるイベント.
133      * 移動なしで一定時間タッチが続いた場合検出される.
134      * @type {String}
135      */
136     enchant.Event.HOLD = 'hold';
137 
138     /**
139      * ドラッグが検出されたときに発生されるイベント.
140      * ホールド中にタッチ位置が移動した際に検出される.
141      * @type {String}
142      */
143     enchant.Event.DRAG = 'drag';
144 
145     /**
146      * リリースが検出されたときに発生されるイベント.
147      * ホールド中にタッチが終了した際に検出される.
148      * @type {String}
149      */
150     enchant.Event.RELEASE = 'release';
151 
152     /**
153      * スリップが検出されたときに発生されるイベント.
154      * ホールドされずタッチ位置が移動した場合検出される.
155      * @type {String}
156      */
157     enchant.Event.SLIP = 'slip';
158 
159     /**
160      * フリックが検出されたときに発生されるイベント.
161      * タッチが終了した際に, 一定速度以上で位置が移動していた場合検出される.
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          * 指定オブジェクトの左側に寄せる.
397          * @param {*} another 基準となるオブジェクト.
398          * @param {Number} margin ずらすピクセル数.
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          * 指定オブジェクトの右側に寄せる.
410          * @param {*} another 基準となるオブジェクト.
411          * @param {Number} margin ずらすピクセル数.
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          * 指定オブジェクトの上側に寄せる.
423          * @param {*} another 基準となるオブジェクト.
424          * @param {Number} margin ずらすピクセル数.
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          * 指定オブジェクトの下側に寄せる.
436          * @param {*} another 基準となるオブジェクト.
437          * @param {Number} margin ずらすピクセル数.
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          * 指定オブジェクト内で左寄せを行う.
449          * @param {*} another 基準となるオブジェクト.
450          * @param {Number} margin ずらすピクセル数.
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          * 指定オブジェクト内で右寄せを行う.
460          * @param {*} another 基準となるオブジェクト.
461          * @param {Number} margin ずらすピクセル数.
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          * 指定オブジェクト内で上寄せを行う.
471          * @param {*} another 基準となるオブジェクト.
472          * @param {Number} margin ずらすピクセル数.
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          * 指定オブジェクト内で下寄せを行う.
482          * @param {*} another 基準となるオブジェクト.
483          * @param {Number} margin ずらすピクセル数.
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          * 指定オブジェクト内でx方向の中央寄せを行う.
493          * @param {*} another 基準となるオブジェクト.
494          * @param {Number} margin ずらすピクセル数.
495          * @requires widget.enchant.js
496          */
497         alignHorizontalCenterIn: function(another) {
498             this.x = calcCenteredPosition(this.width, another.width);
499             return this;
500         },
501         /**
502          * 指定オブジェクト内でy方向の中央寄せを行う.
503          * @param {*} another 基準となるオブジェクト.
504          * @param {Number} margin ずらすピクセル数.
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      * トランジションアニメーションのついたpushSceneを行う.
525      * @param {enchant.Scene} scene 移行する新しいシーン.
526      * @return {enchant.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      * トランジションアニメーションのついたpopSceneを行う.
552      * @return {enchant.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          * タッチ入力の動きから幾つかのジェスチャーを検出してイベントを発行する.
585          * タップ, ダブルタップ, ホールド, ドラッグ, フリックなどを検出することができる.
586          * @param {enchant.Entity} target 入力を検出させたいオブジェクト.
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          * 9patchに対応したサーフェース.
776          * コンテンツ領域の設定には対応していない.
777          * @param {Number} width Surfaceの横幅.
778          * @param {Number} height Surfaceの高さ.
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のソース.
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          * 指定したサイズで作りなおす.
1034          * @param {Number} width 新しい横幅.
1035          * @param {Number} 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.
1054          * @param {Number} width Entityの横幅.
1055          * @param {Number} height Entityの高さ.
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.
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          * EntityGroupにNodeを追加する.
1125          * @param {enchant.Node} child 追加するNode.
1126          */
1127         addChild: enchant.Group.prototype.addChild,
1128         /**
1129          * EntityGroupにNodeを挿入する.
1130          * @param {enchant.Node} child 挿入するNode.
1131          * @param {enchant.Node} reference 挿入位置の前にあるNode.
1132          */
1133         insertBefore: enchant.Group.prototype.insertBefore,
1134         /**
1135          * EntityGroupからNodeを削除する.
1136          * @param {enchant.Node} child 削除するNode.
1137          */
1138         removeChild: enchant.Group.prototype.removeChild,
1139         /**
1140          * 最初の子Node.
1141          */
1142         firstChild: Object.getOwnPropertyDescriptor(enchant.Group.prototype, 'firstChild'),
1143         /**
1144          * 最後の子Node.
1145          * @type {enchant.Node}
1146          */
1147         lastChild: Object.getOwnPropertyDescriptor(enchant.Group.prototype, 'lastChild'),
1148         _dirty: Object.getOwnPropertyDescriptor(enchant.Group.prototype, '_dirty'),
1149         cvsRender: function(ctx) {
1150             if (this.background &&
1151                 this.background._element.width > 0 &&
1152                 this.background._element.height > 0) {
1153                 ctx.drawImage(this.background._element, 0, 0, this.width, this.height);
1154             }
1155         }
1156     });
1157 
1158     /**
1159      * @scope enchant.widget.Modal
1160      */
1161     enchant.widget.Modal = enchant.Class.create(enchant.Scene, {
1162         /**
1163          * モーダルシーン.
1164          * @constructs
1165          * @extends enchant.Scene
1166          */
1167         initialize: function() {
1168             enchant.Scene.call(this);
1169             var core = enchant.Core.instance;
1170             var shade = new enchant.Sprite(core.width, core.height);
1171             shade.backgroundColor = 'rgba(0, 0, 0, 0.1)';
1172             this.addChild(shade);
1173             this.addEventListener(enchant.Event.ENTER, function() {
1174                 shade.tl.fadeTo(0.7, 5, enchant.Easing.QUAD_EASEOUT);
1175             });
1176         }
1177     });
1178 
1179     /**
1180      * @scope enchant.widget.Button.prototype
1181      */
1182     enchant.widget.Button = enchant.Class.create(enchant.widget.EntityGroup, {
1183         /**
1184          * ボタン.
1185          * 通常の背景と押下中の背景を設定できる.
1186          * @param {*} content ボタンの内容.
1187          * @constructs
1188          * @extends enchant.widget.EntityGroup
1189          */
1190         initialize: function(content) {
1191             var core = enchant.Core.instance;
1192             content = content || '';
1193             var minwidth = enchant.widget._env.buttonWidth;
1194             var minheight = enchant.widget._env.buttonHeight;
1195             enchant.widget.EntityGroup.call(this, minwidth, minheight);
1196             this._image;
1197             this._pushedimage;
1198             this._content;
1199             this._rawContent;
1200             var bg1 = new enchant.widget.Ninepatch(minwidth, minheight);
1201             bg1.src = core.assets['button.png'];
1202             this.image = bg1;
1203 
1204             var bg2 = new enchant.widget.Ninepatch(minwidth, minheight);
1205             bg2.src = core.assets['buttonPushed.png'];
1206             this.pushedimage = bg2;
1207 
1208             this.content = content;
1209             this.width = Math.max(this._content.width, minwidth);
1210             this.height = Math.max(this._content.height, minheight);
1211             this.addEventListener(enchant.Event.TOUCH_START, function() {
1212                 if (!this._pushedimage) {
1213                     return;
1214                 }
1215                 this.background = this._pushedimage;
1216             });
1217             this.addEventListener(enchant.Event.TOUCH_END, function() {
1218                 if (!this._pushedimage) {
1219                     return;
1220                 }
1221                 this.background = this._image;
1222             });
1223         },
1224         refresh: function() {
1225             if (this._content) {
1226                 this._content.alignHorizontalCenterIn(this).alignVerticalCenterIn(this);
1227             }
1228         },
1229         /**
1230          * ボタンの幅
1231          * @type number
1232          */
1233         width: {
1234             get: function() {
1235                 return this._width;
1236             },
1237             set: function(width) {
1238                 this._style.width = (this._width = width) + 'px';
1239                 if (this._image instanceof enchant.widget.Ninepatch) {
1240                     this._image.width = width;
1241                 }
1242                 if (this._pushedimage instanceof enchant.widget.Ninepatch) {
1243                     this._pushedimage.width = width;
1244                 }
1245                 this.refresh();
1246             }
1247         },
1248         /**
1249          * ボタンの高さ
1250          * @type number
1251          */
1252         height: {
1253             get: function() {
1254                 return this._height;
1255             },
1256             set: function(height) {
1257                 this._style.height = (this._height = height) + 'px';
1258                 if (this._image instanceof enchant.widget.Ninepatch) {
1259                     this._image.height = height;
1260                 }
1261                 if (this._pushedimage instanceof enchant.widget.Ninepatch) {
1262                     this._pushedimage.height = height;
1263                 }
1264                 this.refresh();
1265             }
1266         },
1267         /**
1268          * ボタンの背景.
1269          */
1270         image: {
1271             get: function() {
1272                 return this._image;
1273             },
1274             set: function(image) {
1275                 if (image == this._image) {
1276                     return;
1277                 }
1278                 this.background = image;
1279                 this._image = image;
1280             }
1281         },
1282         /**
1283          * ボタンが押されている時の背景.
1284          * @type {enchant.Surface}
1285          */
1286         pushedimage: {
1287             get: function() {
1288                 return this._pushedimage;
1289             },
1290             set: function(image) {
1291                 if (image == this._pushedimage) {
1292                     return;
1293                 }
1294                 this._pushedimage = image;
1295             }
1296         },
1297         /**
1298          * ボタンの内容
1299          * @type {String}
1300          */
1301         content: {
1302             get: function() {
1303                 return this._rawContent;
1304             },
1305             set: function(content) {
1306                 this._rawContent = content;
1307                 var font = enchant.widget._env.buttonFont;
1308                 content = enchant.widget.parseContent(content, font);
1309                 if (this._content) {
1310                     this.removeChild(this._content);
1311                 }
1312                 this.addChild(content);
1313                 this._content = content;
1314                 this.refresh();
1315             }
1316         }
1317     });
1318 
1319     /**
1320      * @scope enchant.widget.Alert
1321      */
1322     enchant.widget.Alert = enchant.Class.create(enchant.widget.EntityGroup, {
1323         /**
1324          * アラートダイアログ.
1325          * 通常{@link enchant.widget.AlertScene}から使用する.
1326          * @param {*} content 表示するコンテンツ.
1327          * @param {String} ac 了承ボタンのラベル.
1328          * @see enchant.widget.AlertScene
1329          * @constructs
1330          * @extends enchant.widget.EntityGroup
1331          */
1332         initialize: function(content, ac) {
1333             var core = enchant.Core.instance;
1334             var dialogwidth = enchant.widget._env.dialogWidth;
1335             var dialogheight = enchant.widget._env.dialogHeight;
1336             enchant.widget.EntityGroup.call(this, dialogwidth, dialogheight);
1337             var margin = enchant.widget._env.dialogMargin;
1338 
1339             content = enchant.widget.parseContent(content);
1340             content.alignHorizontalCenterIn(this).alignTopIn(this, margin);
1341 
1342             var accept = new enchant.widget.Button(ac);
1343             accept.alignHorizontalCenterIn(this).alignBottomIn(this, margin);
1344 
1345             var that = this;
1346             accept.addEventListener(enchant.Event.TOUCH_END, function() {
1347                 that.dispatchEvent(new enchant.Event(enchant.Event.ACCEPT));
1348             });
1349 
1350             var np = new enchant.widget.Ninepatch(this.width, this.height);
1351             np.src = core.assets['dialog.png'];
1352             this.background = np;
1353 
1354             this._content = content;
1355             this._accept = accept;
1356 
1357             this.addChild(content);
1358             this.addChild(accept);
1359         },
1360         /**
1361          * 了承ボタンが押されたときに実行される関数.
1362          * @type {Function}
1363          */
1364         onaccept: function() {
1365         }
1366     });
1367 
1368     /**
1369      * @scope enchant.widget.Confirm
1370      */
1371     enchant.widget.Confirm = enchant.Class.create(enchant.widget.EntityGroup, {
1372         /**
1373          * コンファームダイアログ.
1374          * 通常{@link enchant.widget.ConfirmScene}から使用する.
1375          * @param {*} content 表示するコンテンツ.
1376          * @param {String} ac 了承ボタンのラベル.
1377          * @param {String} ig キャンセルボタンのラベル.
1378          * @see enchant.widget.ConfirmScene
1379          * @constructs
1380          * @extends enchant.widget.EntityGroup
1381          */
1382         initialize: function(content, ac, ig) {
1383             var core = enchant.Core.instance;
1384             var dialogwidth = enchant.widget._env.dialogWidth;
1385             var dialogheight = enchant.widget._env.dialogHeight;
1386             enchant.widget.EntityGroup.call(this, dialogwidth, dialogheight);
1387             var margin = enchant.widget._env.dialogMargin;
1388 
1389             var content = enchant.widget.parseContent(content);
1390             content.alignHorizontalCenterIn(this).alignTopIn(this, margin);
1391 
1392             var cancel = new enchant.widget.Button(ig);
1393             cancel.alignLeftIn(this, margin).alignBottomIn(this, margin);
1394 
1395             var accept = new enchant.widget.Button(ac);
1396             accept.alignRightIn(this, margin).alignBottomIn(this, margin);
1397 
1398             var that = this;
1399             cancel.addEventListener(enchant.Event.TOUCH_END, function() {
1400                 that.dispatchEvent(new enchant.Event(enchant.Event.CANCEL));
1401             });
1402             accept.addEventListener(enchant.Event.TOUCH_END, function() {
1403                 that.dispatchEvent(new enchant.Event(enchant.Event.ACCEPT));
1404             });
1405 
1406             var np = new enchant.widget.Ninepatch(this.width, this.height);
1407             np.src = core.assets['dialog.png'];
1408             this.background = np;
1409 
1410             this._content = content;
1411             this._cancel = cancel;
1412             this._accept = accept;
1413 
1414             this.addChild(content);
1415             this.addChild(cancel);
1416             this.addChild(accept);
1417         },
1418         /**
1419          * キャンセルボタンが押されたときに実行される関数.
1420          * @type {Function}
1421          */
1422         oncancel: function() {
1423         },
1424         /**
1425          * 了承ボタンが押されたときに実行される関数.
1426          */
1427         onaccept: function() {
1428         }
1429     });
1430 
1431     /**
1432      * @scope enchant.widget.Prompt
1433      */
1434     enchant.widget.Prompt = enchant.Class.create(enchant.widget.Confirm, {
1435         /**
1436          * プロンプトダイアログ.
1437          * 通常{@link enchant.widget.PromptScene}から使用する.
1438          * @param {*} content 表示するコンテンツ.
1439          * @param {String} ac 了承ボタンのラベル.
1440          * @param {String} ig キャンセルボタンのラベル.
1441          * @see enchant.widget.PromptScene
1442          * @constructs
1443          * @extends enchant.widget.Confirm
1444          */
1445         initialize: function(content, ac, ig, placeholder) {
1446             enchant.widget.Confirm.call(this, content, ac, ig);
1447             var margin = enchant.widget._env.dialogMargin;
1448             var input = this._input = new enchant.widget.InputTextBox();
1449             input.width = this.width / 4 * 3;
1450             input.placeholder = placeholder;
1451             input.alignHorizontalCenterIn(this).alignBottomOf(this._content, margin);
1452             this.addChild(input);
1453         },
1454         /**
1455          * content of prompt.
1456          */
1457         value: {
1458             get: function() {
1459                 return this._input.value;
1460             },
1461             set: function(value) {
1462                 this._input.value = value;
1463             }
1464         }
1465     });
1466 
1467     /**
1468      * @scope enchant.widget.Input
1469      */
1470     enchant.widget.Input = enchant.Class.create(enchant.Entity, {
1471         /**
1472          * <input>を内包したEntity.
1473          * @param {String} type <input>のtype.
1474          * @constructs
1475          * @extends enchant.Entity
1476          */
1477         initialize: function(type) {
1478             enchant.Entity.call(this);
1479             if (!type) {
1480                 type = 'input';
1481             }
1482             var that = this;
1483             this._input = document.createElement(type);
1484 
1485             this._input.addEventListener('change', function(e) {
1486                 that.dispatchEvent(new enchant.Event(enchant.Event.CHANGE));
1487             });
1488 
1489             this._element = document.createElement('div');
1490             this._element.appendChild(this._input);
1491         },
1492         /**
1493          * 入力を許可するか決定する.
1494          * @type {Boolean}
1495          */
1496         disabled: {
1497             get: function() {
1498                 return this._input.disbaled;
1499             },
1500             set: function(value) {
1501                 this._input.disabled = !!value;
1502             }
1503         }
1504     });
1505 
1506     /**
1507      * @scope enchant.widget.InputTextBox
1508      */
1509     enchant.widget.InputTextBox = enchant.Class.create(enchant.widget.Input, {
1510         /**
1511          * テキストボックス.
1512          * @constructs
1513          * @extends enchant.widget.Input
1514          */
1515         initialize: function() {
1516             enchant.widget.Input.call(this);
1517             this._input.type = 'text';
1518 
1519             var metrics = getElementMetrics(this._element.innerHTML);
1520             this.width = metrics.width;
1521             this.height = metrics.height;
1522 
1523             var that = this;
1524             this._focused = false;
1525 
1526             this._input.addEventListener('focus', function() {
1527                 that._focused = true;
1528             });
1529 
1530             this._input.addEventListener('blur', function() {
1531                 that._focused = false;
1532             });
1533         },
1534         /**
1535          * @type {Number}
1536          */
1537         selectionStart: {
1538             get: function() {
1539                 return this._input.selectionStart;
1540             },
1541             set: function(n) {
1542                 this._input.selectionStart = n;
1543             }
1544         },
1545         /**
1546          * @type {Number}
1547          */
1548         selectionEnd: {
1549             get: function() {
1550                 return this._input.selectionEnd;
1551             },
1552             set: function(n) {
1553                 this._input.selectionEnd = n;
1554             }
1555         },
1556         /**
1557          * @type {Boolean}
1558          */
1559         focused: {
1560             get: function() {
1561                 return this._focused;
1562             },
1563             set: function(bool) {
1564                 this._focused = bool;
1565                 if (bool) {
1566                     this._input.focus();
1567                 } else {
1568                     this._input.blur();
1569                 }
1570             }
1571         },
1572         /**
1573          * プレースホルダ.
1574          * @type {String}
1575          */
1576         placeholder: {
1577             get: function() {
1578                 return this._input.placeholder;
1579             },
1580             set: function(value) {
1581                 this._input.placeholder = value;
1582             }
1583         },
1584         /**
1585          * テキストボックスに入力された値.
1586          * @type {String}
1587          */
1588         value: {
1589             get: function() {
1590                 return this._input.value;
1591             },
1592             set: function(value) {
1593                 this._input.value = value;
1594             }
1595         },
1596         /**
1597          * テキストボックスの横幅.
1598          * @type {Number}
1599          */
1600         width: {
1601             get: function() {
1602                 return this._width;
1603             },
1604             set: function(width) {
1605                 this._width = width;
1606                 this._style.width = width + 'px';
1607                 this._input.style.width = width + 'px';
1608             }
1609         },
1610         /**
1611          * テキストボックスの縦幅.
1612          * @type {Number}
1613          */
1614         height: {
1615             get: function() {
1616                 return this._height;
1617             },
1618             set: function(height) {
1619                 this._height = height;
1620                 this._style.height = height + 'px';
1621                 this._input.style.height = height + 'px';
1622             }
1623         }
1624     });
1625 
1626     /**
1627      * @scope enchant.widget.InputSelectBox
1628      */
1629     enchant.widget.InputSelectBox = enchant.Class.create(enchant.widget.Input, {
1630         /**
1631          * セレクトボックス.
1632          * @param {*} option オプションに設定する値.
1633          * @example
1634          *   var option = {
1635          *       male: '男',
1636          *       female: '女'
1637          *   };
1638          *   var selectbox = new InputSelectBox(option);
1639          *
1640          * @constructs
1641          * @extends enchant.widget.Input
1642          */
1643         initialize: function(table) {
1644             enchant.widget.Input.call(this, 'select');
1645             var content;
1646             for (var prop in table) {
1647                 content = table[prop];
1648                 opt = document.createElement('option');
1649                 opt.value = prop;
1650                 opt.textContent = content;
1651                 this._input.appendChild(opt);
1652             }
1653 
1654             this._input.addEventListener('mousedown', function(e) {
1655                 e.stopPropagation();
1656             });
1657 
1658             var metrics = getElementMetrics(this._element.innerHTML);
1659             this.width = metrics.width;
1660             this.height = metrics.height;
1661         },
1662         /**
1663          * 選択されている項目.
1664          * @type {String}
1665          */
1666         selected: {
1667             get: function() {
1668                 return this._input.options[this._input.selectedIndex].value;
1669             },
1670             set: function(value) {
1671                 var opt;
1672                 for (var i = 0, l = this._input.options.length; i < l; i++) {
1673                     opt = this._input.options[i];
1674                     if (opt.getAttribute('value') == value) {
1675                         opt.selected = true;
1676                     } else {
1677                         opt.selected = false;
1678                     }
1679                 }
1680                 return value;
1681             }
1682         }
1683     });
1684 
1685     /**
1686      * @scope enchant.widget.InputCheckBox
1687      */
1688     enchant.widget.InputCheckBox = enchant.Class.create(enchant.widget.Input, {
1689         /**
1690          * チェックボックス.
1691          * @param {String} value 値.
1692          * @param {String} text ラベルテキスト.
1693          * @param {Boolean} checked チェックされているかどうか.
1694          * @constructs
1695          * @extends enchant.widget.Input
1696          */
1697         initialize: function(value, text, checked) {
1698             enchant.widget.Input.call(this);
1699             this._input.type = 'checkbox';
1700             var label = document.createDocumentFragment();
1701             label.textContent = text;
1702             this._element.appendChild(label);
1703             this.checked = checked;
1704             var metrics = getElementMetrics(this._element.innerHTML);
1705             this.width = metrics.width;
1706             this.height = metrics.height;
1707         },
1708         /**
1709          * チェックされているかどうか.
1710          * @type {Boolean}
1711          */
1712         checked: {
1713             get: function() {
1714                 return this._input.checked;
1715             },
1716             set: function(value) {
1717                 this._input.checked = !!value;
1718             }
1719         }
1720     });
1721 
1722     /**
1723      * @scope enchant.widget.InputTextArea
1724      */
1725     enchant.widget.InputTextArea = enchant.Class.create(enchant.Entity, {
1726         /**
1727          * テキストエリア.
1728          * @constructs
1729          * @extends enchant.Entity
1730          */
1731         initialize: function() {
1732             enchant.Entity.call(this);
1733             var textarea = this._textarea = document.createElement('textarea');
1734             textarea.style.resize = 'none';
1735             textarea.style.font = enchant.widget._env.textareaFont;
1736             this._element = document.createElement('div');
1737             this._element.appendChild(textarea);
1738             var that = this;
1739             this._focused = false;
1740             this._next = null;
1741             this._prev = null;
1742 
1743             var that = this;
1744             this.addEventListener(enchant.Event.TOUCH_END, function() {
1745                 this._updateVerticalDist();
1746             });
1747             this._textarea.addEventListener('input', function() {
1748                 that._updateVerticalDist();
1749             });
1750             this._textarea.addEventListener('focus', function() {
1751                 that._focused = true;
1752             });
1753             this._textarea.addEventListener('blur', function() {
1754                 that._focused = false;
1755             });
1756             this._textarea.addEventListener('change', function(e) {
1757                 that.dispatchEvent(new enchant.Event(enchant.Event.CHANGE));
1758             });
1759         },
1760         _updateVerticalDist: function() {
1761             var w = this.value.split('\n');
1762             var n = this.selectionStart;
1763             var s = 0;
1764             for (var i = 0, l = w.length; i < l; i++) {
1765                 n -= w[i].length + 1;
1766                 if (n < 0) {
1767                     break;
1768                 }
1769                 s += w[i].length + 1;
1770             }
1771             var ind = this.selectionStart - s;
1772             if (0 < i) {
1773                 this._prev = -Math.max(w[i - 1].length, ind) - 1;
1774             } else {
1775                 this._prev = -ind;
1776             }
1777             if (i < l - 1) {
1778                 this._next = w[i].length - ind + Math.min(ind, w[i + 1].length) + 1;
1779             } else {
1780                 this._next = w[i].length - ind;
1781             }
1782         },
1783         /**
1784          * @type {Number}
1785          */
1786         selectionStart: {
1787             get: function() {
1788                 return this._textarea.selectionStart;
1789             },
1790             set: function(n) {
1791                 this._textarea.selectionStart = n;
1792             }
1793         },
1794         /**
1795          * @type {Number}
1796          */
1797         selectionEnd: {
1798             get: function() {
1799                 return this._textarea.selectionEnd;
1800             },
1801             set: function(n) {
1802                 this._textarea.selectionEnd = n;
1803             }
1804         },
1805         /**
1806          * @type {Boolean}
1807          */
1808         focused: {
1809             get: function() {
1810                 return this._focused;
1811             },
1812             set: function(bool) {
1813                 this._focused = bool;
1814                 if (bool) {
1815                     this._textarea.focus();
1816                 } else {
1817                     this._textarea.blur();
1818                 }
1819             }
1820         },
1821         /**
1822          [lang]ja]
1823          * プレースホルダ.
1824          [/lang]
1825          * @type {String}
1826          */
1827         placeholder: {
1828             get: function() {
1829                 return this._textarea.placeholder;
1830             },
1831             set: function(value) {
1832                 this._textarea.placeholder = value;
1833             }
1834         },
1835         /**
1836          * テキストエリアに入力された値.
1837          * @type {String}
1838          */
1839         value: {
1840             get: function() {
1841                 return this._textarea.value;
1842             },
1843             set: function(value) {
1844                 this._textarea.value = value;
1845             }
1846         },
1847         /**
1848          * テキストエリアの横幅.
1849          * @type {Number}
1850          */
1851         width: {
1852             get: function() {
1853                 return this._width;
1854             },
1855             set: function(width) {
1856                 this._width = width;
1857                 this._style.width = width + 'px';
1858                 this._textarea.style.width = width + 'px';
1859             }
1860         },
1861         /**
1862          * テキストエリアの縦幅.
1863          * @type {Number}
1864          */
1865         height: {
1866             get: function() {
1867                 return this._height;
1868             },
1869             set: function(height) {
1870                 this._height = height;
1871                 this._style.height = height + 'px';
1872                 this._textarea.style.height = height + 'px';
1873             }
1874         }
1875     });
1876 
1877     /**
1878      * @scope enchant.widget.AlertScene
1879      */
1880     enchant.widget.AlertScene = enchant.Class.create(enchant.widget.Modal, {
1881         /**
1882          * アラートシーン.
1883          * 他の入力を遮り, アラートを表示する.
1884          * @param {*} content 表示するコンテンツ.
1885          * @param {String} acceptName 了承ボタンのラベル.
1886          * @example
1887          *     var alert = new ConfirmScene('それはできません', 'OK');
1888          *     alert.callback = function() {
1889          *     };
1890          *     alert.onaccept = function() {
1891          *     };
1892          * @constructs
1893          * @extends enchant.widget.Modal
1894          */
1895         initialize: function(content, acceptName) {
1896             var core = enchant.Core.instance;
1897             enchant.widget.Modal.call(this);
1898             this._onaccept = function() {
1899             };
1900             this.callback = function() {
1901             };
1902             acceptName = acceptName || enchant.widget._env.acceptName;
1903 
1904             var alert = new enchant.widget.Alert(content, acceptName);
1905             this.addChild(alert);
1906             alert.alignHorizontalCenterIn(this).alignVerticalCenterIn(this);
1907 
1908             var scene = this;
1909 
1910             alert.onaccept = function() {
1911                 core.popScene();
1912                 scene._onaccept.apply(this, arguments);
1913             };
1914             alert.addEventListener(enchant.Event.ACCEPT, function() {
1915                 scene.callback();
1916             });
1917             this.addEventListener(enchant.Event.ENTER, function() {
1918                 Effect.popup.call(alert);
1919             });
1920         },
1921         /**
1922          * @type {Function}
1923          */
1924         onaccept: {
1925             get: function() {
1926                 return this._onaccept;
1927             },
1928             set: function(func) {
1929                 this._onaccept = func;
1930             }
1931         }
1932     });
1933 
1934     /**
1935      * @scope enchant.widget.ConfirmScene
1936      */
1937     enchant.widget.ConfirmScene = enchant.Class.create(enchant.widget.Modal, {
1938         /**
1939          * コンファームシーン.
1940          * 他の入力を遮り, 選択画面を表示する.
1941          * @param {*} content 表示するコンテンツ.
1942          * @param {String} acceptName 了承ボタンのラベル.
1943          * @param {String} cancelName キャンセルボタンのラベル.
1944          * @example
1945          *     var confirm = new ConfirmScene('よろしいですか', 'OK', 'NO');
1946          *     confirm.callback = function(bool) {
1947          *         // acceptならtrue, cancelならfalseが返ってってくる.
1948          *     };
1949          *     // cancel, accept個別に処理を設定することも可能.
1950          *     confirm.oncancel = function() {
1951          *     };
1952          *     confirm.onaccept = function() {
1953          *     };
1954          * @constructs
1955          * @extends enchant.widget.Modal
1956          */
1957         initialize: function(content, acceptName, cancelName) {
1958             var core = enchant.Core.instance;
1959             enchant.widget.Modal.call(this);
1960             this._oncancel = function() {
1961             };
1962             this._onaccept = function() {
1963             };
1964             this.callback = function() {
1965             };
1966             cancelName = cancelName || enchant.widget._env.cancelName;
1967             acceptName = acceptName || enchant.widget._env.acceptName;
1968 
1969             var confirm = new enchant.widget.Confirm(content, acceptName, cancelName);
1970             this.addChild(confirm);
1971             confirm.alignHorizontalCenterIn(this).alignVerticalCenterIn(this);
1972             var scene = this;
1973 
1974             confirm.oncancel = function() {
1975                 core.popScene();
1976                 scene._oncancel.apply(this, arguments);
1977             };
1978             confirm.onaccept = function() {
1979                 core.popScene();
1980                 scene._onaccept.apply(this, arguments);
1981             };
1982             confirm.addEventListener(enchant.Event.CANCEL, function() {
1983                 scene.callback(false);
1984             });
1985             confirm.addEventListener(enchant.Event.ACCEPT, function() {
1986                 scene.callback(true);
1987             });
1988             this.addEventListener(enchant.Event.ENTER, function() {
1989                 Effect.popup.call(confirm);
1990             });
1991         },
1992         /**
1993          * @type {Function}
1994          */
1995         oncancel: {
1996             get: function() {
1997                 return this._oncancel;
1998             },
1999             set: function(func) {
2000                 this._oncancel = func;
2001             }
2002         },
2003         /**
2004          * @type {Function}
2005          */
2006         onaccept: {
2007             get: function() {
2008                 return this._onaccept;
2009             },
2010             set: function(func) {
2011                 this._onaccept = func;
2012             }
2013         }
2014     });
2015 
2016     /**
2017      * @scope enchant.widget.PromptScene
2018      */
2019     enchant.widget.PromptScene = enchant.Class.create(enchant.widget.Modal, {
2020         /**
2021          * コンファームシーン.
2022          * 他の入力を遮り, 入力画面を表示する.
2023          * 複数行に渡る入力をさせたい時は, {@link enchant.widget.InputScene}を使用する.
2024          * @param {*} content 表示するコンテンツ.
2025          * @param {String} acceptName 了承ボタンのラベル.
2026          * @param {String} cancelName キャンセルボタンのラベル.
2027          * @param {String} placeholder プレースホルダ.
2028          * @example
2029          *     var confirm = new PromptScene('名前を入力してください', 'OK', 'cancel');
2030          *     confirm.placeholder = 'なまえ';
2031          *     confirm.callback = function(text) {
2032          *         // acceptなら入力された文字列, cancelならnullが返ってってくる.
2033          *     };
2034          *     // cancel, accept個別に処理を設定することも可能.
2035          *     confirm.oncancel = function() {
2036          *     };
2037          *     confirm.onaccept = function(text) {
2038          *     };
2039          * @see enchant.widget.InputScene
2040          * @constructs
2041          * @extends enchant.widget.Modal
2042          */
2043         initialize: function(content, acceptName, cancelName, placeholder) {
2044             var core = enchant.Core.instance;
2045             var margin = enchant.widget._env.dialogMargin;
2046             enchant.widget.Modal.call(this);
2047             cancelName = cancelName || enchant.widget._env.cancelName;
2048             acceptName = acceptName || enchant.widget._env.acceptName;
2049             this.callback = function() {
2050             };
2051             this._oncancel = function() {
2052             };
2053             this._onaccept = function() {
2054             };
2055             placeholder = placeholder || '';
2056 
2057             var prompt = this._prompt = new enchant.widget.Prompt(content, acceptName, cancelName, placeholder);
2058             prompt.alignHorizontalCenterIn(this).alignVerticalCenterIn(this);
2059             this.addChild(prompt);
2060             var scene = this;
2061 
2062             prompt.oncancel = function() {
2063                 core.popScene();
2064                 scene._oncancel.apply(this, arguments);
2065             };
2066             prompt.onaccept = function() {
2067                 core.popScene();
2068                 scene._onaccept.apply(this, arguments);
2069             };
2070             prompt.addEventListener(enchant.Event.CANCEL, function() {
2071                 scene.callback(null);
2072             });
2073             prompt.addEventListener(enchant.Event.ACCEPT, function() {
2074                 scene.callback(prompt.value);
2075             });
2076             this.addEventListener(enchant.Event.ENTER, function() {
2077                 Effect.popup.call(prompt);
2078             });
2079             this.addEventListener(enchant.Event.UP_BUTTON_DOWN, function() {
2080                 if (prompt._input.focused) {
2081                     prompt._input.selectionStart = 0;
2082                     prompt._input.selectionEnd = 0;
2083                 }
2084             });
2085             this.addEventListener(enchant.Event.DOWN_BUTTON_DOWN, function() {
2086                 if (prompt._input.focused) {
2087                     prompt._input.selectionStart = prompt._input.value.length;
2088                     prompt._input.selectionEnd = prompt._input.value.length;
2089                 }
2090             });
2091             this.addEventListener(enchant.Event.LEFT_BUTTON_DOWN, function() {
2092                 if (prompt._input.focused) {
2093                     prompt._input.selectionStart -= 1;
2094                     prompt._input.selectionEnd -= 1;
2095                 }
2096             });
2097             this.addEventListener(enchant.Event.RIGHT_BUTTON_DOWN, function() {
2098                 if (prompt._input.focused) {
2099                     prompt._input.selectionStart += 1;
2100                 }
2101             });
2102         },
2103         /**
2104          * content of prompt
2105          * @type {String}
2106          */
2107         value: {
2108             get: function() {
2109                 return this._prompt.value;
2110 
2111             },
2112             set: function(value) {
2113                 this._prompt.value = value;
2114             }
2115         },
2116         /**
2117          * @type {Function}
2118          */
2119         oncancel: {
2120             get: function() {
2121                 return this._oncancel;
2122             },
2123             set: function(func) {
2124                 this._oncancel = func;
2125             }
2126         },
2127         /**
2128          * @type {Function}
2129          */
2130         onaccept: {
2131             get: function() {
2132                 return this._onaccept;
2133             },
2134             set: function(func) {
2135                 this._onaccept = func;
2136             }
2137         }
2138     });
2139 
2140     /**
2141      * @scope enchant.widget.InputScene
2142      */
2143     enchant.widget.InputScene = enchant.Class.create(enchant.widget.Modal, {
2144         /**
2145          * インプットシーン.
2146          * 他の入力を遮り, 入力画面を表示する.
2147          * {@link enchant.widget.PromptScene}と違い, 複数行に渡る入力ができる.
2148          * @param {*} content 表示するコンテンツ.
2149          * @param {String} acceptName 了承ボタンのラベル.
2150          * @param {String} cancelName キャンセルボタンのラベル.
2151          * @param {String} placeholder プレースホルダ.
2152          * @example
2153          *     var input = new InputScene('新しいツイート', 'ツイート', 'やめる', '@twitter ');
2154          *     input.callback = function(text) {
2155          *         // acceptなら入力された文字列, cancelならnullが返ってってくる.
2156          *     };
2157          *     // cancel, accept個別に処理を設定することも可能.
2158          *     input.oncancel = function() {
2159          *     };
2160          *     input.onaccept = function(text) {
2161          *     };
2162          * @constructs
2163          * @extends enchant.widget.Modal
2164          */
2165         initialize: function(text, acceptName, cancelName, placeholder) {
2166             var core = enchant.Core.instance;
2167             var minheight = enchant.widget._env.inputMinHeight;
2168             var maxheight = enchant.widget._env.inputMaxHeight;
2169             var dh = maxheight - minheight;
2170             this.callback = function() {
2171             };
2172             this._oncancel = function() {
2173             };
2174             this._onaccept = function() {
2175             };
2176             this._menu = null;
2177             cancelName = cancelName || enchant.widget._env.cancelName;
2178             acceptName = acceptName || enchant.widget._env.acceptName;
2179             placeholder = placeholder || '';
2180 
2181             enchant.widget.Modal.call(this);
2182             var scene = this;
2183 
2184             var cancel = new enchant.widget.Button(cancelName);
2185             var accept = new enchant.widget.Button(acceptName);
2186             var bar = new enchant.widget.NavigationBar(text, cancel, accept);
2187             this.addChild(bar);
2188             var textarea = this._textarea = new enchant.widget.InputTextArea();
2189             textarea.y += bar.height;
2190             textarea.width = core.width;
2191             textarea.height = maxheight;
2192             textarea.placeholder = placeholder;
2193             textarea.oncancel = function() {
2194                 core.popScene();
2195                 scene._oncancel.apply(this, arguments);
2196             };
2197             textarea.onaccept = function() {
2198                 core.popScene();
2199                 scene._onaccept.apply(this, arguments);
2200             };
2201             this.addChild(textarea);
2202 
2203             var _area = textarea._textarea;
2204             _area.onfocus = function() {
2205                 Effect.resizeTo.call(textarea, core.width, minheight, 5, enchant.Easing.QUAD_EASEOUT);
2206                 if (scene._menu != null) {
2207                     scene._menu.tl.moveBy(0, -dh, 5, enchant.Easing.QUAD_EASEOUT);
2208                 }
2209             };
2210             _area.onblur = function() {
2211                 Effect.resizeTo.call(textarea, core.width, maxheight, 5, enchant.Easing.QUAD_EASEOUT);
2212                 if (scene._menu != null) {
2213                     scene._menu.tl.moveBy(0, dh, 5, enchant.Easing.QUAD_EASEOUT);
2214                 }
2215             };
2216             cancel.addEventListener(enchant.Event.TOUCH_END, function() {
2217                 textarea.dispatchEvent(new enchant.Event(enchant.Event.CANCEL));
2218                 scene.callback(null);
2219             });
2220             accept.addEventListener(enchant.Event.TOUCH_END, function() {
2221                 textarea.dispatchEvent(new enchant.Event(enchant.Event.ACCEPT));
2222                 scene.callback(textarea.value);
2223             });
2224             this.addEventListener(enchant.Event.UP_BUTTON_DOWN, function() {
2225                 if (textarea.focused) {
2226                     textarea.selectionStart += textarea._prev;
2227                     textarea.selectionEnd += textarea._prev;
2228                     textarea._updateVerticalDist();
2229                 }
2230             });
2231             this.addEventListener(enchant.Event.DOWN_BUTTON_DOWN, function() {
2232                 if (textarea.focused) {
2233                     textarea.selectionStart += textarea._next;
2234                     textarea._updateVerticalDist();
2235                 }
2236             });
2237             this.addEventListener(enchant.Event.LEFT_BUTTON_DOWN, function() {
2238                 if (textarea.focused) {
2239                     textarea.selectionStart -= 1;
2240                     textarea.selectionEnd -= 1;
2241                     textarea._updateVerticalDist();
2242                 }
2243             });
2244             this.addEventListener(enchant.Event.RIGHT_BUTTON_DOWN, function() {
2245                 if (textarea.focused) {
2246                     textarea.selectionStart += 1;
2247                     textarea._updateVerticalDist();
2248                 }
2249             });
2250         },
2251         /**
2252          * @type {*}
2253          */
2254         menu: {
2255             get: function() {
2256                 return this._menu;
2257             },
2258             set: function(menu) {
2259                 if (this._menu) {
2260                     this.removeChild(this._menu);
2261                 }
2262                 this.x = 0;
2263                 this.y = enchant.widget._env.itemHeight + enchant.widget._env.inputMaxHeight;
2264                 this.addChild(menu);
2265                 this._menu = menu;
2266             }
2267         },
2268         /**
2269          * テキストエリアに入力された値.
2270          * @type {String}
2271          */
2272         value: {
2273             get: function() {
2274                 return this._textarea.value;
2275             },
2276             set: function(value) {
2277                 this._textarea.value = value;
2278             }
2279         },
2280         /**
2281          * @type {String}
2282          */
2283         placeholder: {
2284             get: function() {
2285                 return this._textarea.placeholder;
2286             },
2287             set: function(str) {
2288                 this._textarea.placeholder = str;
2289             }
2290         },
2291         /**
2292          * @type {Function}
2293          */
2294         oncancel: {
2295             get: function() {
2296                 return this._oncancel;
2297             },
2298             set: function(func) {
2299                 this._oncancel = func;
2300             }
2301         },
2302         /**
2303          * @type {Function}
2304          */
2305         onaccept: {
2306             get: function() {
2307                 return this._onaccept;
2308             },
2309             set: function(func) {
2310                 this._onaccept = func;
2311             }
2312         }
2313     });
2314 
2315     /**
2316      * @scope enchant.widget.ListElement
2317      */
2318     enchant.widget.ListElement = enchant.Class.create(enchant.widget.EntityGroup, {
2319         /**
2320          * Listの項目.
2321          * 通常, {@link enchant.widget.ListItem}や, {@link enchant.widget.ListItemVertical}を使用する.
2322          * @param {Number} width 要素の横幅.
2323          * @param {Number} height 要素の縦幅.
2324          * @constructs
2325          * @extends enchant.widget.EntityGroup
2326          */
2327         initialize: function(width, height) {
2328             enchant.widget.EntityGroup.call(this, width, height);
2329             this._content;
2330             this._rawContent;
2331         },
2332         /**
2333          * 変更を更新する.
2334          */
2335         refresh: function() {
2336             var content = this._content;
2337             var margin = enchant.widget._env.listItemMargin;
2338             if (content) {
2339                 content.alignLeftIn(this, margin).alignVerticalCenterIn(this);
2340             }
2341             this.background = this._background;
2342         },
2343         /**
2344          * ListElementの内容.
2345          * @type {enchant.Entity[]}
2346          */
2347         content: {
2348             get: function() {
2349                 return this._rawContent;
2350             },
2351             set: function(content) {
2352                 this._rawContent = content;
2353                 content = enchant.widget.parseContent(content);
2354                 if (this._content) {
2355                     this.removeChild(this._content);
2356                 }
2357                 this.addChild(content);
2358                 this._content = content;
2359                 this.refresh();
2360             }
2361         },
2362         /**
2363          * @type {Number}
2364          */
2365         width: {
2366             get: function() {
2367                 return this._width;
2368             },
2369             set: function(width) {
2370                 this._style.width = (this._width = width) + 'px';
2371                 if (this.background instanceof enchant.widget.Ninepatch) {
2372                     this.background.width = this.width;
2373                 }
2374                 if (this._content) {
2375                     this.refresh();
2376                 }
2377             }
2378         },
2379         /**
2380          * @type {Number}
2381          */
2382         height: {
2383             get: function() {
2384                 return this._height;
2385             },
2386             set: function(height) {
2387                 this._style.height = (this._height = height) + 'px';
2388                 if (this.background instanceof enchant.widget.Ninepatch) {
2389                     this.background.height = this.height;
2390                 }
2391                 if (this._content) {
2392                     this.refresh();
2393                 }
2394             }
2395         }
2396     });
2397 
2398     /**
2399      * @scope enchant.widget.ListItem
2400      */
2401     enchant.widget.ListItem = enchant.Class.create(enchant.widget.ListElement, {
2402         /**
2403          * Listの項目.
2404          * アイコン, 左端のボタンが設定できる.
2405          * 縦に並ぶ項目を設定したいときは, {@link enchant.widget.ListItemVertical}を使用する.
2406          * @param {Number} width 要素の横幅.
2407          * @param {Number} height 要素の縦幅.
2408          * @param {*} [content] ListItemの内容.
2409          * @param {enchant.Sprite|enchant.Surface} [icon] ListItemのアイコン.
2410          * @param {enchant.Sprite|enchant.Surface} [rightIcon] ListItemの左側のアイコン.
2411          * @see enchant.widget.ListItemVertical
2412          * @constructs
2413          * @extends enchant.widget.ListElement
2414          */
2415         initialize: function(width, height, content, icon, rightIcon) {
2416             var core = enchant.Core.instance;
2417             width = width || core.width;
2418             height = height || enchant.widget._env.itemHeight;
2419             content = content || '';
2420             enchant.widget.ListElement.call(this, width, height);
2421             this._icon;
2422             this._rawIcon;
2423             this._rightIcon;
2424             this._rawRightIcon;
2425             this.content = content;
2426             if (icon) {
2427                 this.icon = icon;
2428             }
2429             if (rightIcon) {
2430                 this.rightIcon = rightIcon;
2431             }
2432             var np = new enchant.widget.Ninepatch(this.width, this.height);
2433             np.src = core.assets['listItemBg.png'];
2434             this.background = np;
2435         },
2436         /**
2437          * 変更を更新する.
2438          */
2439         refresh: function() {
2440             var icon = this._icon;
2441             var content = this._content;
2442             var right = this._rightIcon;
2443             var margin = enchant.widget._env.listItemMargin;
2444             if (icon) {
2445                 Adjust.fitToY.call(icon, this, margin, margin);
2446                 icon.alignLeftIn(this, margin).alignVerticalCenterIn(this);
2447                 if (content) {
2448                     content.alignRightOf(icon, margin).alignVerticalCenterIn(this);
2449                 }
2450             } else if (content) {
2451                 content.alignLeftIn(this, margin).alignVerticalCenterIn(this);
2452             }
2453             if (right) {
2454                 right.alignRightIn(this, margin).alignVerticalCenterIn(this);
2455             }
2456         },
2457         /**
2458          * アイコン.
2459          * 左側に表示される.
2460          * @type {enchant.Sprite|enchant.Surface}
2461          */
2462         icon: {
2463             get: function() {
2464                 return this._rawIcon;
2465             },
2466             set: function(icon) {
2467                 this._rawIcon = icon;
2468                 icon = enchant.widget.parseContent(icon);
2469                 if (this._icon) {
2470                     this.removeChild(this._icon);
2471                 }
2472                 this.addChild(icon);
2473                 this._icon = icon;
2474                 this.refresh();
2475             }
2476         },
2477         /**
2478          * 右側のアイコン.
2479          * 右側に表示される.
2480          * @type {enchant.Sprite|enchant.Surface}
2481          */
2482         rightIcon: {
2483             get: function() {
2484                 return this._rawRightIcon;
2485             },
2486             set: function(right) {
2487                 this._rawRightIcon = right;
2488                 right = enchant.widget.parseContent(right);
2489                 if (this._rightIcon) {
2490                     this.removeChild(this._rightIcon);
2491                 }
2492                 this.addChild(right);
2493                 this._rightIcon = right;
2494                 this.refresh();
2495             }
2496         }
2497     });
2498 
2499     /**
2500      * @scope enchant.widget.ListItemVertical
2501      */
2502     enchant.widget.ListItemVertical = enchant.Class.create(enchant.widget.ListElement, {
2503         /**
2504          * Listの項目.
2505          * ヘッダ, フッタが設定できる.
2506          * @param {Number} width 要素の横幅.
2507          * @param {Number} height 要素の縦幅.
2508          * @param {*} [content] ListItemVerticalの内容.
2509          * @param {*} [header] ListItemのヘッダ.
2510          * @param {*} [footer] ListItemのフッタ.
2511          * @constructs
2512          * @extends enchant.widget.ListElement
2513          */
2514         initialize: function(width, height, content, header, footer) {
2515             var core = enchant.Core.instance;
2516             enchant.widget.ListElement.call(this, width, height);
2517             this._header;
2518             this._rawHeader;
2519             this._footer;
2520             this._rawFooter;
2521             if (content) {
2522                 this.content = content;
2523             }
2524             if (header) {
2525                 this.header = header;
2526             }
2527             if (footer) {
2528                 this.footer = footer;
2529             }
2530             this.refresh();
2531             var np = new enchant.widget.Ninepatch(this.width, this.height);
2532             np.src = core.assets['listItemBg.png'];
2533             this.background = np;
2534         },
2535         /**
2536          * 変更を更新する.
2537          */
2538         refresh: function() {
2539             var header = this._header;
2540             var footer = this._footer;
2541             var content = this._content;
2542             var margin = enchant.widget._env.listItemMargin;
2543             if (header) {
2544                 header.alignLeftIn(this, margin).alignTopIn(this, margin);
2545 
2546                 Adjust.fillX.call(content, this, margin);
2547                 if (content) {
2548                     content.alignLeftIn(this, margin).alignBottomOf(header, margin);
2549                 }
2550             } else {
2551                 Adjust.fillX.call(content, this, margin);
2552                 if (content) {
2553                     content.alignLeftIn(this, margin).alignTopIn(this, margin);
2554                 }
2555             }
2556             if (footer) {
2557                 footer.alignLeftIn(this, margin).alignBottomOf(content, margin);
2558             }
2559             var height = 0;
2560             var p;
2561             var scale;
2562             var contents = [ header, content, footer ];
2563             for (prop in contents) {
2564                 p = contents[prop];
2565                 if (p) {
2566                     scale = p.scaleY || 1;
2567                     height += ~~(p.height * scale);
2568                     height += margin * 2;
2569                 }
2570             }
2571             this._style.height = (this._height = height) + 'px';
2572             if (this.background instanceof enchant.widget.Ninepatch) {
2573                 this.background.height = this.height;
2574             }
2575         },
2576         /**
2577          * ヘッダ.
2578          * contentの上に表示される.
2579          * @type {*}
2580          */
2581         header: {
2582             get: function() {
2583                 return this._rawHeader;
2584             },
2585             set: function(header) {
2586                 this._rawHeader = header;
2587                 header = enchant.widget.parseContent(header);
2588                 if (this._header) {
2589                     this.removeChild(this._header);
2590                 }
2591                 this.addChild(header);
2592                 this._header = header;
2593                 this.refresh();
2594             }
2595         },
2596         /**
2597          * フッタ.
2598          * contentの下に表示される.
2599          * @type {*}
2600          */
2601         footer: {
2602             get: function() {
2603                 return this._rawFooter;
2604             },
2605             set: function(footer) {
2606                 this._rawFooter = footer;
2607                 footer = enchant.widget.parseContent(footer);
2608                 if (this._footer) {
2609                     this.removeChild(this._footer);
2610                 }
2611                 this.addChild(footer);
2612                 this._footer = footer;
2613                 this.refresh();
2614             }
2615         }
2616     });
2617 
2618     /**
2619      * @scope enchant.widget.ScrollView
2620      */
2621     enchant.widget.ScrollView = enchant.Class.create(enchant.widget.EntityGroup, {
2622         /**
2623          * スクロールビュー.
2624          * 設定したコンテンツをスクロール可能.
2625          * @param {Number} width ビューの横幅.
2626          * @param {Number} height ビューの縦幅.
2627          * @constructs
2628          * @extends enchant.widget.EntityGroup
2629          */
2630         initialize: function(width, height) {
2631             enchant.widget.EntityGroup.call(this, width, height);
2632             this._style.overflow = 'hidden';
2633             this._content;
2634         },
2635         /**
2636          * ScrollViewの内容.
2637          * @type {enchant.Entity}
2638          */
2639         content: {
2640             get: function() {
2641                 return this._content;
2642             },
2643             set: function(content) {
2644                 if (this._content) {
2645                     this.removeChild(this._content);
2646                 }
2647                 this.addChild(content);
2648                 this._content = content;
2649             }
2650         },
2651         /**
2652          * コンテンツのスクロールを行う.
2653          * 正の値が上方向のスクロールとなる.
2654          * @param {Number} dy スクロール量.
2655          */
2656         scroll: function(dy) {
2657             if (!this._content) {
2658                 return;
2659             }
2660             if (this.height >= this._content.height) {
2661                 this._content.y = 0;
2662                 return;
2663             }
2664             var max = 0
2665             var min = this.height - this._content.height
2666 
2667             var sy = this._content.y + dy;
2668             if (sy > max) {
2669                 dy = max - this._content.y;
2670             } else if (sy < min) {
2671                 dy = min - this._content.y;
2672             }
2673             this._content.y += dy;
2674         }
2675     });
2676 
2677     /**
2678      * @scope enchant.widget.ListView
2679      */
2680     enchant.widget.ListView = enchant.Class.create(enchant.widget.ScrollView, {
2681         /**
2682          * リストビュー.
2683          * @param {Number} width メニューの横幅.
2684          * @param {Number} height メニューの縦幅.
2685          * @param {Boolean} draggable 項目をドラッグできるか設定する.
2686          * List view.
2687          * @constructs
2688          * @extends enchant.widget.ScrollView
2689          */
2690         initialize: function(width, height, draggable) {
2691             enchant.widget.ScrollView.call(this, width, height);
2692             var detector = new enchant.widget.GestureDetector(this);
2693             this.draggable = !!draggable;
2694             this.content = [];
2695             var dragging = null;
2696             var dy = 0;
2697             var prev = null;
2698             var next = null;
2699             var pthreshold = 0;
2700             var nthreshold = 0;
2701             this._clipping = true;
2702 
2703             enchant.widget.GestureDetector.gestureEvents.forEach(function(type) {
2704                 this.addEventListener(type, function(e) {
2705                     var item = this.getSelectedItem(e);
2706                     if (item != null) {
2707                         item.dispatchEvent(e);
2708                     }
2709                 });
2710             }, this);
2711 
2712             var removeChild = enchant.widget.EntityGroup.prototype.removeChild;
2713             var insertBefore = enchant.widget.EntityGroup.prototype.insertBefore;
2714 
2715             var that = this;
2716             var checkChangePos = function(direction) {
2717                 var y = dragging.y;
2718                 var my = dragging.height;
2719                 var nextSibling;
2720                 if (prev && y <= pthreshold && direction < 0) {
2721                     prev.y += my;
2722                     removeChild.call(that._content, dragging);
2723                     insertBefore.call(that._content, dragging, prev);
2724                     updateHoldStat(dragging);
2725                 } else if (next && nthreshold <= y && direction > 0) {
2726                     next.y -= my;
2727                     removeChild.call(that._content, dragging);
2728                     var nextSibling = that._content.childNodes[that._content.childNodes.indexOf(next) + 1];
2729                     insertBefore.call(that._content, dragging, nextSibling);
2730                     updateHoldStat(dragging);
2731                 }
2732             };
2733 
2734             var updateHoldStat = function(element) {
2735                 var i = that._content.childNodes.indexOf(element);
2736                 if (i > 0) {
2737                     prev = that._content.childNodes[i - 1];
2738                     pthreshold = prev.y + prev.height - element.height / 2;
2739                 } else {
2740                     prev = null;
2741                 }
2742                 if (i < that._content.childNodes.length - 1) {
2743                     next = that._content.childNodes[i + 1];
2744                     nthreshold = next.y - element.height / 2;
2745                 } else {
2746                     next = null;
2747                 }
2748             };
2749             this.addEventListener(enchant.Event.ENTER_FRAME, function() {
2750                 if (dy != 0) {
2751                     var old = this._content.y;
2752                     this.scroll(dy);
2753                     checkChangePos(-dy);
2754                     dragging.y -= this._content.y - old;
2755                 }
2756             });
2757             this.addEventListener(enchant.Event.HOLD, function(e) {
2758                 if (!this.draggable) {
2759                     return;
2760                 }
2761                 dragging = this.getSelectedItem(e);
2762                 if (dragging == null) {
2763                     return;
2764                 }
2765                 dragging.opacity = 0.8;
2766                 dragging._style.zIndex = 2;
2767                 updateHoldStat(dragging);
2768             });
2769             this.addEventListener(enchant.Event.RELEASE, function() {
2770                 if (!this.draggable || dragging == null) {
2771                     return;
2772                 }
2773                 dy = 0;
2774                 if (prev) {
2775                     dragging.y = prev.y + prev.height;
2776                 } else {
2777                     dragging.y = 0;
2778                 }
2779                 dragging.opacity = 1.0;
2780                 dragging._style.zIndex = 0;
2781                 dragging = null;
2782                 prev = null;
2783                 next = null;
2784             });
2785             var spd = 40;
2786             this.addEventListener(enchant.Event.DRAG, function(e) {
2787                 if (!this.draggable || dragging == null) {
2788                     return;
2789                 }
2790                 checkChangePos(e.dy);
2791                 dragging.y += e.dy;
2792                 if (e.localY < spd) {
2793                     dy = spd - e.localY;
2794                 } else if (this.height - spd < e.localY) {
2795                     dy = this.height - spd - e.localY;
2796                 } else {
2797                     dy = 0;
2798                 }
2799             });
2800             this.addEventListener(enchant.Event.SLIP, function(e) {
2801                 this.scroll(e.dy);
2802             });
2803         },
2804         /**
2805          * リストビューの内容を設定する.
2806          * @type {enchant.widget.ListElement[]}
2807          */
2808         content: {
2809             get: function() {
2810                 return this._content.childNodes;
2811             },
2812             set: function(content) {
2813                 var addChild = enchant.widget.EntityGroup.prototype.addChild;
2814                 var removeChild = enchant.widget.EntityGroup.prototype.removeChild;
2815                 if (this._content) {
2816                     removeChild.call(this, this._content);
2817                 }
2818                 var list = new List(content);
2819                 list.width = this.width;
2820                 addChild.call(this, list);
2821                 this._content = list;
2822             }
2823         },
2824         /**
2825          * イベントの対象を取得する.
2826          * @param {enchant.Event} event
2827          * @return {enchant.widget.ListElement}
2828          */
2829         getSelectedItem: function(e) {
2830             var y = e.localY - this._content.y;
2831             var list = this._content;
2832             var child;
2833             var h = 0;
2834             for (var i = 0, l = list.childNodes.length; i < l; i++) {
2835                 child = list.childNodes[i];
2836                 h += child.height;
2837                 if (h > y) {
2838                     return child;
2839                 }
2840             }
2841             return null;
2842         },
2843         addChild: function(child) {
2844             this._content.addChild(child);
2845         },
2846         removeChild: function(child) {
2847             this._content.removeChild(child);
2848         },
2849         insertBefore: function(child, reference) {
2850             this._content.insertBefore(child, reference);
2851         }
2852     });
2853     
2854     
2855     var _emptyFunction = function(){};
2856 
2857     /**
2858      * @scope enchant.widget.LazyListItem
2859      */
2860     enchant.widget.LazyListItem = enchant.Class.create(enchant.widget.ListItem, {
2861         /**
2862          * LazyListItem is a class which can be used in an enchant.widget.LazyListView instance
2863          * to enable the lazy loading of the content of a list item.
2864          * The LazyListView instance will call the loadResources function of the LazyListItem instance
2865          * when it feels like the content will be required soon.
2866          * Implementors must implement the loadResources function.
2867          * See the LazyListView example of enchant.js (examples/plugins/widget/LazyListView/).
2868          * 
2869          * @see enchant.widget.LazyListView 
2870          * @see enchant.widget.ListItem
2871          * @constructs
2872          * @extends enchant.widget.ListItem
2873          */
2874         initialize: function() {
2875             enchant.widget.ListItem.apply(this, arguments);
2876         },
2877         _loadResources: function() {
2878             if(this.loadResources) {
2879                 this.loadResources();
2880                 this._loadResources = _emptyFunction;
2881             }
2882         }
2883     });
2884     
2885     var _enchantWidgetListViewPrototypeScroll = enchant.widget.ListView.prototype.scroll;
2886     var _enchantWidgetListViewPrototypeAddChild = enchant.widget.ListView.prototype.addChild;
2887     var _enchantWidgetListViewPrototypeRemoveChild = enchant.widget.ListView.prototype.removeChild;
2888     var _enchantWidgetListViewPrototypeInsertBefore = enchant.widget.ListView.prototype.insertBefore;
2889     
2890     /**
2891      * @scope enchant.widget.LazyListView
2892      */
2893     enchant.widget.LazyListView = enchant.Class.create(enchant.widget.ListView, {
2894         /**
2895          * LazyListView is a class which enables a ListView to load the content of it's
2896          * child ListItems lazily by using enchant.widget.LazyListItem (s).
2897          * It is also possible to use regular ListItems which will behave
2898          * like if they where added to a ListView (therefore, no lazy initialization).
2899          * Furthermore, this LazyListView will dispatch an enchant.Event.CONTENT_REQUESTED event
2900          * when the ListView feels like the available content is not sufficient. 
2901          * The enchant.Event.CONTENT_REQUESTED can be used to add new items to the LazyListView.
2902          * See the LazyListView example of enchant.js (examples/plugins/widget/LazyListView/).
2903          * 
2904          * @see enchant.widget.ListView 
2905          * @see enchant.widget.LazyListItem
2906          * @constructs
2907          * @extends enchant.widget.ListView
2908          */
2909         initialize: function() {
2910             enchant.widget.ListView.apply(this, arguments);
2911         },
2912         _updateContent: function() {
2913             var visibleHeight = this.height - this._content.y + 100;
2914             var content = this.content;
2915             var contentLength = content.length;
2916             for(var i = 0; i < contentLength; i++) {
2917                 if(content[i].y <= visibleHeight && content[i]._loadResources) {
2918                     content[i]._loadResources.call(content[i]);
2919                 } else if(content[i].y > visibleHeight) {
2920                     return;
2921                 }
2922             }
2923             
2924             var event = new enchant.Event(enchant.Event.CONTENT_REQUESTED);
2925             this.dispatchEvent(event); // as we did not return in the loop there are not enough list items available, request new content
2926         },
2927         scroll: function() {
2928             _enchantWidgetListViewPrototypeScroll.apply(this, arguments);
2929             this._updateContent();
2930         },
2931         addChild: function(child) {
2932             _enchantWidgetListViewPrototypeAddChild.apply(this, arguments);
2933             this._updateContent();
2934         },
2935         removeChild: function(child) {
2936             _enchantWidgetListViewPrototypeRemoveChild.apply(this, arguments);
2937             this._updateContent();
2938         },
2939         insertBefore: function(child, reference) {
2940             _enchantWidgetListViewPrototypeInsertBefore.apply(this, arguments);
2941             this._updateContent();
2942         }
2943     });
2944 
2945     var List = enchant.Class.create(enchant.widget.EntityGroup, {
2946         initialize: function(array) {
2947             var core = enchant.Core.instance;
2948             enchant.widget.EntityGroup.call(this);
2949             this.width = core.width;
2950             this.height = core.height;
2951             this._itemHeight = 0;
2952             var element;
2953             for (var i = 0, l = array.length; i < l; i++) {
2954                 element = array[i];
2955                 this.addChild(element);
2956             }
2957 
2958             this._dragging = null;
2959             this._pthreshold = 0;
2960             this._nthreshold = 0;
2961             this._index = 0;
2962         },
2963         addChild: function(child) {
2964             var i = this.childNodes.length;
2965             enchant.widget.EntityGroup.prototype.addChild.call(this, child);
2966             this.refresh(i - 1);
2967         },
2968         insertBefore: function(child, reference) {
2969             enchant.widget.EntityGroup.prototype.insertBefore.call(this, child, reference);
2970             var i = this.childNodes.indexOf(child);
2971             this.refresh(i - 1);
2972         },
2973         removeChild: function(child) {
2974             var i = this.childNodes.indexOf(child);
2975             if (i != -1) {
2976                 enchant.widget.EntityGroup.prototype.removeChild.call(this, child);
2977                 this.refresh(i - 1);
2978             }
2979         },
2980         refresh: function(i) {
2981             var i, l, h, start, child;
2982             if (i > 0) {
2983                 start = this.childNodes[i - 1];
2984                 h = start.y + start.height;
2985             } else {
2986                 i = 0;
2987                 h = 0;
2988             }
2989             for (l = this.childNodes.length; i < l; i++) {
2990                 child = this.childNodes[i];
2991                 child.y = h;
2992                 h += child.height;
2993             }
2994             this.height = this._itemHeight = h;
2995         },
2996         _getElementByLocalPosition: function(localX, localY) {
2997             var child;
2998             var h = 0;
2999             for (var i = 0, l = this.childNodes.length; i < l; i++) {
3000                 child = this.childNodes[i];
3001                 h += child.height;
3002                 if (h > localY) {
3003                     break;
3004                 }
3005             }
3006             return child;
3007         }
3008     });
3009 
3010     /**
3011      * @scope enchant.widget.NavigationBar
3012      */
3013     enchant.widget.NavigationBar = enchant.Class.create(enchant.widget.EntityGroup, {
3014         /**
3015          * ナビゲーションバー.
3016          * 中央, 右, 左それぞれに項目を設定できる.
3017          * @param {*} center 中央に表示させたい項目.
3018          * @param {*} left 左に表示させたい項目.
3019          * @param {*} right 右に表示させたい項目.
3020          * @constructs
3021          * @extends enchant.widget.EntityGroup
3022          */
3023         initialize: function(center, left, right) {
3024             var core = enchant.Core.instance;
3025             enchant.widget.EntityGroup.call(this, core.width, enchant.widget._env.itemHeight);
3026             this._center;
3027             this._rawCenter;
3028             this._left;
3029             this._rawLeft;
3030             this._right;
3031             this._rawRight;
3032             this.center = center;
3033             if (left) {
3034                 this.left = left;
3035             }
3036             if (right) {
3037                 this.right = right;
3038             }
3039             this.refresh();
3040 
3041             var np = new enchant.widget.Ninepatch(this.width, this.height);
3042             np.src = core.assets['navigationBar.png'];
3043             this.background = np;
3044         },
3045         /**
3046          * 変更を更新する.
3047          */
3048         refresh: function() {
3049             var center = this._center;
3050             var left = this._left;
3051             var right = this._right;
3052             var margin = enchant.widget._env.listItemMargin;
3053             if (center) {
3054                 center.alignHorizontalCenterIn(this).alignVerticalCenterIn(this);
3055             }
3056             if (left) {
3057                 left.alignLeftIn(this, margin).alignVerticalCenterIn(this);
3058             }
3059             if (right) {
3060                 right.alignRightIn(this, margin).alignVerticalCenterIn(this);
3061             }
3062         },
3063         /**
3064          * 中央の項目.
3065          * @type {*}
3066          */
3067         center: {
3068             get: function() {
3069                 return this._rawCenter;
3070             },
3071             set: function(center) {
3072                 this._rawCenter = center;
3073                 center = enchant.widget.parseContent(center, enchant.widget._env.navigationBarFont);
3074                 if (this._center) {
3075                     this.removeChild(this._center);
3076                 }
3077                 this.addChild(center);
3078                 this._center = center;
3079                 this.refresh();
3080             }
3081         },
3082         /**
3083          * 左の項目.
3084          * 左詰めで表示される.
3085          * @type {*}
3086          */
3087         left: {
3088             get: function() {
3089                 return this._rawLeft;
3090             },
3091             set: function(left) {
3092                 this._rawLeft = left;
3093                 left = enchant.widget.parseContent(left);
3094                 if (this._left) {
3095                     this.removeChild(this._left);
3096                 }
3097                 this.addChild(left);
3098                 this._left = left;
3099                 this.refresh();
3100             }
3101         },
3102         /**
3103          * 右の項目.
3104          * 右詰めで表示される.
3105          * @type {*}
3106          */
3107         right: {
3108             get: function() {
3109                 return this._rawRight;
3110             },
3111             set: function(right) {
3112                 this._rawRight = right;
3113                 right = enchant.widget.parseContent(right);
3114                 if (this._right) {
3115                     this.removeChild(this._right);
3116                 }
3117                 this.addChild(right);
3118                 this._right = right;
3119                 this.refresh();
3120             }
3121         }
3122     });
3123 
3124     enchant.widget.Icon = enchant.Class.create(enchant.widget.EntityGroup, {
3125         initialize: function(icon, text) {
3126             enchant.widget.EntityGroup.call(this, 44, 44);
3127             icon = enchant.widget.parseContent(icon);
3128             text = enchant.widget.parseContent(text, enchant.widget._env.font);
3129             var sx = 32 / icon.width;
3130             var sy = 32 / icon.height;
3131             icon.scaleX = icon.scaleY = Math.min(sx, sy);
3132             icon.alignHorizontalCenterIn(this).alignTopIn(this);
3133             text.alignHorizontalCenterIn(this).alignBottomOf(icon, -7);
3134             this.addChild(icon);
3135             this.addChild(text);
3136         }
3137     });
3138 
3139     /**
3140      * @scope enchant.widget.IconMenu
3141      */
3142     enchant.widget.IconMenu = enchant.Class.create(enchant.widget.EntityGroup, {
3143         /**
3144          * アイコンが横に並ぶメニュー.
3145          * @param {enchant.Entity[]} buttons 設定したいボタンの配列.
3146          * @constructs
3147          * @extends enchant.widget.EntityGroup
3148          */
3149         initialize: function(buttons) {
3150             var core = enchant.Core.instance;
3151             if (!(buttons instanceof Array)) {
3152                 buttons = Array.prototype.slice.call(arguments);
3153             }
3154             enchant.widget.EntityGroup.call(this, core.width, enchant.widget._env.itemHeight);
3155             this._bgs = [];
3156             this._icons = [];
3157             this.content = buttons;
3158             this.refresh();
3159             this._bgs.forEach(function(bg) {
3160                 var width = bg.width;
3161                 var height = bg.height;
3162                 var np = new enchant.widget.Ninepatch(width, height);
3163                 np.src = core.assets['iconMenuBg.png'];
3164                 bg.image = np;
3165             });
3166         },
3167         /**
3168          * 変更を更新する.
3169          */
3170         getSelectedItem: function(e) {
3171             var x = e.localX;
3172             var list = this._bgs;
3173             var child;
3174             var w = 0;
3175             for (var i = 0, l = list.childNodes.length; i < l; i++) {
3176                 child = list.childNodes[i];
3177                 w += child.width;
3178                 if (w > x) {
3179                     return this._icons[i];
3180                 }
3181             }
3182             return null;
3183         },
3184         refresh: function() {
3185             var icon, bg, bgwidth;
3186             var margin = enchant.widget._env.listItemMargin;
3187             var arr = distribute(this.width, this._icons.length);
3188             var _width = 0;
3189             var menu = this;
3190 
3191             for (var i = 0, l = this._icons.length; i < l; i++) {
3192                 bgwidth = arr[i];
3193                 icon = this._icons[i];
3194                 bg = this._bgs[i];
3195                 bg.width = bgwidth;
3196                 bg.height = this.height;
3197                 bg.image.resize(bg.width, bg.height);
3198                 bg.x = _width;
3199 
3200                 icon.addEventListener(enchant.Event.TOUCH_END, (function(bg) {
3201                     return function(e) {
3202                         bg.dispatchEvent(e);
3203                     };
3204                 })(bg));
3205                 bg.addEventListener(enchant.Event.TOUCH_END, (function(i, elem) {
3206                     return function(e) {
3207                         var evt = new enchant.Event(enchant.Event.TAP);
3208                         evt.x = e.x;
3209                         evt.y = e.y;
3210                         evt.index = i;
3211                         evt.element = elem;
3212                         menu.dispatchEvent(evt);
3213                     };
3214                 })(i, icon));
3215 
3216                 icon.alignHorizontalCenterIn(bg).alignVerticalCenterIn(bg);
3217                 icon.x += _width;
3218 
3219                 _width += bg.width;
3220             }
3221         },
3222         addChild: function(child) {
3223             var core = enchant.Core.instance;
3224             var addChild = enchant.widget.EntityGroup.prototype.addChild;
3225             var size = enchant.widget._env.itemHeight;
3226             var sp = new enchant.Sprite(size, size);
3227             addChild.call(this, sp);
3228             this._bgs.push(sp);
3229             addChild.call(this, child);
3230             this._icons.push(child);
3231             var np = new enchant.widget.Ninepatch(sp.width, sp.height);
3232             np.src = core.assets['iconMenuBg.png'];
3233             sp.image = np;
3234             this.refresh();
3235         },
3236         insertBefore: function(child, target) {
3237             var core = enchant.Core.instance;
3238             var insertBefore = enchant.widget.EntityGroup.prototype.insertBefore;
3239             var i = this._icons.indexOf(target);
3240             var size = enchant.widget._env.itemHeight;
3241             var sp, np;
3242             if (i != -1) {
3243                 target = this._bgs[i];
3244                 sp = new enchant.Sprite(size, size);
3245                 insertBefore.call(this, sp, target);
3246                 this._bgs.splice(i, 0, sp);
3247                 insertBefore.call(this, child, target);
3248                 this._icons.splice(i, 0, child);
3249                 np = new enchant.widget.Ninepatch(sp.width, sp.height);
3250                 np.src = core.assets['iconMenuBg.png'];
3251                 sp.image = np;
3252                 this.refresh();
3253             }
3254         },
3255         removeChild: function(child) {
3256             var removeChild = enchant.widget.EntityGroup.prototype.removeChild;
3257             var i = this._icons.indexOf(child);
3258             if (i != -1) {
3259                 var bg = this._bgs[this._bgs.length - 1];
3260                 removeChild.call(this, bg);
3261                 this._bgs.pop();
3262                 removeChild.call(this, child);
3263                 this._icons.splice(i, 1);
3264                 this.refresh();
3265             }
3266         },
3267         /**
3268          * アイコンを設定する.
3269          * @param {enchant.Entity[]} content 表示させたいオブジェクトの配列.
3270          */
3271         content: {
3272             get: function() {
3273                 return this._icons;
3274             },
3275             set: function(content) {
3276                 var removeChild = enchant.widget.EntityGroup.prototype.removeChild;
3277                 var menu = this;
3278                 if (this.childNodes) {
3279                     this.childNodes.slice().forEach(function(child) {
3280                         removeChild.call(menu, child);
3281                     });
3282                 }
3283                 content.forEach(function(child) {
3284                     menu.addChild(child);
3285                 });
3286             }
3287         }
3288     });
3289 
3290 })();
3291