diff options
Diffstat (limited to 'tipboard/static/js/tipboard.js')
-rw-r--r-- | tipboard/static/js/tipboard.js | 783 |
1 files changed, 783 insertions, 0 deletions
diff --git a/tipboard/static/js/tipboard.js b/tipboard/static/js/tipboard.js new file mode 100644 index 0000000..3727608 --- /dev/null +++ b/tipboard/static/js/tipboard.js @@ -0,0 +1,783 @@ +/*jslint browser: true, devel: true, evil: true*/ +/*global WebSocket: false, Tipboard: false*/ + +function RendererFactory() { } +RendererFactory.prototype.rendererName2renderObj = { + // core renderer + "axistickrenderer": $.jqplot.AxisTickRenderer, + "canvasgridrenderer": $.jqplot.CanvasGridRenderer, + "divtitlerenderer": $.jqplot.DivTitleRenderer, + "linearaxisrenderer": $.jqplot.LinearAxisRenderer, + "markerrenderer": $.jqplot.MarkerRenderer, + "shaperenderer": $.jqplot.ShapeRenderer, + "shadowrenderer": $.jqplot.ShadowRenderer, + "linerenderer": $.jqplot.LineRenderer, + "axislabelrenderer": $.jqplot.AxisLabelRenderer, + // plugins + "barrenderer": $.jqplot.BarRenderer, + "blockrenderer": $.jqplot.BlockRenderer, + "bubblerenderer": $.jqplot.BubbleRenderer, + "canvasaxislabelrenderer": $.jqplot.CanvasAxisLabelRenderer, + "canvasaxistickrenderer": $.jqplot.CanvasAxisTickRenderer, + "categoryaxisrenderer": $.jqplot.CategoryAxisRenderer, + "dateaxisrenderer": $.jqplot.DateAxisRenderer, + "donutrenderer": $.jqplot.DonutRenderer, + "enhancedlegendrenderer": $.jqplot.EnhancedLegendRenderer, + "funnelrenderer": $.jqplot.FunnelRenderer, + "logaxisrenderer": $.jqplot.LogAxisRenderer, + "mekkoaxisrenderer": $.jqplot.MekkoAxisRenderer, + "mekkorenderer": $.jqplot.MekkoRenderer, + "metergaugerenderer": $.jqplot.MeterGaugeRenderer, + "ohlcrenderer": $.jqplot.OHLCRenderer, + "pierenderer": $.jqplot.PieRenderer, + // others + "canvastextrenderer": $.jqplot.CanvasTextRenderer, + "donutlegendrenderer": $.jqplot.DonutLegendRenderer, + "pyramidaxisrenderer": $.jqplot.PyramidAxisRenderer, + "pyramidgridrenderer": $.jqplot.PyramidGridRenderer, + "pyramidrenderer": $.jqplot.PyramidRenderer +}; +RendererFactory.prototype.createRenderer = function (rendererName) { + var lower = rendererName.toLowerCase(); + var rendererClass = RendererFactory.prototype.rendererName2renderObj[lower]; + if (rendererClass === undefined) { + throw new RendererFactory.prototype.UnknownRenderer(rendererName); + } + return rendererClass; +}; +var UnknownRenderer = function (rendererName) { + this.name = "UnknownRederer"; + this.message = "Renderer: '" + rendererName + "' not found"; +}; +UnknownRenderer.prototype = new Error(); +UnknownRenderer.prototype.constructor = UnknownRenderer; +RendererFactory.prototype.UnknownRenderer = UnknownRenderer; + +function RenderersSwapper() { } +RenderersSwapper.prototype.insertRendererClasses = function(obj, keys) { + $.each(keys, function (idx, key) { + var rendererName, rendererClass; + rendererName = null; + try { + rendererName = obj[key]; + } catch (err) { + console.log('skipped', key); + } + if (typeof rendererName === "string") { + rendererClass = RendererFactory.prototype.createRenderer(rendererName); + obj[key] = rendererClass; + } + }); + return obj; +}; + +RenderersSwapper.prototype.swap = function (config) { + /* + jqplot config gets renderers under these paths: + simple: + config.legend.renderer + config.title.renderer + config.grid.renderer + config.axesDefaults.<axisRenderer> + config.seriesDefaults.<seriesRenderer> + nested: + config.axes.<axis>.<axisRenderer> + <axis> can be these keys: + xaxis, x2axis, yaxis, y2axis, .. , y9axis + config.series.<series>.<seriesRenderer> + <series>: array + + <axisRenderer> can be these keys: + - tickRenderer, labelRenderer, renderer + <seriesRenderer> can be these keys: + - markerRenderer, renderer + */ + var simple, complex; + simple = { + 'legend': ['renderer'], + 'title': ['renderer'], + 'grid': ['renderer'], + 'axesDefaults': ["tickRenderer", "labelRenderer", "renderer"], + 'seriesDefaults': ["markerRenderer", "renderer"] + }; + $.each(simple, function (key, rendererTypes) { + if (config[key]) { + config[key] = RenderersSwapper.prototype.insertRendererClasses( + config[key], rendererTypes + ); + } + }); + complex = { + 'axes': simple.axesDefaults, + 'series': simple.seriesDefaults + }; + $.each(complex, function (key, rendererTypes) { + if (config[key]) { + $.each(config[key], function (subkey, values) { + config[key][subkey] = RenderersSwapper.prototype.insertRendererClasses( + config[key][subkey], rendererTypes + ); + }); + } + }); + return config; +}; + +function getFlipTime(node) { + // TODO: make it Tipboard.Dashboard member + var classStr = $(node).attr('class'); + var flipTime = 20000; + $.each(classStr.split(' '), function(idx, val) { + var groups = /flip-time-(\d+)/.exec(val); + if (Boolean(groups) && groups.length > 1) { + flipTime = groups[1]; + flipTime = parseInt(flipTime, 10) * 1000; + return false; + } + }); + return flipTime; +} + +function verboseLog(level, msg) { + /* + This function is: DEPRECATED. + + Available levels: + 1: silent + 2: debug + */ + var threshold = 1; + if (level > threshold) { + console.log(msg); + } +} + +var renderersSwapper = new RenderersSwapper(); + +(function($) { + 'use strict'; + + if (!window.console) { + window.console = { + log: function() {} + }; + } + + window.Tipboard = {}; + + Tipboard.DisplayUtils = { + + pallet: { + 'black': '#000000', + 'white': '#FFFFFF', + 'tile_background': '#25282d', + 'red': '#DC5945', + 'yellow': '#FF9618', + 'green': '#94C140', + 'blue': '#12B0C5', + 'violet': '#9C4274', + 'orange': '#EC663C', + 'naval': '#54C5C0', + }, + + getPalette: function(colors) { + // DEPRECATED, use paletteAsList instead + var palette = []; + for (var name in colors) { + if (['black', 'white', 'tile_background'].indexOf(name) < 0) { + palette.push(colors[name]); + } + } + return palette; + }, + + paletteAsList: function() { + var paletteList = []; + $.each(Tipboard.DisplayUtils.palette, function(name, value) { + if (['black', 'white', 'tile_background'].indexOf(name) < 0) { + paletteList.push(value); + } + }); + return paletteList; + }, + + replaceFromPalette: function(color) { + $.each(this.palette, function(colorName, colorValue) { + if (color === colorName) { + color = colorValue; + return false; + } + }); + return color; + }, + + paletteAsSeriesColors: function() { + var seriesColors = []; + $.each( + Tipboard.DisplayUtils.getPalette(Tipboard.DisplayUtils.palette), + function(idx, color) { + seriesColors.push({'color': color}); + } + ); + return seriesColors; + }, + + expandLastChild: function(container) { + /* + Autogrow height of node *body* in *container* node respecting of *header* + height. + + container: + item1 + [..] + itemLast + */ + var siblingsHeight = 0; + var len = $(container).children().length; + $.each($(container).children(), function(index, element) { + if (index == len - 1) { + return false; + } + siblingsHeight += $(element).outerHeight(true); + }); + if (len - 1 < 0) { + console.log('expanding stopped - container has no elements'); + return false; + } + var body = $(container).children()[len - 1]; + var border = $(body).outerHeight(true) - $(body).height(); + var bodyHeight = $(container).height() - siblingsHeight - border; + $(body).height(bodyHeight); + }, + + showTileData: function (tile) { + $.each(['.tile-content'], function(idx, klass) { + var node = $(tile).find(klass); + if (node.length > 1) { + $(node[1]).remove(); + $(node[0]).show(); + } + }); + }, + showExcMsg: function (tile, msg) { + $.each(['.tile-content'], function(idx, klass) { + var nodes = $(tile).find(klass); + if (nodes.length === 1) { + var cloned = $(nodes).clone(); + $(nodes).hide(); + $(cloned).insertAfter(nodes); + $(cloned).addClass('exception-message'); + $(cloned).show(); + } else { + $(nodes[0]).hide(); + $(nodes[1]).show(); + } + nodes = $(tile).find('.tile-content'); + $(nodes[1]).html(msg); + }); + }, + + createGraph: function (tileId, plotData, config) { + var containerId, plot; + containerId = tileId + '-chart'; + plot = $.jqplot(containerId, plotData, config); + Tipboard.Dashboard.chartsIds[tileId] = plot; + }, + + rescaleTile: function (tile) { + /* <tile>: DOM element representing tile */ + Tipboard.DisplayUtils.expandLastChild(tile); + Tipboard.DisplayUtils.expandLastChild($(tile).find('.tile-content')[0]); + }, + + applyHighlighterConfig: function (highlighterNode, color, fading) { + if (typeof color === "undefined" && typeof fading === "undefined") { + // clear settings + $(highlighterNode).css('background-color', 'rgba(0, 0, 0, 0)'); + highlighterNode.removeClass('fading-background-color'); + } else { + if (typeof color === "undefined") { + color = $(highlighterNode).css('background-color', color); + } + color = Tipboard.DisplayUtils.replaceFromPalette(color); + $(highlighterNode).css('background-color', color); + + if (fading === true) { + highlighterNode.addClass('fading-background-color'); + } + if (fading === false) { + highlighterNode.removeClass('fading-background-color'); + } + } + highlighterNode.before(highlighterNode.clone(true)).remove(); + }, + + highlightAsOk: function(element) { + $(element).addClass('highlighted-green'); + $(element).removeClass('highlighted-red'); + }, + + highlightAsFail: function(element) { + $(element).removeClass('highlighted-green'); + $(element).addClass('highlighted-red'); + }, + + arrowUp: function(element) { + $(element).addClass('arrow-up'); + $(element).removeClass('arrow-down'); + }, + + arrowDown: function(element) { + $(element).removeClass('arrow-up'); + $(element).addClass('arrow-down'); + }, + + changeFontSize: function() { + var windowHeight = $(window).height(); + if (windowHeight > $(window).width()) { + windowHeight = $(window).width(); + } + if (windowHeight <= 768) { + return; + } + var newFontSize = Math.max(Math.floor(12 * windowHeight / 768) + 1, 12); + $('body').css({ + 'font-size': newFontSize + }); + console.log('Changed font size to ' + newFontSize + '.'); + }, + + makeTilesMargins: function() { + console.log('Updating tile sizes.'); + $('.tile').each(function() { + var tileWidth = $(this).parent().width() - 20; + var tileHeight = $(this).parent().height() - 20; + $(this).css({ + 'width': tileWidth, + 'height': tileHeight, + 'top': 10, + 'left': 10 + }); + }); + }, + + changeChartsSize: function() { + console.log('Updating chart sizes.'); + $('.chart').each(function() { + var tileHeaderHeight = $(this).parents('.tile').find('.tile-header').height(); + var tileContentHeight = 32; + + function increaseContentHeight() { + if ($(this).hasClass('small-padding')) { + tileContentHeight += $(this).height() * 1.5; + } else { + tileContentHeight += $(this).height(); + } + } + if (!$(this).hasClass('chart-only')) { + $(this).parents('.tile').find('.tile-content .result').each(increaseContentHeight); + $(this).parents('.tile').find('.tile-content h2').each(increaseContentHeight); + } + var chartHeight = $(this).parents('.tile').height() - tileHeaderHeight - tileContentHeight; + $(this).css({ + height: chartHeight + }); + // replot + var tileId = $(this).parents('div.tile').attr('id'); + var plot = Tipboard.Dashboard.chartsIds[tileId]; + if (Boolean(plot)) { + plot.replot( { resetAxes: true } ); + } + }); + }, + + resizeWindowEvent: function() { + this.makeTilesMargins(); + this.changeFontSize(); + this.changeChartsSize(); + }, + + flipFlippablesIn: function(container) { + /* + * pass class *flippedforward* to next node with class + * *flippable* within *container*, if last element then wrap it + * and pass *flippedforward* to the first element with class + * *flippable* within the *container* + */ + var nextFlipIdx; + var containerFlips = $(container).find('.flippable'); + $(containerFlips).each(function(index, tile) { + if ($(tile).hasClass("flippedforward")) { + nextFlipIdx = (index + 1) % containerFlips.length; + $(tile).removeClass("flippedforward"); + return false; // break + } + }); + if (typeof nextFlipIdx !== undefined) { + var tileToFlip = containerFlips[nextFlipIdx]; + $(tileToFlip).addClass("flippedforward"); + } + }, + + reloadPage: function() { + var http = new XMLHttpRequest(); + http.open('HEAD', window.location.href); + http.onreadystatechange = function() { + if (this.readyState === this.DONE && this.status === 200) { + window.location.reload(); + } + }; + http.send(); + } + }; + + Tipboard.TileDisplayDecorator = { + fitTile: function(tile) { + Tipboard.DisplayUtils.expandLastChild(tile); + }, + highlightResult: function(tile) { + $(tile).find('.highlighted-result [data-source-key]').each(function() { + var elementValue = parseInt($(this).html(), 10); + var parentContainer = $(this).parent('.highlighted-result'); + var threshold = $(this).attr('data-threshold'); + var isOK = false; + var thresholdFloat = parseFloat(threshold, 10); + if (!isNaN(thresholdFloat)) { + isOK = elementValue > thresholdFloat; + } else { + try { + isOK = eval(threshold); + } catch (TypeError) { + isOK = elementValue > 50; + } + } + if (isOK) { + Tipboard.DisplayUtils.highlightAsOk(parentContainer); + } else { + Tipboard.DisplayUtils.highlightAsFail(parentContainer); + } + }); + }, + + drawArrow: function(tile) { + $(tile).find('.with-arrow-result [data-source-key]').each(function() { + var elementValue = parseInt($(this).html(), 10); + var parentContainer = $(this).parent('.with-arrow-result'); + var arrowContainer = $(parentContainer).find('span.arrow'); + if (elementValue < 0) { + Tipboard.DisplayUtils.arrowDown(arrowContainer); + } else { + Tipboard.DisplayUtils.arrowUp(arrowContainer); + } + }); + }, + + runAllDecorators: function(tile) { + var item = void 0; + for (item in Tipboard.TileDisplayDecorator) { + if (typeof Tipboard.TileDisplayDecorator[item] === "function" && item !== "runAllDecorators") { + Tipboard.TileDisplayDecorator[item](tile); + } + } + } + }; + + Tipboard.WebSocketManager = { + onOpen: function(evt) { + console.log("Web socket opened."); + this.websocket.send("update"); + }, + + onClose: function(evt) { + console.log("Web socket closed. Restarting..."); + this.websocket = void 0; + setTimeout(Tipboard.WebSocketManager.init.bind(this), 1000); + }, + + onMessage: function(evt) { + var tileData = JSON.parse(evt.data); + console.log("Web socket received data: ", tileData); + // FIXME: pass colors in more suitable place + Tipboard.DisplayUtils.palette = $.extend( + true, this.palette, tileData.tipboard.color + ); + var tileId = Tipboard.Dashboard.escapeId(tileData.id); + Tipboard.Dashboard.updateTile( + tileId, + tileData.tile_template, + tileData.data, + tileData.meta, + tileData.tipboard, + tileData.modified + ); + }, + + onError: function(evt) { + console.log("WebSocket error: " + evt.data); + }, + + init: function() { + if ((this.websocket !== void 0) && !(this.websocket.readyState === WebSocket.CLOSED || this.websocket.readyState === WebSocket.CLOSING)) { + console.log("Closing outdated Web socket."); + this.websocket.close(); + return; // the rest will be handled in onClose() + } + console.log("Initializing a new Web socket manager."); + this.websocket = new WebSocket( + "ws://" + window.location.host + "/communication/websocket" + ); + this.websocket.onopen = function(evt) { + Tipboard.WebSocketManager.onOpen(evt); + }; + this.websocket.onclose = function(evt) { + Tipboard.WebSocketManager.onClose(evt); + }; + this.websocket.onmessage = function(evt) { + Tipboard.WebSocketManager.onMessage(evt); + }; + this.websocket.onerror = function(evt) { + Tipboard.WebSocketManager.onError(evt); + }; + } + }; + + Tipboard.Dashboard = { + webSocketResetInterval: 900000, + flipIds: [], + updateFunctions: {}, + chartsIds: {}, + }; + + // exceptions definition + var TimeoutBadFormat = function (timeout) { + this.name = "TimeoutBadFormat"; + this.message = "Timeout consists non-digits: '" + timeout + "'"; + }; + TimeoutBadFormat.prototype = new Error(); + TimeoutBadFormat.prototype.constructor = TimeoutBadFormat; + Tipboard.Dashboard.TimeoutBadFormat = TimeoutBadFormat; + + var UnknownUpdateFunction = function (tileType) { + this.name = "UnknownUpdateFunction"; + this.message = "Couldn't find update function for: " + tileType; + }; + UnknownUpdateFunction.prototype = new Error(); + UnknownUpdateFunction.prototype.constructor = UnknownUpdateFunction; + Tipboard.Dashboard.UnknownUpdateFunction = UnknownUpdateFunction; + + Tipboard.Dashboard.id2node = function(id) { + var tile = $('#' + id)[0]; + return tile; + }; + + Tipboard.Dashboard.tile2id = function(tileNode) { + return $(tileNode).attr('id'); + }; + + Tipboard.Dashboard.escapeId = function(id) { + /* + the Tipboard application allows user to use eg. '.' in tiles' ids + jquery requires such chars to be escaped + */ + // XXX: backslash MUST BE FIRST, otherwise this convertions is + // broken (escaping chars which meant to be escapers) + var charsToEscape = "\\!\"#$%&'()*+,./:;<=>?@[]^`{|}~"; + for(var i=0; i<charsToEscape.length; i++) { + var _char = charsToEscape[i]; + id = id.replace(_char, '\\' + _char); + } + return id; + }; + + Tipboard.Dashboard.setDataByKeys = function( + tileId, dataToPut, keysToUse + ) { + /* + *keysToUse*: list of keys, or string 'all', if 'all' then all keys + used from *dataToPut* + */ + if (keysToUse === 'all') { + var allKeys = []; + for(var k in dataToPut) allKeys.push(k); + keysToUse = allKeys; + } + var tile = Tipboard.Dashboard.id2node(tileId); + $.each(keysToUse, function(idx, key) { + var value = dataToPut[key]; + if (value === void 0) { + var msg = 'WARN: No key "' + key + '" in data' + console.log(msg, dataToPut); + } else { + var dstId = '#' + tileId + '-' + key; + var dst = $(tile).find(dstId)[0]; + if (typeof dst === 'undefined') { + var msg = 'WARN: Not found node with id: ' + dstId; + console.log(msg); + } else { + $(dst).text(value); + } + } + }); + }; + + Tipboard.Dashboard.updateTile = function( + tileId, tileType, data, meta, tipboard, lastMod + ) { + console.log('Update tile: ', tileId); + var tile = Tipboard.Dashboard.id2node(tileId); + // destroy old graph + var chartObj = Tipboard.Dashboard.chartsIds[tileId]; + if (typeof chartObj === "object") { + Tipboard.Dashboard.chartsIds[tileId].destroy(); + } + try { + var fn = Tipboard.Dashboard.getUpdateFunction(tileType); + fn(tileId, data, meta, tipboard); + $('#' + tileId + '-lastModified').val(lastMod); + Tipboard.DisplayUtils.showTileData(tile); + } catch (err) { + console.log('ERROR: ', tileId, err); + var msg = [ + 'Tile ' + tileId + ' configuration error:', + err.name || 'error name: n/a', + err.message || 'error message: n/a', + ].join('<br>'); + Tipboard.DisplayUtils.showExcMsg(tile, msg); + } + }; + + Tipboard.Dashboard.getUpdateFunction = function(tileType) { + var fn = this.updateFunctions[tileType]; + if (typeof fn !== 'function') { + throw new Tipboard.Dashboard.UnknownUpdateFunction(tileType); + } + return fn; + }; + + Tipboard.Dashboard.registerUpdateFunction = function(name, fn) { + this.updateFunctions[name] = fn; + }; + + Tipboard.Dashboard.isTileRenderedSuccessful = function(tile) { + var suc = $(tile).find('.exception-message').length === 0 ? true : false; + return suc; + }; + + Tipboard.Dashboard.isValidDatetimeFormat = function(dateString) { + // valid dateString should be in ISO-8601 format, e.g.: + // "2014-01-10T15:27:34+02:00" + var regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}$/; + var valid = regex.test(dateString); + return valid; + }; + + Tipboard.Dashboard.isDataExpired = function(lastMod, timeout) { + var reason = ""; + if (timeout === "") { + // backward compability, no timeout then skip checking + return; + } + if (!/^\d+$/.test(timeout)) { + throw new Tipboard.Dashboard.TimeoutBadFormat(timeout); + } + if (lastMod === "") { + reason = [ + 'Could not find the timestamp.', + '(maybe your data is stalled?)' + ].join('<br>'); + } else { + if (Tipboard.Dashboard.isValidDatetimeFormat(lastMod)) { + var now = new Date(); + var lastDateTime = new Date(lastMod); + var dataAge = (now - lastDateTime)/1000; // dataAge is in seconds + if (dataAge >= timeout) { + reason = "Tile's data EXPIRED."; + } + } else { + reason = [ + 'Timestamp found has bad format.', + 'found: ' + lastMod, + 'vs.', + 'expected: "yyyy-mm-dd hh:mm:ss"', + ].join('<br>'); + } + } + return reason; + }; + + Tipboard.Dashboard.autoAddFlipClasses = function(flippingContainer) { + $.each($(flippingContainer).find('.tile'), function(idx, elem) { + if (idx === 0) { + $(elem).addClass('flippedforward'); + } + $(elem).addClass('flippable'); + }); + }; + + Tipboard.Dashboard.addTilesCounter = function(col) { + var tilesTotal = $(col).children('div.tile').length; + if (tilesTotal > 1) { + $.each($(col).children('div'), function(tileIdx, tile) { + var container = $(tile).find('.tile-header'); + var title = $(container).children()[0]; + $(title).addClass('flip-tile-counter'); + var counter = (tileIdx + 1) + '/' + tilesTotal; + $(container).append('<span class="tile-counter">' + counter + '</span>'); + $(container).append('<div style="clear:both"></div>'); + }); + } + }; + + $(document).ready(function() { + $.jqplot.config.enablePlugins = true; + // resize events + $(window).bind("resize fullscreen-off", function() { + Tipboard.DisplayUtils.resizeWindowEvent(); + }); + // websocket management + Tipboard.WebSocketManager.init(); + setInterval( + Tipboard.WebSocketManager.init.bind(Tipboard.WebSocketManager), + Tipboard.Dashboard.webSocketResetInterval + ); + // flipping tiles + var flipContainers = $('div[class*="flip-time-"]'); + $.each(flipContainers, function(idx, flippingContainer) { + Tipboard.Dashboard.autoAddFlipClasses(flippingContainer); + var flipInterval = getFlipTime(flippingContainer); + var flipIntervalId = setInterval(function() { + Tipboard.DisplayUtils.flipFlippablesIn(flippingContainer); + }, flipInterval); + Tipboard.Dashboard.flipIds.push(flipIntervalId); + }); + $.each($("body > div"), function(rowIdx, row) { + // show tiles number (like: 1/3) + $.each($(row).children('div'), function(colIdx, col) { + Tipboard.Dashboard.addTilesCounter(col); + }); + }); + // watchdog + setInterval(Tipboard.DisplayUtils.reloadPage, 3600000); + + // check if all tiles have fresh data + setInterval(function() { + var tiles = $('.tile'); + $.each(tiles, function(idx, tile) { + if (Tipboard.Dashboard.isTileRenderedSuccessful(tile)) { + var tileId = Tipboard.Dashboard.tile2id(tile); + var lastMod = $('#' + tileId + '-lastModified').val(); + var timeout = $('#' + tileId + '-timeout').val(); + var reason = Tipboard.Dashboard.isDataExpired( + lastMod, timeout + ); + if (reason) { + Tipboard.DisplayUtils.showExcMsg(tile, reason); + } else { + Tipboard.DisplayUtils.showTileData(tile); + } + } + }); + }, 5000); + Tipboard.DisplayUtils.resizeWindowEvent(); + }); +}($)); |