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