[483] | 1 | define([ |
---|
| 2 | "dojo/_base/declare", |
---|
| 3 | "dojo/_base/event", |
---|
| 4 | "dojo/_base/lang", |
---|
| 5 | "dojo/sniff", |
---|
| 6 | "dojo/_base/window", |
---|
| 7 | "dojo/dom-class", |
---|
| 8 | "dojo/dom-construct", |
---|
| 9 | "dojo/dom-geometry", |
---|
| 10 | "dojo/dom-style", |
---|
| 11 | "./_ItemBase", |
---|
| 12 | "./Badge", |
---|
| 13 | "./TransitionEvent", |
---|
| 14 | "./iconUtils", |
---|
| 15 | "./lazyLoadUtils", |
---|
| 16 | "./viewRegistry", |
---|
| 17 | "./_css3", |
---|
| 18 | "dojo/has!dojo-bidi?dojox/mobile/bidi/IconItem" |
---|
| 19 | ], function(declare, event, lang, has, win, domClass, domConstruct, domGeometry, domStyle, ItemBase, Badge, TransitionEvent, iconUtils, lazyLoadUtils, viewRegistry, css3, BidiIconItem){ |
---|
| 20 | |
---|
| 21 | // module: |
---|
| 22 | // dojox/mobile/IconItem |
---|
| 23 | |
---|
| 24 | var IconItem = declare(has("dojo-bidi") ? "dojox.mobile.NonBidiIconItem" : "dojox.mobile.IconItem", ItemBase, { |
---|
| 25 | // summary: |
---|
| 26 | // An icon item widget. |
---|
| 27 | // description: |
---|
| 28 | // IconItem represents an item that has an application component |
---|
| 29 | // and its icon image. You can tap the icon to open the |
---|
| 30 | // corresponding application component. You can also use the icon |
---|
| 31 | // to move to a different view by specifying either of the moveTo, |
---|
| 32 | // href or url parameters. |
---|
| 33 | |
---|
| 34 | // lazy: String |
---|
| 35 | // If true, the content of the widget, which includes dojo markup, |
---|
| 36 | // is instantiated lazily. That is, only when the widget is opened |
---|
| 37 | // by the user, the required modules are loaded and the content |
---|
| 38 | // widgets are instantiated. |
---|
| 39 | // This option works both in the sync and async loader mode. |
---|
| 40 | lazy: false, |
---|
| 41 | |
---|
| 42 | // requires: String |
---|
| 43 | // Comma-separated required module names to be lazily loaded. This |
---|
| 44 | // property is effective only when lazy=true. All the modules |
---|
| 45 | // specified with data-dojo-type and their depending modules are |
---|
| 46 | // automatically loaded by the IconItem when it is opened. |
---|
| 47 | // However, if you need other extra modules to be loaded, use this parameter. |
---|
| 48 | // This option works both in the sync and async loader mode. |
---|
| 49 | requires: "", |
---|
| 50 | |
---|
| 51 | // timeout: String |
---|
| 52 | // Duration of highlight in seconds. |
---|
| 53 | timeout: 10, |
---|
| 54 | |
---|
| 55 | // content: String |
---|
| 56 | // An HTML fragment to embed as icon content. |
---|
| 57 | content: "", |
---|
| 58 | |
---|
| 59 | // badge: String |
---|
| 60 | // A text to show in a badge (ex. "55"). |
---|
| 61 | badge: "", |
---|
| 62 | |
---|
| 63 | // badgeClass: String |
---|
| 64 | // A class name of a DOM button for a badge. |
---|
| 65 | badgeClass: "mblDomButtonRedBadge", |
---|
| 66 | |
---|
| 67 | // deletable: Boolean |
---|
| 68 | // If true, you can delete this IconItem by clicking on the delete |
---|
| 69 | // icon during edit mode. |
---|
| 70 | // If false, the delete icon is not displayed during edit mode so |
---|
| 71 | // that it cannot be deleted. |
---|
| 72 | deletable: true, |
---|
| 73 | |
---|
| 74 | // deleteIcon: String |
---|
| 75 | // A delete icon to display at the top-left corner of the item |
---|
| 76 | // during edit mode. The value can be either a path for an image |
---|
| 77 | // file or a class name of a DOM button. |
---|
| 78 | deleteIcon: "", |
---|
| 79 | |
---|
| 80 | // tag: String |
---|
| 81 | // A name of the HTML tag to create as domNode. |
---|
| 82 | tag: "li", |
---|
| 83 | |
---|
| 84 | /* internal properties */ |
---|
| 85 | // Note these are overrides for similar properties defined in _ItemBase. |
---|
| 86 | paramsToInherit: "transition,icon,deleteIcon,badgeClass,deleteIconTitle,deleteIconRole", |
---|
| 87 | baseClass: "mblIconItem", |
---|
| 88 | _selStartMethod: "touch", |
---|
| 89 | _selEndMethod: "none", |
---|
| 90 | |
---|
| 91 | destroy: function(){ |
---|
| 92 | if(this.badgeObj){ |
---|
| 93 | delete this.badgeObj; |
---|
| 94 | } |
---|
| 95 | this.inherited(arguments); |
---|
| 96 | }, |
---|
| 97 | |
---|
| 98 | buildRendering: function(){ |
---|
| 99 | this.domNode = this.srcNodeRef || domConstruct.create(this.tag); |
---|
| 100 | |
---|
| 101 | if(this.srcNodeRef){ |
---|
| 102 | // reparent |
---|
| 103 | this._tmpNode = domConstruct.create("div"); |
---|
| 104 | for(var i = 0, len = this.srcNodeRef.childNodes.length; i < len; i++){ |
---|
| 105 | this._tmpNode.appendChild(this.srcNodeRef.firstChild); |
---|
| 106 | } |
---|
| 107 | } |
---|
| 108 | |
---|
| 109 | this.iconDivNode = domConstruct.create("div", {className:"mblIconArea"}, this.domNode); |
---|
| 110 | this.iconParentNode = domConstruct.create("div", {className:"mblIconAreaInner"}, this.iconDivNode); |
---|
| 111 | this.labelNode = domConstruct.create("span", {className:"mblIconAreaTitle"}, this.iconDivNode); |
---|
| 112 | |
---|
| 113 | this.inherited(arguments); |
---|
| 114 | }, |
---|
| 115 | |
---|
| 116 | startup: function(){ |
---|
| 117 | if(this._started){ return; } |
---|
| 118 | |
---|
| 119 | var p = this.getParent(); |
---|
| 120 | require([p.iconItemPaneClass], lang.hitch(this, function(module){ |
---|
| 121 | var w = this.paneWidget = new module(p.iconItemPaneProps); |
---|
| 122 | this.containerNode = w.containerNode; |
---|
| 123 | if(this._tmpNode){ |
---|
| 124 | // reparent |
---|
| 125 | for(var i = 0, len = this._tmpNode.childNodes.length; i < len; i++){ |
---|
| 126 | w.containerNode.appendChild(this._tmpNode.firstChild); |
---|
| 127 | } |
---|
| 128 | this._tmpNode = null; |
---|
| 129 | } |
---|
| 130 | p.paneContainerWidget.addChild(w, this.getIndexInParent()); |
---|
| 131 | w.set("label", this.label); |
---|
| 132 | this._clickCloseHandle = this.connect(w.closeIconNode, "onclick", "_closeIconClicked"); |
---|
| 133 | this._keydownCloseHandle = this.connect(w.closeIconNode, "onkeydown", "_closeIconClicked"); // for desktop browsers |
---|
| 134 | })); |
---|
| 135 | |
---|
| 136 | this.inherited(arguments); |
---|
| 137 | if(!this._isOnLine){ |
---|
| 138 | this._isOnLine = true; |
---|
| 139 | // retry applying the attribute for which the custom setter delays the actual |
---|
| 140 | // work until _isOnLine is true. |
---|
| 141 | this.set("icon", this._pendingIcon !== undefined ? this._pendingIcon : this.icon); |
---|
| 142 | // Not needed anymore (this code executes only once per life cycle): |
---|
| 143 | delete this._pendingIcon; |
---|
| 144 | } |
---|
| 145 | if(!this.icon && p.defaultIcon){ |
---|
| 146 | this.set("icon", p.defaultIcon); |
---|
| 147 | } |
---|
| 148 | |
---|
| 149 | this._dragstartHandle = this.connect(this.domNode, "ondragstart", event.stop); |
---|
| 150 | this.connect(this.domNode, "onkeydown", "_onClick"); // for desktop browsers |
---|
| 151 | }, |
---|
| 152 | |
---|
| 153 | highlight: function(/*Number?*/timeout){ |
---|
| 154 | // summary: |
---|
| 155 | // Shakes the icon 10 seconds. |
---|
| 156 | domClass.add(this.iconDivNode, "mblVibrate"); |
---|
| 157 | timeout = (timeout !== undefined) ? timeout : this.timeout; |
---|
| 158 | if(timeout > 0){ |
---|
| 159 | var _this = this; |
---|
| 160 | _this.defer(function(){ |
---|
| 161 | _this.unhighlight(); |
---|
| 162 | }, timeout*1000); |
---|
| 163 | } |
---|
| 164 | }, |
---|
| 165 | |
---|
| 166 | unhighlight: function(){ |
---|
| 167 | // summary: |
---|
| 168 | // Stops shaking the icon. |
---|
| 169 | if(!has("ie") && has("trident") === 7){ |
---|
| 170 | // Workaround on IE11: if just removing the style, the icon continues to shake |
---|
| 171 | domStyle.set(this.iconDivNode, "animation-name", ""); |
---|
| 172 | } |
---|
| 173 | domClass.remove(this.iconDivNode, "mblVibrate"); |
---|
| 174 | }, |
---|
| 175 | |
---|
| 176 | isOpen: function(e){ |
---|
| 177 | // summary: |
---|
| 178 | // Returns true if the icon is open. |
---|
| 179 | return this.paneWidget.isOpen(); |
---|
| 180 | }, |
---|
| 181 | |
---|
| 182 | _onClick: function(e){ |
---|
| 183 | // summary: |
---|
| 184 | // Internal handler for click events. |
---|
| 185 | // tags: |
---|
| 186 | // private |
---|
| 187 | if(this.getParent().isEditing || e && e.type === "keydown" && e.keyCode !== 13){ return; } |
---|
| 188 | if(this.onClick(e) === false){ return; } // user's click action |
---|
| 189 | this.defaultClickAction(e); |
---|
| 190 | }, |
---|
| 191 | |
---|
| 192 | onClick: function(/*Event*/ /*===== e =====*/){ |
---|
| 193 | // summary: |
---|
| 194 | // User-defined function to handle clicks. |
---|
| 195 | // tags: |
---|
| 196 | // callback |
---|
| 197 | }, |
---|
| 198 | |
---|
| 199 | _onNewWindowOpened: function(e){ |
---|
| 200 | // Override from _ItemBase |
---|
| 201 | this.set("selected", false); |
---|
| 202 | }, |
---|
| 203 | |
---|
| 204 | _prepareForTransition: function(e, transOpts){ |
---|
| 205 | // Override from _ItemBase |
---|
| 206 | if(transOpts){ |
---|
| 207 | this.defer(function(d){ |
---|
| 208 | this.set("selected", false); |
---|
| 209 | }, 1500); |
---|
| 210 | return true; |
---|
| 211 | }else{ |
---|
| 212 | if(this.getParent().transition === "below" && this.isOpen()){ |
---|
| 213 | this.close(); |
---|
| 214 | }else{ |
---|
| 215 | this.open(e); |
---|
| 216 | } |
---|
| 217 | return false; |
---|
| 218 | } |
---|
| 219 | }, |
---|
| 220 | |
---|
| 221 | _closeIconClicked: function(e){ |
---|
| 222 | // summary: |
---|
| 223 | // Internal handler for click events. |
---|
| 224 | // tags: |
---|
| 225 | // private |
---|
| 226 | if(e){ |
---|
| 227 | if(e.type === "keydown" && e.keyCode !== 13){ return; } |
---|
| 228 | if(this.closeIconClicked(e) === false){ return; } // user's click action |
---|
| 229 | this.defer(function(d){ this._closeIconClicked(); }); |
---|
| 230 | return; |
---|
| 231 | } |
---|
| 232 | this.close(); |
---|
| 233 | }, |
---|
| 234 | |
---|
| 235 | closeIconClicked: function(/*Event*/ /*===== e =====*/){ |
---|
| 236 | // summary: |
---|
| 237 | // User-defined function to handle clicks for the close icon. |
---|
| 238 | // tags: |
---|
| 239 | // callback |
---|
| 240 | }, |
---|
| 241 | |
---|
| 242 | open: function(e){ |
---|
| 243 | // summary: |
---|
| 244 | // Opens the icon content, or makes a transition. |
---|
| 245 | var parent = this.getParent(); // IconContainer |
---|
| 246 | if(this.transition === "below"){ |
---|
| 247 | if(parent.single){ |
---|
| 248 | parent.closeAll(); |
---|
| 249 | } |
---|
| 250 | this._open_1(); |
---|
| 251 | }else{ |
---|
| 252 | parent._opening = this; |
---|
| 253 | if(parent.single){ |
---|
| 254 | this.paneWidget.closeHeaderNode.style.display = "none"; |
---|
| 255 | if(!this.isOpen()){ |
---|
| 256 | parent.closeAll(); |
---|
| 257 | } |
---|
| 258 | parent.appView._heading.set("label", this.label); |
---|
| 259 | } |
---|
| 260 | this.moveTo = parent.id + "_mblApplView"; |
---|
| 261 | new TransitionEvent(this.domNode, this.getTransOpts(), e).dispatch(); |
---|
| 262 | } |
---|
| 263 | }, |
---|
| 264 | |
---|
| 265 | _open_1: function(){ |
---|
| 266 | // summary: |
---|
| 267 | // Opens the icon content for the 'below' transition. |
---|
| 268 | // tags: |
---|
| 269 | // private |
---|
| 270 | this.paneWidget.show(); |
---|
| 271 | this.unhighlight(); |
---|
| 272 | if(this.lazy){ |
---|
| 273 | lazyLoadUtils.instantiateLazyWidgets(this.containerNode, this.requires); |
---|
| 274 | this.lazy = false; |
---|
| 275 | } |
---|
| 276 | this.scrollIntoView(this.paneWidget.domNode); |
---|
| 277 | this.onOpen(); |
---|
| 278 | }, |
---|
| 279 | |
---|
| 280 | scrollIntoView: function(/*DomNode*/node){ |
---|
| 281 | // summary: |
---|
| 282 | // Scrolls until the given node is in the view. |
---|
| 283 | var s = viewRegistry.getEnclosingScrollable(node); |
---|
| 284 | if(s){ // this node is placed inside scrollable |
---|
| 285 | var dim = s.getDim(); |
---|
| 286 | if(dim.c.h >= dim.d.h){ // #16306: only if the content is larger than the display area |
---|
| 287 | s.scrollIntoView(node, true); |
---|
| 288 | } |
---|
| 289 | }else{ |
---|
| 290 | win.global.scrollBy(0, domGeometry.position(node, false).y); |
---|
| 291 | } |
---|
| 292 | }, |
---|
| 293 | |
---|
| 294 | close: function(/*Boolean?*/noAnimation){ |
---|
| 295 | // summary: |
---|
| 296 | // Closes the icon content. |
---|
| 297 | if(!this.isOpen()){ return; } |
---|
| 298 | this.set("selected", false); |
---|
| 299 | if(has("css3-animations") && !noAnimation){ |
---|
| 300 | var contentNode = this.paneWidget.domNode; |
---|
| 301 | if(this.getParent().transition == "below"){ |
---|
| 302 | domClass.add(contentNode, "mblCloseContent mblShrink"); |
---|
| 303 | var nodePos = domGeometry.position(contentNode, true); |
---|
| 304 | var targetPos = domGeometry.position(this.domNode, true); |
---|
| 305 | var origin = (targetPos.x + targetPos.w/2 - nodePos.x) + "px " + (targetPos.y + targetPos.h/2 - nodePos.y) + "px"; |
---|
| 306 | domStyle.set(contentNode, css3.add({}, { transformOrigin:origin })); |
---|
| 307 | }else{ |
---|
| 308 | domClass.add(contentNode, "mblCloseContent mblShrink0"); |
---|
| 309 | } |
---|
| 310 | }else{ |
---|
| 311 | this.paneWidget.hide(); |
---|
| 312 | } |
---|
| 313 | this.onClose(); |
---|
| 314 | }, |
---|
| 315 | |
---|
| 316 | onOpen: function(){ |
---|
| 317 | // summary: |
---|
| 318 | // Stub method to allow the application to connect. |
---|
| 319 | }, |
---|
| 320 | |
---|
| 321 | onClose: function(){ |
---|
| 322 | // summary: |
---|
| 323 | // Stub method to allow the application to connect. |
---|
| 324 | }, |
---|
| 325 | |
---|
| 326 | _setLabelAttr: function(/*String*/text){ |
---|
| 327 | // tags: |
---|
| 328 | // private |
---|
| 329 | this.label = text; |
---|
| 330 | var s = this._cv ? this._cv(text) : text; |
---|
| 331 | this.labelNode.innerHTML = s; |
---|
| 332 | if(this.paneWidget){ |
---|
| 333 | this.paneWidget.set("label", text); |
---|
| 334 | } |
---|
| 335 | }, |
---|
| 336 | |
---|
| 337 | _getBadgeAttr: function(){ |
---|
| 338 | // tags: |
---|
| 339 | // private |
---|
| 340 | return this.badgeObj ? this.badgeObj.getValue() : null; |
---|
| 341 | }, |
---|
| 342 | |
---|
| 343 | _setBadgeAttr: function(/*String*/value){ |
---|
| 344 | // tags: |
---|
| 345 | // private |
---|
| 346 | if(!this.badgeObj){ |
---|
| 347 | this.badgeObj = new Badge({fontSize:14, className:this.badgeClass}); |
---|
| 348 | domStyle.set(this.badgeObj.domNode, { |
---|
| 349 | position: "absolute", |
---|
| 350 | top: "-2px", |
---|
| 351 | right: "2px" |
---|
| 352 | }); |
---|
| 353 | } |
---|
| 354 | this.badgeObj.setValue(value); |
---|
| 355 | if(value){ |
---|
| 356 | this.iconDivNode.appendChild(this.badgeObj.domNode); |
---|
| 357 | }else{ |
---|
| 358 | this.iconDivNode.removeChild(this.badgeObj.domNode); |
---|
| 359 | } |
---|
| 360 | }, |
---|
| 361 | |
---|
| 362 | _setDeleteIconAttr: function(icon){ |
---|
| 363 | // tags: |
---|
| 364 | // private |
---|
| 365 | if(!this.getParent()){ return; } // icon may be invalid because inheritParams is not called yet |
---|
| 366 | |
---|
| 367 | this._set("deleteIcon", icon); |
---|
| 368 | icon = this.deletable ? icon : ""; |
---|
| 369 | this.deleteIconNode = iconUtils.setIcon(icon, this.deleteIconPos, this.deleteIconNode, |
---|
| 370 | this.deleteIconTitle || this.alt, this.iconDivNode); |
---|
| 371 | if(this.deleteIconNode){ |
---|
| 372 | domClass.add(this.deleteIconNode, "mblIconItemDeleteIcon"); |
---|
| 373 | if(this.deleteIconRole){ |
---|
| 374 | this.deleteIconNode.setAttribute("role", this.deleteIconRole); |
---|
| 375 | } |
---|
| 376 | } |
---|
| 377 | }, |
---|
| 378 | |
---|
| 379 | _setContentAttr: function(/*String|DomNode*/data){ |
---|
| 380 | // tags: |
---|
| 381 | // private |
---|
| 382 | var root; |
---|
| 383 | if(!this.paneWidget){ |
---|
| 384 | if(!this._tmpNode){ |
---|
| 385 | this._tmpNode = domConstruct.create("div"); |
---|
| 386 | } |
---|
| 387 | root = this._tmpNode; |
---|
| 388 | }else{ |
---|
| 389 | root = this.paneWidget.containerNode; |
---|
| 390 | } |
---|
| 391 | |
---|
| 392 | if(typeof data === "object"){ |
---|
| 393 | domConstruct.empty(root); |
---|
| 394 | root.appendChild(data); |
---|
| 395 | }else{ |
---|
| 396 | root.innerHTML = data; |
---|
| 397 | } |
---|
| 398 | }, |
---|
| 399 | |
---|
| 400 | _setSelectedAttr: function(/*Boolean*/selected){ |
---|
| 401 | // summary: |
---|
| 402 | // Makes this widget in the selected or unselected state. |
---|
| 403 | // tags: |
---|
| 404 | // private |
---|
| 405 | this.inherited(arguments); |
---|
| 406 | domStyle.set(this.iconNode, "opacity", |
---|
| 407 | selected ? this.getParent().pressedIconOpacity : 1); |
---|
| 408 | } |
---|
| 409 | }); |
---|
| 410 | |
---|
| 411 | return has("dojo-bidi") ? declare("dojox.mobile.IconItem", [IconItem, BidiIconItem]) : IconItem; |
---|
| 412 | }); |
---|