[483] | 1 | define([ |
---|
| 2 | "dojo/_base/array", |
---|
| 3 | "dojo/_base/declare", |
---|
| 4 | "dojo/_base/lang", |
---|
| 5 | "dojo/_base/window", |
---|
| 6 | "dojo/dom-class", |
---|
| 7 | "dojo/touch", |
---|
| 8 | "dijit/registry", |
---|
| 9 | "dijit/_Contained", |
---|
| 10 | "dijit/_Container", |
---|
| 11 | "dijit/_WidgetBase", |
---|
| 12 | "./TransitionEvent", |
---|
| 13 | "./iconUtils", |
---|
| 14 | "./sniff", |
---|
| 15 | "dojo/has!dojo-bidi?dojox/mobile/bidi/_ItemBase" |
---|
| 16 | ], function(array, declare, lang, win, domClass, touch, registry, Contained, Container, WidgetBase, TransitionEvent, iconUtils, has, BidiItemBase){ |
---|
| 17 | |
---|
| 18 | // module: |
---|
| 19 | // dojox/mobile/_ItemBase |
---|
| 20 | |
---|
| 21 | var _ItemBase = declare(has("dojo-bidi") ? "dojox.mobile._NonBidiItemBase" : "dojox.mobile._ItemBase", [WidgetBase, Container, Contained], { |
---|
| 22 | // summary: |
---|
| 23 | // A base class for item classes (e.g. ListItem, IconItem, etc.). |
---|
| 24 | // description: |
---|
| 25 | // _ItemBase is a base class for widgets that have capability to |
---|
| 26 | // make a view transition when clicked. |
---|
| 27 | |
---|
| 28 | // icon: String |
---|
| 29 | // An icon image to display. The value can be either a path for an |
---|
| 30 | // image file or a class name of a DOM button. If icon is not |
---|
| 31 | // specified, the iconBase parameter of the parent widget is used. |
---|
| 32 | icon: "", |
---|
| 33 | |
---|
| 34 | // iconPos: String |
---|
| 35 | // The position of an aggregated icon. IconPos is comma separated |
---|
| 36 | // values like top,left,width,height (ex. "0,0,29,29"). If iconPos |
---|
| 37 | // is not specified, the iconPos parameter of the parent widget is |
---|
| 38 | // used. |
---|
| 39 | iconPos: "", // top,left,width,height (ex. "0,0,29,29") |
---|
| 40 | |
---|
| 41 | // alt: String |
---|
| 42 | // An alternate text for the icon image. |
---|
| 43 | alt: "", |
---|
| 44 | |
---|
| 45 | // href: String |
---|
| 46 | // A URL of another web page to go to. |
---|
| 47 | href: "", |
---|
| 48 | |
---|
| 49 | // hrefTarget: String |
---|
| 50 | // A target that specifies where to open a page specified by |
---|
| 51 | // href. The value will be passed to the 2nd argument of |
---|
| 52 | // window.open(). |
---|
| 53 | hrefTarget: "", |
---|
| 54 | |
---|
| 55 | // moveTo: String |
---|
| 56 | // The id of the transition destination view which resides in the |
---|
| 57 | // current page. |
---|
| 58 | // |
---|
| 59 | // If the value has a hash sign ('#') before the id (e.g. #view1) |
---|
| 60 | // and the dojo/hash module is loaded by the user application, the |
---|
| 61 | // view transition updates the hash in the browser URL so that the |
---|
| 62 | // user can bookmark the destination view. In this case, the user |
---|
| 63 | // can also use the browser's back/forward button to navigate |
---|
| 64 | // through the views in the browser history. |
---|
| 65 | // |
---|
| 66 | // If null, transitions to a blank view. |
---|
| 67 | // If '#', returns immediately without transition. |
---|
| 68 | moveTo: "", |
---|
| 69 | |
---|
| 70 | // scene: String |
---|
| 71 | // The name of a scene. Used from dojox/mobile/app. |
---|
| 72 | scene: "", |
---|
| 73 | |
---|
| 74 | // clickable: Boolean |
---|
| 75 | // If true, this item becomes clickable even if a transition |
---|
| 76 | // destination (moveTo, etc.) is not specified. |
---|
| 77 | clickable: false, |
---|
| 78 | |
---|
| 79 | // url: String |
---|
| 80 | // A URL of an html fragment page or JSON data that represents a |
---|
| 81 | // new view content. The view content is loaded with XHR and |
---|
| 82 | // inserted in the current page. Then a view transition occurs to |
---|
| 83 | // the newly created view. The view is cached so that subsequent |
---|
| 84 | // requests would not load the content again. |
---|
| 85 | url: "", |
---|
| 86 | |
---|
| 87 | // urlTarget: String |
---|
| 88 | // Node id under which a new view will be created according to the |
---|
| 89 | // url parameter. If not specified, The new view will be created as |
---|
| 90 | // a sibling of the current view. |
---|
| 91 | urlTarget: "", |
---|
| 92 | |
---|
| 93 | // back: Boolean |
---|
| 94 | // If true, history.back() is called when clicked. |
---|
| 95 | back: false, |
---|
| 96 | |
---|
| 97 | // transition: String |
---|
| 98 | // A type of animated transition effect. You can choose from the |
---|
| 99 | // standard transition types, "slide", "fade", "flip", or from the |
---|
| 100 | // extended transition types, "cover", "coverv", "dissolve", |
---|
| 101 | // "reveal", "revealv", "scaleIn", "scaleOut", "slidev", |
---|
| 102 | // "swirl", "zoomIn", "zoomOut", "cube", and "swap". If "none" is |
---|
| 103 | // specified, transition occurs immediately without animation. |
---|
| 104 | transition: "", |
---|
| 105 | |
---|
| 106 | // transitionDir: Number |
---|
| 107 | // The transition direction. If 1, transition forward. If -1, |
---|
| 108 | // transition backward. For example, the slide transition slides |
---|
| 109 | // the view from right to left when dir == 1, and from left to |
---|
| 110 | // right when dir == -1. |
---|
| 111 | transitionDir: 1, |
---|
| 112 | |
---|
| 113 | // transitionOptions: Object |
---|
| 114 | // A hash object that holds transition options. |
---|
| 115 | transitionOptions: null, |
---|
| 116 | |
---|
| 117 | // callback: Function|String |
---|
| 118 | // A callback function that is called when the transition has been |
---|
| 119 | // finished. A function reference, or name of a function in |
---|
| 120 | // context. |
---|
| 121 | callback: null, |
---|
| 122 | |
---|
| 123 | // label: String |
---|
| 124 | // A label of the item. If the label is not specified, innerHTML is |
---|
| 125 | // used as a label. |
---|
| 126 | label: "", |
---|
| 127 | |
---|
| 128 | // toggle: Boolean |
---|
| 129 | // If true, the item acts like a toggle button. |
---|
| 130 | toggle: false, |
---|
| 131 | |
---|
| 132 | // selected: Boolean |
---|
| 133 | // If true, the item is highlighted to indicate it is selected. |
---|
| 134 | selected: false, |
---|
| 135 | |
---|
| 136 | // tabIndex: String |
---|
| 137 | // Tabindex setting for the item so users can hit the tab key to |
---|
| 138 | // focus on it. |
---|
| 139 | tabIndex: "0", |
---|
| 140 | |
---|
| 141 | // _setTabIndexAttr: [private] String |
---|
| 142 | // Sets tabIndex to domNode. |
---|
| 143 | _setTabIndexAttr: "", |
---|
| 144 | |
---|
| 145 | /* internal properties */ |
---|
| 146 | |
---|
| 147 | // paramsToInherit: String |
---|
| 148 | // Comma separated parameters to inherit from the parent. |
---|
| 149 | paramsToInherit: "transition,icon", |
---|
| 150 | |
---|
| 151 | // _selStartMethod: String |
---|
| 152 | // Specifies how the item enters the selected state. |
---|
| 153 | // |
---|
| 154 | // - "touch": Use touch events to enter the selected state. |
---|
| 155 | // - "none": Do not change the selected state. |
---|
| 156 | _selStartMethod: "none", // touch or none |
---|
| 157 | |
---|
| 158 | // _selEndMethod: String |
---|
| 159 | // Specifies how the item leaves the selected state. |
---|
| 160 | // |
---|
| 161 | // - "touch": Use touch events to leave the selected state. |
---|
| 162 | // - "timer": Use setTimeout to leave the selected state. |
---|
| 163 | // - "none": Do not change the selected state. |
---|
| 164 | _selEndMethod: "none", // touch, timer, or none |
---|
| 165 | |
---|
| 166 | // _delayedSelection: Boolean |
---|
| 167 | // If true, selection is delayed 100ms and canceled if dragged in |
---|
| 168 | // order to avoid selection when flick operation is performed. |
---|
| 169 | _delayedSelection: false, |
---|
| 170 | |
---|
| 171 | // _duration: Number |
---|
| 172 | // Duration of selection, milliseconds. |
---|
| 173 | _duration: 800, |
---|
| 174 | |
---|
| 175 | // _handleClick: Boolean |
---|
| 176 | // If true, this widget listens to touch events. |
---|
| 177 | _handleClick: true, |
---|
| 178 | |
---|
| 179 | buildRendering: function(){ |
---|
| 180 | this.inherited(arguments); |
---|
| 181 | this._isOnLine = this.inheritParams(); |
---|
| 182 | }, |
---|
| 183 | |
---|
| 184 | startup: function(){ |
---|
| 185 | if(this._started){ return; } |
---|
| 186 | if(!this._isOnLine){ |
---|
| 187 | this.inheritParams(); |
---|
| 188 | } |
---|
| 189 | this._updateHandles(); |
---|
| 190 | this.inherited(arguments); |
---|
| 191 | }, |
---|
| 192 | |
---|
| 193 | inheritParams: function(){ |
---|
| 194 | // summary: |
---|
| 195 | // Copies from the parent the values of parameters specified |
---|
| 196 | // by the property paramsToInherit. |
---|
| 197 | var parent = this.getParent(); |
---|
| 198 | if(parent){ |
---|
| 199 | array.forEach(this.paramsToInherit.split(/,/), function(p){ |
---|
| 200 | if(p.match(/icon/i)){ |
---|
| 201 | var base = p + "Base", pos = p + "Pos"; |
---|
| 202 | if(this[p] && parent[base] && |
---|
| 203 | parent[base].charAt(parent[base].length - 1) === '/'){ |
---|
| 204 | this[p] = parent[base] + this[p]; |
---|
| 205 | } |
---|
| 206 | if(!this[p]){ this[p] = parent[base]; } |
---|
| 207 | if(!this[pos]){ this[pos] = parent[pos]; } |
---|
| 208 | } |
---|
| 209 | if(!this[p]){ this[p] = parent[p]; } |
---|
| 210 | }, this); |
---|
| 211 | } |
---|
| 212 | return !!parent; |
---|
| 213 | }, |
---|
| 214 | |
---|
| 215 | _updateHandles: function(){ |
---|
| 216 | // tags: |
---|
| 217 | // private |
---|
| 218 | if(this._handleClick && this._selStartMethod === "touch"){ |
---|
| 219 | if(!this._onTouchStartHandle){ |
---|
| 220 | this._onTouchStartHandle = this.connect(this.domNode, touch.press, "_onTouchStart"); |
---|
| 221 | } |
---|
| 222 | }else{ |
---|
| 223 | if(this._onTouchStartHandle){ |
---|
| 224 | this.disconnect(this._onTouchStartHandle); |
---|
| 225 | this._onTouchStartHandle = null; |
---|
| 226 | } |
---|
| 227 | } |
---|
| 228 | }, |
---|
| 229 | |
---|
| 230 | getTransOpts: function(){ |
---|
| 231 | // summary: |
---|
| 232 | // Copies from the parent and returns the values of parameters |
---|
| 233 | // specified by the property paramsToInherit. |
---|
| 234 | var opts = this.transitionOptions || {}; |
---|
| 235 | array.forEach(["moveTo", "href", "hrefTarget", "url", "target", |
---|
| 236 | "urlTarget", "scene", "transition", "transitionDir"], function(p){ |
---|
| 237 | opts[p] = opts[p] || this[p]; |
---|
| 238 | }, this); |
---|
| 239 | return opts; // Object |
---|
| 240 | }, |
---|
| 241 | |
---|
| 242 | userClickAction: function(/*Event*/ /*===== e =====*/){ |
---|
| 243 | // summary: |
---|
| 244 | // User-defined click action. |
---|
| 245 | }, |
---|
| 246 | |
---|
| 247 | defaultClickAction: function(/*Event*/e){ |
---|
| 248 | // summary: |
---|
| 249 | // The default action of this item. |
---|
| 250 | this.handleSelection(e); |
---|
| 251 | if(this.userClickAction(e) === false){ return; } // user's click action |
---|
| 252 | this.makeTransition(e); |
---|
| 253 | }, |
---|
| 254 | |
---|
| 255 | handleSelection: function(/*Event*/e){ |
---|
| 256 | // summary: |
---|
| 257 | // Handles this items selection state. |
---|
| 258 | |
---|
| 259 | // Before transitioning, we want the visual effect of selecting the item. |
---|
| 260 | // To ensure this effect happens even if _delayedSelection is true: |
---|
| 261 | if(this._delayedSelection){ |
---|
| 262 | this.set("selected", true); |
---|
| 263 | } // the item will be deselected after transition. |
---|
| 264 | |
---|
| 265 | if(this._onTouchEndHandle){ |
---|
| 266 | this.disconnect(this._onTouchEndHandle); |
---|
| 267 | this._onTouchEndHandle = null; |
---|
| 268 | } |
---|
| 269 | |
---|
| 270 | var p = this.getParent(); |
---|
| 271 | if(this.toggle){ |
---|
| 272 | this.set("selected", !this._currentSel); |
---|
| 273 | }else if(p && p.selectOne){ |
---|
| 274 | this.set("selected", true); |
---|
| 275 | }else{ |
---|
| 276 | if(this._selEndMethod === "touch"){ |
---|
| 277 | this.set("selected", false); |
---|
| 278 | }else if(this._selEndMethod === "timer"){ |
---|
| 279 | this.defer(function(){ |
---|
| 280 | this.set("selected", false); |
---|
| 281 | }, this._duration); |
---|
| 282 | } |
---|
| 283 | } |
---|
| 284 | }, |
---|
| 285 | |
---|
| 286 | makeTransition: function(/*Event*/e){ |
---|
| 287 | // summary: |
---|
| 288 | // Makes a transition. |
---|
| 289 | if(this.back && history){ |
---|
| 290 | history.back(); |
---|
| 291 | return; |
---|
| 292 | } |
---|
| 293 | if (this.href && this.hrefTarget && this.hrefTarget != "_self") { |
---|
| 294 | win.global.open(this.href, this.hrefTarget || "_blank"); |
---|
| 295 | this._onNewWindowOpened(e); |
---|
| 296 | return; |
---|
| 297 | } |
---|
| 298 | var opts = this.getTransOpts(); |
---|
| 299 | var doTransition = |
---|
| 300 | !!(opts.moveTo || opts.href || opts.url || opts.target || opts.scene); |
---|
| 301 | if(this._prepareForTransition(e, doTransition ? opts : null) === false){ return; } |
---|
| 302 | if(doTransition){ |
---|
| 303 | this.setTransitionPos(e); |
---|
| 304 | new TransitionEvent(this.domNode, opts, e).dispatch(); |
---|
| 305 | } |
---|
| 306 | }, |
---|
| 307 | |
---|
| 308 | _onNewWindowOpened: function(/*Event*/ /*===== e =====*/){ |
---|
| 309 | // summary: |
---|
| 310 | // Subclasses may want to implement it. |
---|
| 311 | }, |
---|
| 312 | |
---|
| 313 | _prepareForTransition: function(/*Event*/e, /*Object*/transOpts){ |
---|
| 314 | // summary: |
---|
| 315 | // Subclasses may want to implement it. |
---|
| 316 | }, |
---|
| 317 | |
---|
| 318 | _onTouchStart: function(e){ |
---|
| 319 | // tags: |
---|
| 320 | // private |
---|
| 321 | if(this.getParent().isEditing || this.onTouchStart(e) === false){ return; } // user's touchStart action |
---|
| 322 | if(!this._onTouchEndHandle && this._selStartMethod === "touch"){ |
---|
| 323 | // Connect to the entire window. Otherwise, fail to receive |
---|
| 324 | // events if operation is performed outside this widget. |
---|
| 325 | // Expose both connect handlers in case the user has interest. |
---|
| 326 | this._onTouchMoveHandle = this.connect(win.body(), touch.move, "_onTouchMove"); |
---|
| 327 | this._onTouchEndHandle = this.connect(win.body(), touch.release, "_onTouchEnd"); |
---|
| 328 | } |
---|
| 329 | this.touchStartX = e.touches ? e.touches[0].pageX : e.clientX; |
---|
| 330 | this.touchStartY = e.touches ? e.touches[0].pageY : e.clientY; |
---|
| 331 | this._currentSel = this.selected; |
---|
| 332 | |
---|
| 333 | if(this._delayedSelection){ |
---|
| 334 | // so as not to make selection when the user flicks on ScrollableView |
---|
| 335 | this._selTimer = this.defer(function(){ |
---|
| 336 | this.set("selected", true); |
---|
| 337 | }, 100); |
---|
| 338 | }else{ |
---|
| 339 | this.set("selected", true); |
---|
| 340 | } |
---|
| 341 | }, |
---|
| 342 | |
---|
| 343 | onTouchStart: function(/*Event*/ /*===== e =====*/){ |
---|
| 344 | // summary: |
---|
| 345 | // User-defined function to handle touchStart events. |
---|
| 346 | // tags: |
---|
| 347 | // callback |
---|
| 348 | }, |
---|
| 349 | |
---|
| 350 | _onTouchMove: function(e){ |
---|
| 351 | // tags: |
---|
| 352 | // private |
---|
| 353 | var x = e.touches ? e.touches[0].pageX : e.clientX; |
---|
| 354 | var y = e.touches ? e.touches[0].pageY : e.clientY; |
---|
| 355 | if(Math.abs(x - this.touchStartX) >= 4 || |
---|
| 356 | Math.abs(y - this.touchStartY) >= 4){ // dojox/mobile/scrollable.threshold |
---|
| 357 | this.cancel(); |
---|
| 358 | var p = this.getParent(); |
---|
| 359 | if(p && p.selectOne){ |
---|
| 360 | this._prevSel && this._prevSel.set("selected", true); |
---|
| 361 | }else{ |
---|
| 362 | this.set("selected", false); |
---|
| 363 | } |
---|
| 364 | } |
---|
| 365 | }, |
---|
| 366 | |
---|
| 367 | _disconnect: function(){ |
---|
| 368 | // tags: |
---|
| 369 | // private |
---|
| 370 | this.disconnect(this._onTouchMoveHandle); |
---|
| 371 | this.disconnect(this._onTouchEndHandle); |
---|
| 372 | this._onTouchMoveHandle = this._onTouchEndHandle = null; |
---|
| 373 | }, |
---|
| 374 | |
---|
| 375 | cancel: function(){ |
---|
| 376 | // summary: |
---|
| 377 | // Cancels an ongoing selection (if any). |
---|
| 378 | if(this._selTimer){ |
---|
| 379 | this._selTimer.remove(); |
---|
| 380 | this._selTimer = null; |
---|
| 381 | } |
---|
| 382 | this._disconnect(); |
---|
| 383 | }, |
---|
| 384 | |
---|
| 385 | _onTouchEnd: function(e){ |
---|
| 386 | // tags: |
---|
| 387 | // private |
---|
| 388 | if(!this._selTimer && this._delayedSelection){ return; } |
---|
| 389 | this.cancel(); |
---|
| 390 | this._onClick(e); |
---|
| 391 | }, |
---|
| 392 | |
---|
| 393 | setTransitionPos: function(e){ |
---|
| 394 | // summary: |
---|
| 395 | // Stores the clicked position for later use. |
---|
| 396 | // description: |
---|
| 397 | // Some of the transition animations (e.g. ScaleIn) need the |
---|
| 398 | // clicked position. |
---|
| 399 | var w = this; |
---|
| 400 | while(true){ |
---|
| 401 | w = w.getParent(); |
---|
| 402 | if(!w || domClass.contains(w.domNode, "mblView")){ break; } |
---|
| 403 | } |
---|
| 404 | if(w){ |
---|
| 405 | w.clickedPosX = e.clientX; |
---|
| 406 | w.clickedPosY = e.clientY; |
---|
| 407 | } |
---|
| 408 | }, |
---|
| 409 | |
---|
| 410 | transitionTo: function(/*String|Object*/moveTo, /*String*/href, /*String*/url, /*String*/scene){ |
---|
| 411 | // summary: |
---|
| 412 | // Performs a view transition. |
---|
| 413 | // description: |
---|
| 414 | // Given a transition destination, this method performs a view |
---|
| 415 | // transition. This method is typically called when this item |
---|
| 416 | // is clicked. |
---|
| 417 | var opts = (moveTo && typeof(moveTo) === "object") ? moveTo : |
---|
| 418 | {moveTo: moveTo, href: href, url: url, scene: scene, |
---|
| 419 | transition: this.transition, transitionDir: this.transitionDir}; |
---|
| 420 | new TransitionEvent(this.domNode, opts).dispatch(); |
---|
| 421 | }, |
---|
| 422 | |
---|
| 423 | _setIconAttr: function(icon){ |
---|
| 424 | // tags: |
---|
| 425 | // private |
---|
| 426 | if(!this._isOnLine){ |
---|
| 427 | // record the value to be able to reapply it (see the code in the startup method) |
---|
| 428 | this._pendingIcon = icon; |
---|
| 429 | return; |
---|
| 430 | } // icon may be invalid because inheritParams is not called yet |
---|
| 431 | this._set("icon", icon); |
---|
| 432 | this.iconNode = iconUtils.setIcon(icon, this.iconPos, this.iconNode, this.alt, this.iconParentNode, this.refNode, this.position); |
---|
| 433 | }, |
---|
| 434 | |
---|
| 435 | _setLabelAttr: function(/*String*/text){ |
---|
| 436 | // tags: |
---|
| 437 | // private |
---|
| 438 | this._set("label", text); |
---|
| 439 | this.labelNode.innerHTML = this._cv ? this._cv(text) : text; |
---|
| 440 | }, |
---|
| 441 | |
---|
| 442 | _setSelectedAttr: function(/*Boolean*/selected){ |
---|
| 443 | // summary: |
---|
| 444 | // Makes this widget in the selected or unselected state. |
---|
| 445 | // description: |
---|
| 446 | // Subclass should override. |
---|
| 447 | // tags: |
---|
| 448 | // private |
---|
| 449 | if(selected){ |
---|
| 450 | var p = this.getParent(); |
---|
| 451 | if(p && p.selectOne){ |
---|
| 452 | // deselect the currently selected item |
---|
| 453 | var arr = array.filter(p.getChildren(), function(w){ |
---|
| 454 | return w.selected; |
---|
| 455 | }); |
---|
| 456 | array.forEach(arr, function(c){ |
---|
| 457 | this._prevSel = c; |
---|
| 458 | c.set("selected", false); |
---|
| 459 | }, this); |
---|
| 460 | } |
---|
| 461 | } |
---|
| 462 | this._set("selected", selected); |
---|
| 463 | } |
---|
| 464 | }); |
---|
| 465 | return has("dojo-bidi") ? declare("dojox.mobile._ItemBase", [_ItemBase, BidiItemBase]) : _ItemBase; |
---|
| 466 | }); |
---|