| /* | |
| * Licensed to the Apache Software Foundation (ASF) under one | |
| * or more contributor license agreements. See the NOTICE file | |
| * distributed with this work for additional information | |
| * regarding copyright ownership. The ASF licenses this file | |
| * to you under the Apache License, Version 2.0 (the | |
| * "License"); you may not use this file except in compliance | |
| * with the License. You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, | |
| * software distributed under the License is distributed on an | |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| * KIND, either express or implied. See the License for the | |
| * specific language governing permissions and limitations | |
| * under the License. | |
| */ | |
| /** | |
| * Simple draggable tool, just for demo or testing. | |
| * Use jquery. | |
| */ | |
| (function (global) { | |
| var BORDER_WIDTH = 4; | |
| var $ = global.jQuery; | |
| global.draggable = { | |
| /** | |
| * @param {HTMLElement} mainEl | |
| * @param {module:echarts/echarts~EChart} chart | |
| * @param {Object} [opt] {width: ..., height: ...} | |
| * @param {number} [opt.width] If not specified, use mainEl current width. | |
| * @param {number} [opt.height] If not specified, use mainEl current height. | |
| * @param {boolean} [opt.lockX=false] | |
| * @param {boolean} [opt.lockY=false] | |
| * @param {number} [opt.throttle=false] | |
| * @param {Function} [opt.onDrag] | |
| * @return {type} description | |
| */ | |
| init: function (mainEl, chart, opt) { | |
| opt = opt || {}; | |
| var onDrag = opt.onDrag || $.proxy(chart.resize, chart); | |
| var onDragThrottled = chart ? onDrag : function () {}; | |
| if (opt.throttle) { | |
| onDragThrottled = throttle(onDragThrottled, opt.throttle, true, false); | |
| } | |
| var mainEl = $(mainEl); | |
| var id = mainEl.attr('data-draggable-id'); | |
| var width = mainEl.outerWidth(); | |
| var height = mainEl.outerHeight(); | |
| var mainStyle = mainEl[0].style; | |
| var placeholder = $('<div></div>').css({ | |
| position: mainStyle.position, | |
| width: width, | |
| height: height, | |
| top: mainStyle.top, | |
| bottom: mainStyle.bottom, | |
| left: mainStyle.left, | |
| right: mainStyle.right, | |
| borderWidth: 0, | |
| margin: 0, | |
| padding: 0, | |
| visibility: 'hidden' | |
| }); | |
| placeholder.insertAfter(mainEl); | |
| if (id == null) { | |
| id = +Math.random(); | |
| mainEl.attr('data-draggable-id', id); | |
| } | |
| else { | |
| $('.draggable-control[data-draggable-id=' + id + ']').remove(); | |
| } | |
| var controlEl = $( | |
| '<div class="draggable-control">DRAG<span class="draggable-label"></span></div>' | |
| ); | |
| controlEl.css({ | |
| 'position': 'absolute', | |
| 'border-radius': '30px', | |
| 'width': '60px', | |
| 'height': '60px', | |
| 'line-height': '60px', | |
| 'text-align': 'center', | |
| 'background': '#333', | |
| 'color': '#fff', | |
| 'cursor': 'pointer', | |
| 'font-size': '18px', | |
| 'box-shadow': '0 0 5px #333', | |
| '-webkit-user-select': 'none', | |
| 'user-select': 'none' | |
| }); | |
| var label = controlEl.find('.draggable-label'); | |
| label.css({ | |
| 'display': 'block', | |
| 'position': 'absolute', | |
| 'color': '#000', | |
| 'font-size': '12px', | |
| 'text-align': 'center', | |
| 'left': 0, | |
| 'top': '65px', | |
| 'width': '60px', | |
| 'line-height': 1 | |
| }); | |
| mainEl.css({ | |
| 'position': 'absolute', | |
| 'width': mainEl[0].offsetWidth + 'px', | |
| 'height': mainEl[0].offsetHeight + 'px', | |
| 'border-style': 'solid', | |
| 'border-color': '#ddd', | |
| 'border-width': BORDER_WIDTH + 'px', | |
| 'padding': 0, | |
| 'margin': 0 | |
| }); | |
| mainEl.parent().append(controlEl); | |
| var locationMaker = createLocationMaker(mainEl); | |
| var controlSize = controlEl[0].offsetWidth; | |
| var dragging = false; | |
| controlEl.on('mousedown', function () { | |
| dragging = true; | |
| }); | |
| $(document).on('mousemove', function (e) { | |
| if (dragging) { | |
| resize(e.pageX, e.pageY); | |
| } | |
| }); | |
| $(document).on('mouseup', function () { | |
| dragging = false; | |
| }); | |
| relocate(opt.width, opt.height); | |
| // A temporarily way to handle the reflow. | |
| // Where the position should be sync to the placeholder. | |
| $(function () { | |
| setTimeout(function () { | |
| relocate(); | |
| }, 0); | |
| }); | |
| function relocate(width, height) { | |
| mainEl.css({ | |
| 'left': locationMaker.left(placeholder[0].offsetLeft) + 'px', | |
| 'top': locationMaker.top(placeholder[0].offsetTop) + 'px', | |
| }); | |
| var mainContentWidth = width != null ? width : locationMaker.width(mainEl.width()); | |
| var mainContentHeight = height != null ? height : locationMaker.height(mainEl.height()); | |
| var mainOffset = mainEl.offset(); | |
| resize( | |
| mainOffset.left + mainContentWidth + BORDER_WIDTH, | |
| mainOffset.top + mainContentHeight + BORDER_WIDTH, | |
| true | |
| ); | |
| } | |
| function resize(x, y, isInit) { | |
| var mainOffset = mainEl.offset(); | |
| var mainPosition = mainEl.position(); | |
| var mainContentWidth = x - mainOffset.left - BORDER_WIDTH; | |
| var mainContentHeight = y - mainOffset.top - BORDER_WIDTH; | |
| if (isInit || !opt.lockX) { | |
| controlEl.css( | |
| 'left', | |
| (mainPosition.left + mainContentWidth + BORDER_WIDTH - controlSize / 2) + 'px' | |
| ); | |
| mainEl.css( | |
| 'width', | |
| locationMaker.width(mainContentWidth) + 'px' | |
| ); | |
| } | |
| if (isInit || !opt.lockY) { | |
| controlEl.css( | |
| 'top', | |
| (mainPosition.top + mainContentHeight + BORDER_WIDTH - controlSize / 2) + 'px' | |
| ); | |
| mainEl.css( | |
| 'height', | |
| locationMaker.height(mainContentHeight) + 'px' | |
| ); | |
| } | |
| label.text(Math.round(mainContentWidth) + ' x ' + Math.round(mainContentHeight)); | |
| onDragThrottled(); | |
| } | |
| } | |
| }; | |
| function createLocationMaker(mainEl) { | |
| var isBorderBox = mainEl.css('box-sizing') === 'border-box'; | |
| return { | |
| width: function (w) { | |
| return w + (isBorderBox ? 2 * BORDER_WIDTH : 0); | |
| }, | |
| height: function (h) { | |
| return h + (isBorderBox ? 2 * BORDER_WIDTH : 0); | |
| }, | |
| left: function (l) { | |
| return l - (isBorderBox ? 0: BORDER_WIDTH); | |
| }, | |
| top: function (t) { | |
| return t - (isBorderBox ? 0: BORDER_WIDTH); | |
| } | |
| }; | |
| } | |
| function throttle(fn, delay, trailing, debounce) { | |
| var currCall = (new Date()).getTime(); | |
| var lastCall = 0; | |
| var lastExec = 0; | |
| var timer = null; | |
| var diff; | |
| var scope; | |
| var args; | |
| var isSingle = typeof fn === 'function'; | |
| delay = delay || 0; | |
| if (isSingle) { | |
| return createCallback(); | |
| } | |
| else { | |
| var ret = []; | |
| for (var i = 0; i < fn.length; i++) { | |
| ret[i] = createCallback(i); | |
| } | |
| return ret; | |
| } | |
| function createCallback(index) { | |
| function exec() { | |
| lastExec = (new Date()).getTime(); | |
| timer = null; | |
| (isSingle ? fn : fn[index]).apply(scope, args || []); | |
| } | |
| var cb = function () { | |
| currCall = (new Date()).getTime(); | |
| scope = this; | |
| args = arguments; | |
| diff = currCall - (debounce ? lastCall : lastExec) - delay; | |
| clearTimeout(timer); | |
| if (debounce) { | |
| if (trailing) { | |
| timer = setTimeout(exec, delay); | |
| } | |
| else if (diff >= 0) { | |
| exec(); | |
| } | |
| } | |
| else { | |
| if (diff >= 0) { | |
| exec(); | |
| } | |
| else if (trailing) { | |
| timer = setTimeout(exec, -diff); | |
| } | |
| } | |
| lastCall = currCall; | |
| }; | |
| /** | |
| * Clear throttle. | |
| * @public | |
| */ | |
| cb.clear = function () { | |
| if (timer) { | |
| clearTimeout(timer); | |
| timer = null; | |
| } | |
| }; | |
| return cb; | |
| } | |
| } | |
| })(window); |