UNPKG

2gis-maps

Version:

Interactive 2GIS maps API, based on Leaflet

957 lines (769 loc) 31.7 kB
(function(window, undefined) { 'use strict'; if (!window) return; // Server side var _baron = window.baron, // Stored baron value for noConflict usage $ = window.jQuery, // Trying to use jQuery origin = { v: { // Vertical x: 'Y', pos: 'top', crossPos: 'left', size: 'height', crossSize: 'width', client: 'clientHeight', crossClient: 'clientWidth', offset: 'offsetHeight', crossOffset: 'offsetWidth', offsetPos: 'offsetTop', scroll: 'scrollTop', scrollSize: 'scrollHeight' }, h: { // Horizontal x: 'X', pos: 'left', crossPos: 'top', size: 'width', crossSize: 'height', client: 'clientWidth', crossClient: 'clientHeight', offset: 'offsetWidth', crossOffset: 'offsetHeight', offsetPos: 'offsetLeft', scroll: 'scrollLeft', scrollSize: 'scrollWidth' } }, each = function(obj, iterator) { var i = 0; if (obj.length === undefined || obj === window) obj = [obj]; while (obj[i]) { iterator.call(this, obj[i], i); i++; } }, baron = function(params) { // this - window or jQuery instance var jQueryMode = false,//(this && this[0] && this[0].nodeType), roots, $; params = params || {}; $ = params.$ || window.jQuery; if (jQueryMode) { params.root = roots = this; } else { roots = $(params.root || params.scroller); } return new baron.fn.constructor(roots, params, $); }; baron.fn = { constructor: function(roots, input, $) { var params = validate(input); params.$ = $; each.call(this, roots, function(root, i) { var localParams = clone(params); if (params.root && params.scroller) { localParams.scroller = params.$(params.scroller, root); if (!localParams.scroller.length) { localParams.scroller = root; } } else { localParams.scroller = root; } localParams.root = root; this[i] = init(localParams); this.length = i + 1; }); this.params = params; }, dispose: function() { var params = this.params; each(this, function(item) { item.dispose(params); }); this.params = null; }, update: function() { var i = 0; while (this[i]) { this[i].update.apply(this[i], arguments); i++; } }, baron: function(params) { params.root = []; params.scroller = this.params.scroller; each.call(this, this, function(elem) { params.root.push(elem.root); }); params.direction = (this.params.direction == 'v') ? 'h' : 'v'; params._chain = true; return baron(params); } }; function manageEvents(item, eventManager, mode) { item._eventHandlers = item._eventHandlers || [ // Creating new functions for one baron item only one time { // onScroll: element: item.scroller, handler: function(e) { item.scroll(e); }, type: 'scroll' }, { // onMouseDown: element: item.bar, handler: function(e) { e.preventDefault ? e.preventDefault() : e.returnValue = false; // Text selection disabling in Opera... and all other browsers? item.selection(); // Disable text selection in ie8 item.drag.now = 1; // Save private byte }, type: 'touchstart mousedown' }, { // onMouseUp: element: document, handler: function() { item.selection(1); // Enable text selection item.drag.now = 0; }, type: 'mouseup blur touchend' }, { // onCoordinateReset: element: item.bar, handler: function(e) { if (e.button != 2) { // Not RM item._pos0(e); } }, type: 'touchstart mousedown' }, { // onMouseMove: element: document, handler: function(e) { if (item.drag.now) { item.drag(e); } }, type: 'mousemove touchmove' }, { // onResize: element: window, handler: function() { item.update(); }, type: 'resize' }, { // sizeChange: element: item.root, handler: function() { item.update(); }, type: 'sizeChange' } ]; each(item._eventHandlers, function(event) { if (event.element) { eventManager(event.element, event.type, event.handler, mode); } }); // if (item.scroller) { // event(item.scroller, 'scroll', item._eventHandlers.onScroll, mode); // } // if (item.bar) { // event(item.bar, 'touchstart mousedown', item._eventHandlers.onMouseDown, mode); // } // event(document, 'mouseup blur touchend', item._eventHandlers.onMouseUp, mode); // event(document, 'touchstart mousedown', item._eventHandlers.onCoordinateReset, mode); // event(document, 'mousemove touchmove', item._eventHandlers.onMouseMove, mode); // event(window, 'resize', item._eventHandlers.onResize, mode); // if (item.root) { // event(item.root, 'sizeChange', item._eventHandlers.onResize, mode); // Custon event for alternate baron update mechanism // } } function manageAttr(node, direction, mode) { var attrName = 'data-baron-' + direction; if (mode == 'on') { node.setAttribute(attrName, 'inited'); } else if (mode == 'off') { node.removeAttribute(attrName); } else { return node.getAttribute(attrName); } } function init(params) { if (manageAttr(params.root, params.direction)) return; var out = new item.prototype.constructor(params); // __proto__ of returning object is baron.prototype manageEvents(out, params.event, 'on'); manageAttr(out.root, params.direction, 'on'); out.update(); return out; } function clone(input) { var output = {}; input = input || {}; for (var key in input) { if (input.hasOwnProperty(key)) { output[key] = input[key]; } } return output; } function validate(input) { var output = clone(input); output.direction = output.direction || 'v'; var event = input.event || function(elem, event, func, mode) { output.$(elem)[mode || 'on'](event, func); }; output.event = function(elems, e, func, mode) { each(elems, function(elem) { event(elem, e, func, mode); }); }; return output; } function fire(eventName) { /* jshint validthis:true */ if (this.events && this.events[eventName]) { for (var i = 0 ; i < this.events[eventName].length ; i++) { var args = Array.prototype.slice.call( arguments, 1 ); this.events[eventName][i].apply(this, args); } } } var item = {}; item.prototype = { constructor: function(params) { var $, barPos, scrollerPos0, track, resizePauseTimer, scrollPauseTimer, pause, scrollLastFire, resizeLastFire; resizeLastFire = scrollLastFire = new Date().getTime(); $ = this.$ = params.$; this.event = params.event; this.events = {}; function getNode(sel, context) { return $(sel, context)[0]; // Can be undefined } // DOM elements this.root = params.root; // Always html node, not just selector this.scroller = getNode(params.scroller); // (params.scroller) ? getNode(params.scroller, this.root) : this.root; this.bar = getNode(params.bar, this.root); track = this.track = getNode(params.track, this.root); if (!this.track && this.bar) { track = this.bar.parentNode; } this.clipper = this.scroller.parentNode; // Parameters this.direction = params.direction; this.origin = origin[this.direction]; this.barOnCls = params.barOnCls; this.barTopLimit = 0; pause = params.pause * 1000 || 0; // Updating height or width of bar function setBarSize(size) { /* jshint validthis:true */ var barMinSize = this.barMinSize || 20; if (size > 0 && size < barMinSize) { size = barMinSize; } if (this.bar) { $(this.bar).css(this.origin.size, parseInt(size, 10) + 'px'); } } // Updating top or left bar position function posBar(pos) { /* jshint validthis:true */ if (this.bar) { $(this.bar).css(this.origin.pos, +pos + 'px'); } } // Free path for bar function k() { /* jshint validthis:true */ return track[this.origin.client] - this.barTopLimit - this.bar[this.origin.offset]; } // Relative content top position to bar top position function relToPos(r) { /* jshint validthis:true */ return r * k.call(this) + this.barTopLimit; } // Bar position to relative content position function posToRel(t) { /* jshint validthis:true */ return (t - this.barTopLimit) / k.call(this); } // Cursor position in main direction in px // Now with iOs support this.cursor = function(e) { return e['client' + this.origin.x] || (((e.originalEvent || e).touches || {})[0] || {})['page' + this.origin.x]; }; // Text selection pos preventing function dontPosSelect() { return false; } this.pos = function(x) { // Absolute scroller position in px var ie = 'page' + this.origin.x + 'Offset', key = (this.scroller[ie]) ? ie : this.origin.scroll; if (x !== undefined) this.scroller[key] = x; return this.scroller[key]; }; this.rpos = function(r) { // Relative scroller position (0..1) var free = this.scroller[this.origin.scrollSize] - this.scroller[this.origin.client], x; if (r) x = this.pos(r * free); else x = this.pos(); return x / (free || 1); }; // Switch on the bar by adding user-defined CSS classname to scroller this.barOn = function(dispose) { if (this.barOnCls) { if (dispose || this.scroller[this.origin.client] >= this.scroller[this.origin.scrollSize]) { $(this.root).removeClass(this.barOnCls); } else { $(this.root).addClass(this.barOnCls); } } }; this._pos0 = function(e) { scrollerPos0 = this.cursor(e) - barPos; }; this.drag = function(e) { this.scroller[this.origin.scroll] = posToRel.call(this, this.cursor(e) - scrollerPos0) * (this.scroller[this.origin.scrollSize] - this.scroller[this.origin.client]); }; // Text selection preventing on drag this.selection = function(enable) { this.event(document, 'selectpos selectstart', dontPosSelect, enable ? 'off' : 'on'); }; // onResize & DOM modified handler this.resize = function() { var self = this, delay = 0; if (new Date().getTime() - resizeLastFire < pause) { clearTimeout(resizePauseTimer); delay = pause; } function upd() { var delta = self.scroller[self.origin.crossOffset] - self.scroller[self.origin.crossClient]; if (params.freeze && !self.clipper.style[self.origin.crossSize]) { // Sould fire only once $(self.clipper).css(self.origin.crossSize, self.clipper[self.origin.crossClient] - delta + 'px'); } $(self.scroller).css(self.origin.crossSize, self.clipper[self.origin.crossClient] + delta + 'px'); Array.prototype.unshift.call(arguments, 'resize'); fire.apply(self, arguments); resizeLastFire = new Date().getTime(); } if (delay) { resizePauseTimer = setTimeout(upd, delay); } else { upd(); } }; // onScroll handler this.scroll = function() { var oldBarSize, newBarSize, delay = 0, self = this; if (new Date().getTime() - scrollLastFire < pause) { clearTimeout(scrollPauseTimer); delay = pause; } function upd() { if (self.bar) { newBarSize = (track[self.origin.client] - self.barTopLimit) * self.scroller[self.origin.client] / self.scroller[self.origin.scrollSize]; // Positioning bar if (oldBarSize != newBarSize) { setBarSize.call(self, newBarSize); oldBarSize = newBarSize; } barPos = relToPos.call(self, self.rpos()); posBar.call(self, barPos); } Array.prototype.unshift.call( arguments, 'scroll' ); fire.apply(self, arguments); scrollLastFire = new Date().getTime(); } if (delay) { scrollPauseTimer = setTimeout(upd, delay); } else { upd(); } }; return this; }, update: function(params) { fire.call(this, 'upd', params); // Обновляем параметры всех плагинов this.resize(1); this.barOn(); this.scroll(); return this; }, dispose: function(params) { manageEvents(this, this.event, 'off'); manageAttr(this.root, params.direction, 'off'); $(this.scroller).css(this.origin.crossSize, ''); this.barOn(true); fire.call(this, 'dispose'); }, on: function(eventName, func, arg) { var names = eventName.split(' '); for (var i = 0 ; i < names.length ; i++) { if (names[i] == 'init') { func.call(this, arg); } else { this.events[names[i]] = this.events[names[i]] || []; this.events[names[i]].push(function(userArg) { func.call(this, userArg || arg); }); } } } }; baron.fn.constructor.prototype = baron.fn; item.prototype.constructor.prototype = item.prototype; // Use when you need "baron" global var for another purposes baron.noConflict = function() { window.baron = _baron; // Restoring original value of "baron" global var return baron; }; baron.version = '0.6.6'; if ($ && $.fn) { // Adding baron to jQuery as plugin $.fn.baron = baron; } window.baron = baron; // Use noConflict method if you need window.baron var for another purposes var fix = function(userParams) { var elements, viewPortSize, params = { // Default params outside: '', before: '', after: '', past: '', future: '', radius: 0, minView: 0 }, topFixHeights = [], // inline style for element topRealHeights = [], // real offset position when not fixed headerTops = [], scroller = this.scroller, eventManager = this.event, $ = this.$, self = this; function fixElement(i, pos) { if (viewPortSize < (params.minView || 0)) { // No headers fixing when no enought space for viewport pos = undefined; } if (pos !== undefined) { pos += 'px'; this.$(elements[i]).css(this.origin.pos, pos).addClass(params.outside); } else { this.$(elements[i]).css(this.origin.pos, '').removeClass(params.outside); } } function bubbleWheel(e) { try { i = document.createEvent('WheelEvent'); // i - for extra byte // evt.initWebKitWheelEvent(deltaX, deltaY, window, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey); i.initWebKitWheelEvent(e.originalEvent.wheelDeltaX, e.originalEvent.wheelDeltaY); scroller.dispatchEvent(i); e.preventDefault(); } catch (e) {} } function init(_params) { var pos; for (var key in _params) { params[key] = _params[key]; } elements = this.$(params.elements, this.scroller); if (elements) { viewPortSize = this.scroller[this.origin.client]; for (var i = 0 ; i < elements.length ; i++) { // Variable header heights pos = {}; pos[this.origin.size] = elements[i][this.origin.offset]; if (elements[i].parentNode !== this.scroller) { this.$(elements[i].parentNode).css(pos); } pos = {}; pos[this.origin.crossSize] = elements[i].parentNode[this.origin.crossClient]; this.$(elements[i]).css(pos); // Between fixed headers viewPortSize -= elements[i][this.origin.offset]; headerTops[i] = elements[i].parentNode[this.origin.offsetPos]; // No paddings for parentNode // Summary elements height above current topFixHeights[i] = (topFixHeights[i - 1] || 0); // Not zero because of negative margins topRealHeights[i] = (topRealHeights[i - 1] || Math.min(headerTops[i], 0)); if (elements[i - 1]) { topFixHeights[i] += elements[i - 1][this.origin.offset]; topRealHeights[i] += elements[i - 1][this.origin.offset]; } if ( !(i == 0 && headerTops[i] == 0)/* && force */) { this.event(elements[i], 'mousewheel', bubbleWheel, 'off'); this.event(elements[i], 'mousewheel', bubbleWheel); } } if (params.limiter && elements[0]) { // Bottom edge of first header as top limit for track if (this.track && this.track != this.scroller) { pos = {}; pos[this.origin.pos] = elements[0].parentNode[this.origin.offset]; this.$(this.track).css(pos); } else { this.barTopLimit = elements[0].parentNode[this.origin.offset]; } // this.barTopLimit = elements[0].parentNode[this.origin.offset]; this.scroll(); } if (params.limiter === false) { // undefined (in second fix instance) should have no influence on bar limit this.barTopLimit = 0; } } var event = { element: elements, handler: function() { var parent = $(this)[0].parentNode, top = parent.offsetTop, num; // finding num -> elements[num] === this for (var i = 0 ; i < elements.length ; i++ ) { if (elements[i] === this) num = i; } var pos = top - topFixHeights[num]; if (params.scroll) { // User defined callback params.scroll({ x1: self.scroller.scrollTop, x2: pos }); } else { self.scroller.scrollTop = pos; } }, type: 'click' }; if (params.clickable) { this._eventHandlers.push(event); // For auto-dispose eventManager(event.element, event.type, event.handler, 'off'); eventManager(event.element, event.type, event.handler, 'on'); } } this.on('init', init, userParams); this.on('init scroll', function() { var fixState, hTop, fixFlag = []; // 1 - past, 2 - future, 3 - current (not fixed) if (elements) { var change; // fixFlag update for (var i = 0 ; i < elements.length ; i++) { fixState = 0; if (headerTops[i] - this.pos() < topRealHeights[i] + params.radius) { // Header trying to go up fixState = 1; hTop = topFixHeights[i]; } else if (headerTops[i] - this.pos() > topRealHeights[i] + viewPortSize - params.radius) { // Header trying to go down fixState = 2; hTop = topFixHeights[i] + viewPortSize; } else { // Header in viewport fixState = 3; hTop = undefined; } if (fixState != fixFlag[i]) { fixElement.call(this, i, hTop); fixFlag[i] = fixState; change = true; } } // Adding positioning classes (on last top and first bottom header) if (change) { // At leats one change in elements flag structure occured for (i = 0 ; i < elements.length ; i++) { if (fixFlag[i] == 1 && params.past) { this.$(elements[i]).addClass(params.past).removeClass(params.future); } if (fixFlag[i] == 2 && params.future) { this.$(elements[i]).addClass(params.future).removeClass(params.past); } if (fixFlag[i] == 3 && (params.future || params.past)) { this.$(elements[i]).removeClass(params.past).removeClass(params.future); } if (fixFlag[i] != fixFlag[i + 1] && fixFlag[i] == 1 && params.before) { this.$(elements[i]).addClass(params.before).removeClass(params.after); // Last top fixed header } else if (fixFlag[i] != fixFlag[i - 1] && fixFlag[i] == 2 && params.after) { this.$(elements[i]).addClass(params.after).removeClass(params.before); // First bottom fixed header } else { this.$(elements[i]).removeClass(params.before).removeClass(params.after); } } } } }); this.on('resize upd', function(updParams) { init.call(this, updParams && updParams.fix); }); }; baron.fn.fix = function(params) { var i = 0; while (this[i]) { fix.call(this[i], params); i++; } return this; }; var controls = function(params) { var forward, backward, track, screen, self = this; // AAAAAA!!!!!11 screen = params.screen || 0.9; if (params.forward) { forward = this.$(params.forward, this.clipper); this.event(forward, 'click', function() { var y = self.pos() - params.delta || 30; self.pos(y); }); } if (params.backward) { backward = this.$(params.backward, this.clipper); this.event(backward, 'click', function() { var y = self.pos() + params.delta || 30; self.pos(y); }); } if (params.track) { if (params.track === true) { track = this.track; } else { track = this.$(params.track, this.clipper)[0]; } if (track) { this.event(track, 'mousedown', function(e) { var x = e['offset' + self.origin.x], xBar = self.bar[self.origin.offsetPos], sign = 0; if (x < xBar) { sign = -1; } else if (x > xBar + self.bar[self.origin.offset]) { sign = 1; } var y = self.pos() + sign * screen * self.scroller[self.origin.client]; self.pos(y); }); } } }; baron.fn.controls = function(params) { var i = 0; while (this[i]) { controls.call(this[i], params); i++; } return this; }; var pull = function(params) { var block = this.$(params.block), size = params.size || this.origin.size, limit = params.limit || 80, onExpand = params.onExpand, elements = params.elements || [], inProgress = params.inProgress || '', self = this, _insistence = 0, _zeroXCount = 0, _interval, _timer, _x = 0, _onExpandCalled, _waiting = params.waiting || 500, _on; function getSize() { return self.scroller[self.origin.scroll] + self.scroller[self.origin.offset]; } // Scroller content height function getContentSize() { return self.scroller[self.origin.scrollSize]; } // Scroller height function getScrollerSize() { return self.scroller[self.origin.client]; } function step(x, force) { var k = x * 0.0005; return Math.floor(force - k * (x + 550)); } function toggle(on) { _on = on; if (on) { update(); // First time with no delay _interval = setInterval(update, 200); } else { clearInterval(_interval); } } function update() { var pos = {}, height = getSize(), scrollHeight = getContentSize(), dx, op4, scrollInProgress = _insistence == 1; op4 = 0; // Возвращающая сила if (_insistence > 0) { op4 = 40; } //if (_insistence > -1) { dx = step(_x, op4); if (height >= scrollHeight - _x && _insistence > -1) { if (scrollInProgress) { _x += dx; } } else { _x = 0; } if (_x < 0) _x = 0; pos[size] = _x + 'px'; if (getScrollerSize() <= getContentSize()) { self.$(block).css(pos); for (var i = 0 ; i < elements.length ; i++) { self.$(elements[i].self).css(elements[i].property, Math.min(_x / limit * 100, 100) + '%'); } } if (inProgress && _x) { self.$(self.root).addClass(inProgress); } if (_x == 0) { if (params.onCollapse) { params.onCollapse(); } } _insistence = 0; _timer = setTimeout(function() { _insistence = -1; }, _waiting); //} if (onExpand && _x > limit && !_onExpandCalled) { onExpand(); _onExpandCalled = true; } if (_x == 0) { _zeroXCount++; } else { _zeroXCount = 0; } if (_zeroXCount > 1) { toggle(false); _onExpandCalled = false; if (inProgress) { self.$(self.root).removeClass(inProgress); } } } this.on('init', function() { toggle(true); }); this.on('dispose', function() { toggle(false); }); this.event(this.scroller, 'mousewheel DOMMouseScroll', function(e) { var down = e.wheelDelta < 0 || (e.originalEvent && e.originalEvent.wheelDelta < 0) || e.detail > 0; if (down) { _insistence = 1; clearTimeout(_timer); if (!_on && getSize() >= getContentSize()) { toggle(true); } } // else { // toggle(false); // } }); }; baron.fn.pull = function(params) { var i = 0; while (this[i]) { pull.call(this[i], params); i++; } return this; }; })(window);