[483] | 1 | dojo.provide("dojox.mobile.app.ImageThumbView"); |
---|
| 2 | dojo.experimental("dojox.mobile.app.ImageThumbView"); |
---|
| 3 | |
---|
| 4 | dojo.require("dijit._WidgetBase"); |
---|
| 5 | dojo.require("dojo.string"); |
---|
| 6 | |
---|
| 7 | dojo.declare("dojox.mobile.app.ImageThumbView", dijit._WidgetBase, { |
---|
| 8 | // summary: |
---|
| 9 | // An image thumbnail gallery |
---|
| 10 | |
---|
| 11 | // items: Array |
---|
| 12 | // The data items from which the image urls are retrieved. |
---|
| 13 | // If an item is a string, it is expected to be a URL. Otherwise |
---|
| 14 | // by default it is expected to have a 'url' member. This can |
---|
| 15 | // be configured using the 'urlParam' attribute on this widget. |
---|
| 16 | items: [], |
---|
| 17 | |
---|
| 18 | // urlParam: String |
---|
| 19 | // The paramter name used to retrieve an image url from a JSON object |
---|
| 20 | urlParam: "url", |
---|
| 21 | |
---|
| 22 | labelParam: null, |
---|
| 23 | |
---|
| 24 | itemTemplate: '<div class="mblThumbInner">' + |
---|
| 25 | '<div class="mblThumbOverlay"></div>' + |
---|
| 26 | '<div class="mblThumbMask">' + |
---|
| 27 | '<div class="mblThumbSrc" style="background-image:url(${url})"></div>' + |
---|
| 28 | '</div>' + |
---|
| 29 | '</div>', |
---|
| 30 | |
---|
| 31 | minPadding: 4, |
---|
| 32 | |
---|
| 33 | maxPerRow: 3, |
---|
| 34 | |
---|
| 35 | maxRows: -1, |
---|
| 36 | |
---|
| 37 | baseClass: "mblImageThumbView", |
---|
| 38 | |
---|
| 39 | thumbSize: "medium", |
---|
| 40 | |
---|
| 41 | animationEnabled: true, |
---|
| 42 | |
---|
| 43 | selectedIndex: -1, |
---|
| 44 | |
---|
| 45 | cache: null, |
---|
| 46 | |
---|
| 47 | cacheMustMatch: false, |
---|
| 48 | |
---|
| 49 | clickEvent: "onclick", |
---|
| 50 | |
---|
| 51 | cacheBust: false, |
---|
| 52 | |
---|
| 53 | disableHide: false, |
---|
| 54 | |
---|
| 55 | constructor: function(params, node){ |
---|
| 56 | }, |
---|
| 57 | |
---|
| 58 | postCreate: function(){ |
---|
| 59 | |
---|
| 60 | this.inherited(arguments); |
---|
| 61 | var _this = this; |
---|
| 62 | |
---|
| 63 | var hoverCls = "mblThumbHover"; |
---|
| 64 | |
---|
| 65 | this.addThumb = dojo.hitch(this, this.addThumb); |
---|
| 66 | this.handleImgLoad = dojo.hitch(this, this.handleImgLoad); |
---|
| 67 | this.hideCached = dojo.hitch(this, this.hideCached); |
---|
| 68 | |
---|
| 69 | this._onLoadImages = {}; |
---|
| 70 | |
---|
| 71 | this.cache = []; |
---|
| 72 | this.visibleImages = []; |
---|
| 73 | |
---|
| 74 | this._cacheCounter = 0; |
---|
| 75 | |
---|
| 76 | this.connect(this.domNode, this.clickEvent, function(event){ |
---|
| 77 | var itemNode = _this._getItemNodeFromEvent(event); |
---|
| 78 | |
---|
| 79 | if(itemNode && !itemNode._cached){ |
---|
| 80 | _this.onSelect(itemNode._item, itemNode._index, _this.items); |
---|
| 81 | dojo.query(".selected", this.domNode).removeClass("selected"); |
---|
| 82 | dojo.addClass(itemNode, "selected"); |
---|
| 83 | } |
---|
| 84 | }); |
---|
| 85 | |
---|
| 86 | dojo.addClass(this.domNode, this.thumbSize); |
---|
| 87 | |
---|
| 88 | this.resize(); |
---|
| 89 | this.render(); |
---|
| 90 | }, |
---|
| 91 | |
---|
| 92 | onSelect: function(item, index, items){ |
---|
| 93 | // summary: |
---|
| 94 | // Dummy function that is triggered when an image is selected. |
---|
| 95 | }, |
---|
| 96 | |
---|
| 97 | _setAnimationEnabledAttr: function(value){ |
---|
| 98 | this.animationEnabled = value; |
---|
| 99 | dojo[value ? "addClass" : "removeClass"](this.domNode, "animated"); |
---|
| 100 | }, |
---|
| 101 | |
---|
| 102 | _setItemsAttr: function(items){ |
---|
| 103 | this.items = items || []; |
---|
| 104 | |
---|
| 105 | var urls = {}; |
---|
| 106 | var i; |
---|
| 107 | for(i = 0; i < this.items.length; i++){ |
---|
| 108 | urls[this.items[i][this.urlParam]] = 1; |
---|
| 109 | } |
---|
| 110 | |
---|
| 111 | var clearedUrls = []; |
---|
| 112 | for(var url in this._onLoadImages){ |
---|
| 113 | if(!urls[url] && this._onLoadImages[url]._conn){ |
---|
| 114 | dojo.disconnect(this._onLoadImages[url]._conn); |
---|
| 115 | this._onLoadImages[url].src = null; |
---|
| 116 | clearedUrls.push(url); |
---|
| 117 | } |
---|
| 118 | } |
---|
| 119 | |
---|
| 120 | for(i = 0; i < clearedUrls.length; i++){ |
---|
| 121 | delete this._onLoadImages[url]; |
---|
| 122 | } |
---|
| 123 | |
---|
| 124 | this.render(); |
---|
| 125 | }, |
---|
| 126 | |
---|
| 127 | _getItemNode: function(node){ |
---|
| 128 | while(node && !dojo.hasClass(node, "mblThumb") && node != this.domNode){ |
---|
| 129 | node = node.parentNode; |
---|
| 130 | } |
---|
| 131 | |
---|
| 132 | return (node == this.domNode) ? null : node; |
---|
| 133 | }, |
---|
| 134 | |
---|
| 135 | _getItemNodeFromEvent: function(event){ |
---|
| 136 | if(event.touches && event.touches.length > 0){ |
---|
| 137 | event = event.touches[0]; |
---|
| 138 | } |
---|
| 139 | return this._getItemNode(event.target); |
---|
| 140 | }, |
---|
| 141 | |
---|
| 142 | resize: function(){ |
---|
| 143 | this._thumbSize = null; |
---|
| 144 | |
---|
| 145 | this._size = dojo.contentBox(this.domNode); |
---|
| 146 | |
---|
| 147 | this.disableHide = true; |
---|
| 148 | this.render(); |
---|
| 149 | this.disableHide = false; |
---|
| 150 | }, |
---|
| 151 | |
---|
| 152 | hideCached: function(){ |
---|
| 153 | // summary: |
---|
| 154 | // Hides all cached nodes, so that they're no invisible and overlaying |
---|
| 155 | // other screen elements. |
---|
| 156 | for(var i = 0; i < this.cache.length; i++){ |
---|
| 157 | if (this.cache[i]) { |
---|
| 158 | dojo.style(this.cache[i], "display", "none"); |
---|
| 159 | } |
---|
| 160 | } |
---|
| 161 | }, |
---|
| 162 | |
---|
| 163 | render: function(){ |
---|
| 164 | var i; |
---|
| 165 | var url; |
---|
| 166 | var item; |
---|
| 167 | |
---|
| 168 | var thumb; |
---|
| 169 | while(this.visibleImages && this.visibleImages.length > 0){ |
---|
| 170 | thumb = this.visibleImages.pop(); |
---|
| 171 | this.cache.push(thumb); |
---|
| 172 | |
---|
| 173 | if (!this.disableHide) { |
---|
| 174 | dojo.addClass(thumb, "hidden"); |
---|
| 175 | } |
---|
| 176 | thumb._cached = true; |
---|
| 177 | } |
---|
| 178 | |
---|
| 179 | if(this.cache && this.cache.length > 0){ |
---|
| 180 | setTimeout(this.hideCached, 1000); |
---|
| 181 | } |
---|
| 182 | |
---|
| 183 | if(!this.items || this.items.length == 0){ |
---|
| 184 | return; |
---|
| 185 | } |
---|
| 186 | |
---|
| 187 | for(i = 0; i < this.items.length; i++){ |
---|
| 188 | item = this.items[i]; |
---|
| 189 | url = (dojo.isString(item) ? item : item[this.urlParam]); |
---|
| 190 | |
---|
| 191 | this.addThumb(item, url, i); |
---|
| 192 | |
---|
| 193 | if(this.maxRows > 0 && (i + 1) / this.maxPerRow >= this.maxRows){ |
---|
| 194 | break; |
---|
| 195 | } |
---|
| 196 | } |
---|
| 197 | |
---|
| 198 | if(!this._thumbSize){ |
---|
| 199 | return; |
---|
| 200 | } |
---|
| 201 | |
---|
| 202 | var column = 0; |
---|
| 203 | var row = -1; |
---|
| 204 | |
---|
| 205 | var totalThumbWidth = this._thumbSize.w + (this.padding * 2); |
---|
| 206 | var totalThumbHeight = this._thumbSize.h + (this.padding * 2); |
---|
| 207 | |
---|
| 208 | var nodes = this.thumbNodes = |
---|
| 209 | dojo.query(".mblThumb", this.domNode); |
---|
| 210 | |
---|
| 211 | var pos = 0; |
---|
| 212 | nodes = this.visibleImages; |
---|
| 213 | for(i = 0; i < nodes.length; i++){ |
---|
| 214 | if(nodes[i]._cached){ |
---|
| 215 | continue; |
---|
| 216 | } |
---|
| 217 | |
---|
| 218 | if(pos % this.maxPerRow == 0){ |
---|
| 219 | row ++; |
---|
| 220 | } |
---|
| 221 | column = pos % this.maxPerRow; |
---|
| 222 | |
---|
| 223 | this.place( |
---|
| 224 | nodes[i], |
---|
| 225 | (column * totalThumbWidth) + this.padding, // x position |
---|
| 226 | (row * totalThumbHeight) + this.padding // y position |
---|
| 227 | ); |
---|
| 228 | |
---|
| 229 | if(!nodes[i]._loading){ |
---|
| 230 | dojo.removeClass(nodes[i], "hidden"); |
---|
| 231 | } |
---|
| 232 | |
---|
| 233 | if(pos == this.selectedIndex){ |
---|
| 234 | dojo[pos == this.selectedIndex ? "addClass" : "removeClass"] |
---|
| 235 | (nodes[i], "selected"); |
---|
| 236 | } |
---|
| 237 | pos++; |
---|
| 238 | } |
---|
| 239 | |
---|
| 240 | var numRows = Math.ceil(pos / this.maxPerRow); |
---|
| 241 | |
---|
| 242 | this._numRows = numRows; |
---|
| 243 | |
---|
| 244 | this.setContainerHeight((numRows * (this._thumbSize.h + this.padding * 2))); |
---|
| 245 | }, |
---|
| 246 | |
---|
| 247 | setContainerHeight: function(amount){ |
---|
| 248 | dojo.style(this.domNode, "height", amount + "px"); |
---|
| 249 | }, |
---|
| 250 | |
---|
| 251 | addThumb: function(item, url, index){ |
---|
| 252 | |
---|
| 253 | var thumbDiv; |
---|
| 254 | var cacheHit = false; |
---|
| 255 | if(this.cache.length > 0){ |
---|
| 256 | // Reuse a previously created node if possible |
---|
| 257 | var found = false; |
---|
| 258 | // Search for an image with the same url first |
---|
| 259 | for(var i = 0; i < this.cache.length; i++){ |
---|
| 260 | if(this.cache[i]._url == url){ |
---|
| 261 | thumbDiv = this.cache.splice(i, 1)[0]; |
---|
| 262 | found = true; |
---|
| 263 | break |
---|
| 264 | } |
---|
| 265 | } |
---|
| 266 | |
---|
| 267 | // if no image with the same url is found, just take the last one |
---|
| 268 | if(!thumbDiv && !this.cacheMustMatch){ |
---|
| 269 | thumbDiv = this.cache.pop(); |
---|
| 270 | dojo.removeClass(thumbDiv, "selected"); |
---|
| 271 | } else { |
---|
| 272 | cacheHit = true; |
---|
| 273 | } |
---|
| 274 | } |
---|
| 275 | |
---|
| 276 | if(!thumbDiv){ |
---|
| 277 | |
---|
| 278 | // Create a new thumb |
---|
| 279 | thumbDiv = dojo.create("div", { |
---|
| 280 | "class": "mblThumb hidden", |
---|
| 281 | innerHTML: dojo.string.substitute(this.itemTemplate, { |
---|
| 282 | url: url |
---|
| 283 | }, null, this) |
---|
| 284 | }, this.domNode); |
---|
| 285 | } |
---|
| 286 | |
---|
| 287 | if(this.labelParam) { |
---|
| 288 | var labelNode = dojo.query(".mblThumbLabel", thumbDiv)[0]; |
---|
| 289 | if(!labelNode) { |
---|
| 290 | labelNode = dojo.create("div", { |
---|
| 291 | "class": "mblThumbLabel" |
---|
| 292 | }, thumbDiv); |
---|
| 293 | } |
---|
| 294 | labelNode.innerHTML = item[this.labelParam] || ""; |
---|
| 295 | } |
---|
| 296 | |
---|
| 297 | dojo.style(thumbDiv, "display", ""); |
---|
| 298 | if (!this.disableHide) { |
---|
| 299 | dojo.addClass(thumbDiv, "hidden"); |
---|
| 300 | } |
---|
| 301 | |
---|
| 302 | if (!cacheHit) { |
---|
| 303 | var loader = dojo.create("img", {}); |
---|
| 304 | loader._thumbDiv = thumbDiv; |
---|
| 305 | loader._conn = dojo.connect(loader, "onload", this.handleImgLoad); |
---|
| 306 | loader._url = url; |
---|
| 307 | thumbDiv._loading = true; |
---|
| 308 | |
---|
| 309 | this._onLoadImages[url] = loader; |
---|
| 310 | if (loader) { |
---|
| 311 | loader.src = url; |
---|
| 312 | } |
---|
| 313 | } |
---|
| 314 | this.visibleImages.push(thumbDiv); |
---|
| 315 | |
---|
| 316 | thumbDiv._index = index; |
---|
| 317 | thumbDiv._item = item; |
---|
| 318 | thumbDiv._url = url; |
---|
| 319 | thumbDiv._cached = false; |
---|
| 320 | |
---|
| 321 | if(!this._thumbSize){ |
---|
| 322 | this._thumbSize = dojo.marginBox(thumbDiv); |
---|
| 323 | |
---|
| 324 | if(this._thumbSize.h == 0){ |
---|
| 325 | this._thumbSize.h = 100; |
---|
| 326 | this._thumbSize.w = 100; |
---|
| 327 | } |
---|
| 328 | |
---|
| 329 | if(this.labelParam){ |
---|
| 330 | this._thumbSize.h += 8; |
---|
| 331 | } |
---|
| 332 | |
---|
| 333 | this.calcPadding(); |
---|
| 334 | } |
---|
| 335 | }, |
---|
| 336 | |
---|
| 337 | handleImgLoad: function(event){ |
---|
| 338 | var img = event.target; |
---|
| 339 | dojo.disconnect(img._conn); |
---|
| 340 | dojo.removeClass(img._thumbDiv, "hidden"); |
---|
| 341 | img._thumbDiv._loading = false; |
---|
| 342 | img._conn = null; |
---|
| 343 | |
---|
| 344 | var url = img._url; |
---|
| 345 | if(this.cacheBust){ |
---|
| 346 | url += (url.indexOf("?") > -1 ? "&" : "?") |
---|
| 347 | + "cacheBust=" + (new Date()).getTime() + "_" + (this._cacheCounter++); |
---|
| 348 | } |
---|
| 349 | |
---|
| 350 | dojo.query(".mblThumbSrc", img._thumbDiv) |
---|
| 351 | .style("backgroundImage", "url(" + url + ")"); |
---|
| 352 | |
---|
| 353 | delete this._onLoadImages[img._url]; |
---|
| 354 | }, |
---|
| 355 | |
---|
| 356 | calcPadding: function(){ |
---|
| 357 | var width = this._size.w; |
---|
| 358 | |
---|
| 359 | var thumbWidth = this._thumbSize.w; |
---|
| 360 | |
---|
| 361 | var imgBounds = thumbWidth + this.minPadding; |
---|
| 362 | |
---|
| 363 | this.maxPerRow = Math.floor(width / imgBounds); |
---|
| 364 | |
---|
| 365 | this.padding = Math.floor((width - (thumbWidth * this.maxPerRow)) / (this.maxPerRow * 2)); |
---|
| 366 | }, |
---|
| 367 | |
---|
| 368 | place: function(node, x, y){ |
---|
| 369 | dojo.style(node, { |
---|
| 370 | "-webkit-transform" :"translate(" + x + "px," + y + "px)" |
---|
| 371 | }); |
---|
| 372 | }, |
---|
| 373 | |
---|
| 374 | destroy: function(){ |
---|
| 375 | // Stop the loading of any more images |
---|
| 376 | |
---|
| 377 | var img; |
---|
| 378 | var counter = 0; |
---|
| 379 | for (var url in this._onLoadImages){ |
---|
| 380 | img = this._onLoadImages[url]; |
---|
| 381 | if (img) { |
---|
| 382 | img.src = null; |
---|
| 383 | counter++; |
---|
| 384 | } |
---|
| 385 | } |
---|
| 386 | |
---|
| 387 | this.inherited(arguments); |
---|
| 388 | } |
---|
| 389 | }); |
---|