<commit_before>/** @module deliteful/ViewStack */
define(["dcl/dcl",
	"dojo/sniff",
	"dojo/on",
	"dojo/Deferred",
	"dojo/dom-geometry",
	"dojo/dom-class",
	"delite/register",
	"delite/Widget",
	"delite/DisplayContainer",
	"delite/theme!./ViewStack/themes/{{theme}}/ViewStack_css",
	"delite/css!./ViewStack/transitions/slide_css",
	"delite/css!./ViewStack/transitions/reveal_css",
	"delite/css!./ViewStack/transitions/flip_css",
	"delite/css!./ViewStack/transitions/fade_css"],
	function (dcl, has, on, Deferred, domGeometry, domClass, register, Widget, DisplayContainer) {
		function setVisibility(node, val) {
			if (node) {
				if (val) {
					node.style.visibility = "visible";
					node.style.display = "";
				} else {
					node.style.visibility = "hidden";
					node.style.display = "none";
				}
			}
		}
		function setReverse(node) {
			if (node) {
				domClass.add(node, "-d-view-stack-reverse");
			}
		}
		function cleanCSS(node) {
			if (node) {
				node.className = node.className.split(/ +/).filter(function (x) {
					return !/^-d-view-stack/.test(x);
				}).join(" ");
			}
		}
		function transitionClass(s) {
			return "-d-view-stack-" + s;
		}

		/**
		 * ViewStack container widget. Display one child at a time.
		 *
		 * The first child is displayed by default.
		 * The methods 'show' is used to change the visible child.
		 *
		 * Styling
		 * The following CSS attributes must not be changed.
		 *  ViewStack node:  position, box-sizing, overflow-x
		 *  ViewStack children:  position, box-sizing, width, height
		 *
		 * @example:
		 * <d-view-stack id="vs">
		 *
...

		 *
...

		 *
...

		 * </d-view-stack>
		 * <button onclick="vs.show(childB, {transition: 'reveal', reverse: true})">...</button>
		 * @class module:deliteful/ViewStack
		 * @augments {module:delite/DisplayContainer}
		 */
		return register("d-view-stack", [HTMLElement, DisplayContainer],
			/** @lends module:deliteful/ViewStack# */{
			/**
			 * The name of the CSS class of this widget.
			 * @member {string}
			 * @default "d-view-stack"
			 */
			baseClass: "d-view-stack",

			/**
			 * The transition type used if not specified in the second argument of the show method.
			 * Transitions type are: "none", "slide", "reveal", "flip", "fade".
			 * @member {string}
			 * @default "slide"
			 */
			transition: "slide",

			/**
			 * If true, the transition animation is reversed.
			 * This attribute is supported by "slide" and "reveal" transition types.
			 * @member {boolean}
			 * @default false
			 */
			reverse: false,

			/**
			 * The selected child id, can be set explicitly or through the show() method.
			 * The effect of setting this property (i.e. getting the value through the getter) might be
			 * asynchronous when an animated transition occurs.
			 * @member {string}
			 * @default ""
			 */
			selectedChildId: "",

			_setSelectedChildIdAttr: function (child) {
				this.show(child, this._started ? null : {transition: "none"});
			},

			_getSelectedChildIdAttr: function () {
				return this._visibleChild ? this._visibleChild.id : "";
			},

			_timing: 0,

			_setChildrenVisibility: function () {
				var cdn = this.children;
				if (!this._visibleChild && cdn.length > 0) {
					this._visibleChild = cdn[0];
				}
				for (var i = 0; i < cdn.length; i++) {
					setVisibility(cdn[i], cdn[i] === this._visibleChild);
				}
			},

			preCreate: function () {
				this._transitionTiming = {default: 0, chrome: 20, ios: 20, android: 100, mozilla: 100};
				for (var o in this._transitionTiming) {
					if (has(o) && this._timing < this._transitionTiming[o]) {
						this._timing = this._transitionTiming[o];
					}
				}
				if (typeof MutationObserver !== "undefined") {
					new MutationObserver(this._setChildrenVisibility.bind(this)).observe(this, {childList: true});
				}
				else {
					this._setupContainerMethods();
				}
			},

			_setupContainerMethods: function () {
				var superAppendChild = this.appendChild;
				this.appendChild = function (newChild) {
					var res = superAppendChild.apply(this, [newChild]);
					this._setChildrenVisibility();
					return res;
				};
				var superInsertBefore = this.insertBefore;
				this.insertBefore = function (newChild, refChild) {
					var res = superInsertBefore.apply(this, [newChild, refChild]);
					this._setChildrenVisibility();
					return res;
				};
			},

			buildRendering: function () {
				this._setChildrenVisibility();
			},

			showNext: function (props) {
				//		Shows the next child in the container.
				this._showPreviousNext(this.getNextSibling.bind(this), props);
			},

			showPrevious: function (props) {
				//		Shows the previous child in the container.
				this._showPreviousNext(this.getPreviousSibling.bind(this), props);
			},

			_showPreviousNext: function (func, props) {
				if (!this._visibleChild && this.children.length > 0) {
					this._visibleChild = this.children[0];
				}
				if (this._visibleChild) {
					var target = func(this._visibleChild);
					if (target) {
						this.show(target, props);
					}
				}
			},

			changeDisplay: function (widget, event) {
				// Resolved when display is completed.
				var deferred = new Deferred();

				if (!widget || widget.parentNode !== this) {
					deferred.resolve();
					return deferred.promise;
				}

				var origin = this._visibleChild;

				// Needed because the CSS state of a node can be incorrect
				// if a previous transitionEnd has been dropped
				cleanCSS(origin);
				cleanCSS(widget);

				setVisibility(widget, true);
				this._visibleChild = widget;

				var transition = event.transition || "slide";
				var reverse = this.isLeftToRight() ? event.reverse : !event.reverse;
				if (transition !== "none") {
					if (origin) {
						this._setAfterTransitionHandlers(origin, event);
						domClass.add(origin, transitionClass(transition));
					}
					if (widget) {
						this._setAfterTransitionHandlers(widget, event, deferred);
						domClass.add(widget, [transitionClass(transition), "-d-view-stack-in"]);
					}
					if (reverse) {
						setReverse(origin);
						setReverse(widget);
					}
					this.defer(function () {
						if (widget) {
							domClass.add(widget, "-d-view-stack-transition");
						}
						if (origin) {
							domClass.add(origin, ["-d-view-stack-transition", "-d-view-stack-out"]);
						}
						if (reverse) {
							setReverse(origin);
							setReverse(widget);
						}
						if (widget) {
							domClass.add(widget, "-d-view-stack-in");
						}
					}, this._timing);
				} else {
					if (origin !== widget) {
						setVisibility(origin, false);
					}
					deferred.resolve();
				}

				return deferred.promise;
			},
			/**
			 * @summary:
			 * Shows a children of the ViewStack. The parameter 'params' is optional. If not specified,
			 * this.transition, and this.reverse are used.
			 * This method must be called to display a particular destination child on this container.
			 * @param {dest} Widget or HTMLElement or id that points to the child this container must display.
			 * @param {params} Optional params. A hash like {transition: "reveal", reverse: true}. The transition value
			 * can be "slide", "overlay", "fade" or "flip". Reverse transition applies to "slide" and
			 * "reveal".
			 * @returns {module:dojo/promise/Promise} A promise that will be resolved when the display and
			 * transition effect will have been performed.
			 */
			show: dcl.superCall(function (sup) {
				return function (dest, params) {

					if (this._visibleChild && this._visibleChild.parentNode !== this) {
						// The visible child has been removed.
						this._visibleChild = null;
					}
					if (!this._visibleChild && this.children.length > 0) {
						// The default visible child is the first one.
						this._visibleChild = this.children[0];
					}
					return sup.apply(this, [dest, params]);
				};
			}),

			_setAfterTransitionHandlers: function (node, event, deferred) {
				var self = this, endProps = {
					node: node,
					handle: function () { self._afterTransitionHandle(endProps); },
					props: event,
					deferred: deferred
				};

				domClass.add(this, "-d-view-stack-transition");
				node.addEventListener("webkitTransitionEnd", endProps.handle);
				node.addEventListener("transitionend", endProps.handle); // IE10 + FF
			},

			_afterTransitionHandle: function (item) {
				// Defensive approach
				// We should work only on item.node but transitionEnd events can be dropped on aggressive interactions
				for (var i = 0; i < this.children.length; i++) {
					setVisibility(this.children[i], this._visibleChild === this.children[i]);
				}
				cleanCSS(item.node);
				item.node.removeEventListener("webkitTransitionEnd", item.handle);
				item.node.removeEventListener("transitionend", item.handle);
				if (item.deferred) {
					domClass.remove(this, "-d-view-stack-transition");
					item.deferred.resolve();
				}
			}
		});
	});

<commit_msg>JSDoc: remove a left-over @summary<commit_after>/** @module deliteful/ViewStack */
define(["dcl/dcl",
	"dojo/sniff",
	"dojo/on",
	"dojo/Deferred",
	"dojo/dom-geometry",
	"dojo/dom-class",
	"delite/register",
	"delite/Widget",
	"delite/DisplayContainer",
	"delite/theme!./ViewStack/themes/{{theme}}/ViewStack_css",
	"delite/css!./ViewStack/transitions/slide_css",
	"delite/css!./ViewStack/transitions/reveal_css",
	"delite/css!./ViewStack/transitions/flip_css",
	"delite/css!./ViewStack/transitions/fade_css"],
	function (dcl, has, on, Deferred, domGeometry, domClass, register, Widget, DisplayContainer) {
		function setVisibility(node, val) {
			if (node) {
				if (val) {
					node.style.visibility = "visible";
					node.style.display = "";
				} else {
					node.style.visibility = "hidden";
					node.style.display = "none";
				}
			}
		}
		function setReverse(node) {
			if (node) {
				domClass.add(node, "-d-view-stack-reverse");
			}
		}
		function cleanCSS(node) {
			if (node) {
				node.className = node.className.split(/ +/).filter(function (x) {
					return !/^-d-view-stack/.test(x);
				}).join(" ");
			}
		}
		function transitionClass(s) {
			return "-d-view-stack-" + s;
		}

		/**
		 * ViewStack container widget. Display one child at a time.
		 *
		 * The first child is displayed by default.
		 * The methods 'show' is used to change the visible child.
		 *
		 * Styling
		 * The following CSS attributes must not be changed.
		 *  ViewStack node:  position, box-sizing, overflow-x
		 *  ViewStack children:  position, box-sizing, width, height
		 *
		 * @example:
		 * <d-view-stack id="vs">
		 *
...

		 *
...

		 *
...

		 * </d-view-stack>
		 * <button onclick="vs.show(childB, {transition: 'reveal', reverse: true})">...</button>
		 * @class module:deliteful/ViewStack
		 * @augments {module:delite/DisplayContainer}
		 */
		return register("d-view-stack", [HTMLElement, DisplayContainer],
			/** @lends module:deliteful/ViewStack# */{
			/**
			 * The name of the CSS class of this widget.
			 * @member {string}
			 * @default "d-view-stack"
			 */
			baseClass: "d-view-stack",

			/**
			 * The transition type used if not specified in the second argument of the show method.
			 * Transitions type are: "none", "slide", "reveal", "flip", "fade".
			 * @member {string}
			 * @default "slide"
			 */
			transition: "slide",

			/**
			 * If true, the transition animation is reversed.
			 * This attribute is supported by "slide" and "reveal" transition types.
			 * @member {boolean}
			 * @default false
			 */
			reverse: false,

			/**
			 * The selected child id, can be set explicitly or through the show() method.
			 * The effect of setting this property (i.e. getting the value through the getter) might be
			 * asynchronous when an animated transition occurs.
			 * @member {string}
			 * @default ""
			 */
			selectedChildId: "",

			_setSelectedChildIdAttr: function (child) {
				this.show(child, this._started ? null : {transition: "none"});
			},

			_getSelectedChildIdAttr: function () {
				return this._visibleChild ? this._visibleChild.id : "";
			},

			_timing: 0,

			_setChildrenVisibility: function () {
				var cdn = this.children;
				if (!this._visibleChild && cdn.length > 0) {
					this._visibleChild = cdn[0];
				}
				for (var i = 0; i < cdn.length; i++) {
					setVisibility(cdn[i], cdn[i] === this._visibleChild);
				}
			},

			preCreate: function () {
				this._transitionTiming = {default: 0, chrome: 20, ios: 20, android: 100, mozilla: 100};
				for (var o in this._transitionTiming) {
					if (has(o) && this._timing < this._transitionTiming[o]) {
						this._timing = this._transitionTiming[o];
					}
				}
				if (typeof MutationObserver !== "undefined") {
					new MutationObserver(this._setChildrenVisibility.bind(this)).observe(this, {childList: true});
				}
				else {
					this._setupContainerMethods();
				}
			},

			_setupContainerMethods: function () {
				var superAppendChild = this.appendChild;
				this.appendChild = function (newChild) {
					var res = superAppendChild.apply(this, [newChild]);
					this._setChildrenVisibility();
					return res;
				};
				var superInsertBefore = this.insertBefore;
				this.insertBefore = function (newChild, refChild) {
					var res = superInsertBefore.apply(this, [newChild, refChild]);
					this._setChildrenVisibility();
					return res;
				};
			},

			buildRendering: function () {
				this._setChildrenVisibility();
			},

			showNext: function (props) {
				//		Shows the next child in the container.
				this._showPreviousNext(this.getNextSibling.bind(this), props);
			},

			showPrevious: function (props) {
				//		Shows the previous child in the container.
				this._showPreviousNext(this.getPreviousSibling.bind(this), props);
			},

			_showPreviousNext: function (func, props) {
				if (!this._visibleChild && this.children.length > 0) {
					this._visibleChild = this.children[0];
				}
				if (this._visibleChild) {
					var target = func(this._visibleChild);
					if (target) {
						this.show(target, props);
					}
				}
			},

			changeDisplay: function (widget, event) {
				// Resolved when display is completed.
				var deferred = new Deferred();

				if (!widget || widget.parentNode !== this) {
					deferred.resolve();
					return deferred.promise;
				}

				var origin = this._visibleChild;

				// Needed because the CSS state of a node can be incorrect
				// if a previous transitionEnd has been dropped
				cleanCSS(origin);
				cleanCSS(widget);

				setVisibility(widget, true);
				this._visibleChild = widget;

				var transition = event.transition || "slide";
				var reverse = this.isLeftToRight() ? event.reverse : !event.reverse;
				if (transition !== "none") {
					if (origin) {
						this._setAfterTransitionHandlers(origin, event);
						domClass.add(origin, transitionClass(transition));
					}
					if (widget) {
						this._setAfterTransitionHandlers(widget, event, deferred);
						domClass.add(widget, [transitionClass(transition), "-d-view-stack-in"]);
					}
					if (reverse) {
						setReverse(origin);
						setReverse(widget);
					}
					this.defer(function () {
						if (widget) {
							domClass.add(widget, "-d-view-stack-transition");
						}
						if (origin) {
							domClass.add(origin, ["-d-view-stack-transition", "-d-view-stack-out"]);
						}
						if (reverse) {
							setReverse(origin);
							setReverse(widget);
						}
						if (widget) {
							domClass.add(widget, "-d-view-stack-in");
						}
					}, this._timing);
				} else {
					if (origin !== widget) {
						setVisibility(origin, false);
					}
					deferred.resolve();
				}

				return deferred.promise;
			},
			/**
			 * Shows a children of the ViewStack. The parameter 'params' is optional. If not specified,
			 * this.transition, and this.reverse are used.
			 * This method must be called to display a particular destination child on this container.
			 * @param {dest} Widget or HTMLElement or id that points to the child this container must display.
			 * @param {params} Optional params. A hash like {transition: "reveal", reverse: true}. The transition value
			 * can be "slide", "overlay", "fade" or "flip". Reverse transition applies to "slide" and
			 * "reveal".
			 * @returns {module:dojo/promise/Promise} A promise that will be resolved when the display and
			 * transition effect will have been performed.
			 */
			show: dcl.superCall(function (sup) {
				return function (dest, params) {

					if (this._visibleChild && this._visibleChild.parentNode !== this) {
						// The visible child has been removed.
						this._visibleChild = null;
					}
					if (!this._visibleChild && this.children.length > 0) {
						// The default visible child is the first one.
						this._visibleChild = this.children[0];
					}
					return sup.apply(this, [dest, params]);
				};
			}),

			_setAfterTransitionHandlers: function (node, event, deferred) {
				var self = this, endProps = {
					node: node,
					handle: function () { self._afterTransitionHandle(endProps); },
					props: event,
					deferred: deferred
				};

				domClass.add(this, "-d-view-stack-transition");
				node.addEventListener("webkitTransitionEnd", endProps.handle);
				node.addEventListener("transitionend", endProps.handle); // IE10 + FF
			},

			_afterTransitionHandle: function (item) {
				// Defensive approach
				// We should work only on item.node but transitionEnd events can be dropped on aggressive interactions
				for (var i = 0; i < this.children.length; i++) {
					setVisibility(this.children[i], this._visibleChild === this.children[i]);
				}
				cleanCSS(item.node);
				item.node.removeEventListener("webkitTransitionEnd", item.handle);
				item.node.removeEventListener("transitionend", item.handle);
				if (item.deferred) {
					domClass.remove(this, "-d-view-stack-transition");
					item.deferred.resolve();
				}
			}
		});
	});

<|endoftext|>


<commit_before>var defaultConf =  {
		linear: {
			drawAllLinks: false,
			startLineColor: "#49006a",
			endLineColor: "#1d91c0",
		},
		circular: {
			tickSize: 5
		},
		graphicalParameters: {
			width: 1000,
			height: 1000,
			karyoHeight: 30,
			karyoDistance: 10,
			linkKaryoDistance: 20,
			tickLabelFrequency: 10,
			tickDistance: 100,
			treeWidth: 300,
			genomeLabelWidth: 150
		},
		minLinkIdentity: 40,
		maxLinkIdentity: 100,
		midLinkIdentity: 60,
		minLinkIdentityColor: "#D21414",
		maxLinkIdentityColor: "#1DAD0A",
		midLinkIdentityColor: "#FFEE05",
		minLinkLength: 100,
		maxLinkLength: 5000,
		layout: "linear",
		tree: {
			drawTree: false,
			orientation: "left"
		},
		features: {
			showAllFeatures: false,
			supportedFeatures: {
				gen: {
					form: "rect",
					color: "#E2EDFF",
					height: 30,
					visible: false,
				},
				invertedRepeat: {
					form: "arrow",
					color: "#e7d3e2",
					height: 30,
					visible: false,
					pattern: "woven"
				},
				nStretch: {
					form: "rect",
					color: "#000000",
					height: 30,
					visible: false,
					pattern: "lines"
				},
				repeat: {
					form: "arrow",
					color: "#56cd0f",
					height: 30,
					visible: false,
					pattern: "woven"
				}
			},
			fallbackStyle: {
				form: "rect",
				color: "#787878",
				height: 30,
				visible: false
			}
		},
		labels: {
			showAllLabels: false,
			ticks: {
				showTicks: true,
				showTickLabels: true
			},
			chromosome: {
				showChromosomeLabels: true
			},
			genome: {
				showGenomeLabels: true
			},
			features: {
				showFeatureLabels: false
			}
		}
	};<commit_msg>Change also the defaultConf and make one failing test pass<commit_after>var defaultConf =  {
		linear: {
			drawAllLinks: false,
			startLineColor: "#49006a",
			endLineColor: "#1d91c0",
		},
		circular: {
			tickSize: 5
		},
		graphicalParameters: {
			width: 1000,
			height: 1000,
			karyoHeight: 30,
			karyoDistance: 10,
			linkKaryoDistance: 20,
			tickLabelFrequency: 10,
			tickDistance: 100,
			treeWidth: 300,
			genomeLabelWidth: 150
		},
		minLinkIdentity: 40,
		maxLinkIdentity: 100,
		midLinkIdentity: 60,
		minLinkIdentityColor: "#D21414",
		maxLinkIdentityColor: "#1DAD0A",
		midLinkIdentityColor: "#FFEE05",
		minLinkLength: 100,
		maxLinkLength: 5000,
		layout: "linear",
		tree: {
			drawTree: false,
			orientation: "left"
		},
		features: {
			showAllFeatures: false,
			supportedFeatures: {
				gen: {
					form: "rect",
					color: "#E2EDFF",
					height: 30,
					visible: false,
					labeling: true
				},
				invertedRepeat: {
					form: "arrow",
					color: "#e7d3e2",
					height: 30,
					visible: false,
					pattern: "woven",
					labeling: true
				},
				nStretch: {
					form: "rect",
					color: "#000000",
					height: 30,
					visible: false,
					pattern: "lines",
					labeling: true
				},
				repeat: {
					form: "arrow",
					color: "#56cd0f",
					height: 30,
					visible: false,
					pattern: "woven",
					labeling: true
				}
			},
			fallbackStyle: {
				form: "rect",
				color: "#787878",
				height: 30,
				visible: false,
				labeling: true
			}
		},
		labels: {
			showAllLabels: false,
			ticks: {
				showTicks: true,
				showTickLabels: true
			},
			chromosome: {
				showChromosomeLabels: true
			},
			genome: {
				showGenomeLabels: true
			},
			features: {
				showFeatureLabels: false
			}
		}
	};<|endoftext|>


<commit_before>var $ = require('jquery');
var cdb = require('cartodb.js');
var HistogramChartView = require('../histogram/chart');

/**
 * Time-series histogram view.
 */
module.exports = cdb.core.View.extend({
  defaults: {
    mobileThreshold: 960, // px; should match CSS media-query
    histogramChartHeight: 48 + // inline bars height
      4 + // bottom margin
      16 + // labels
      4, // margins
    histogramChartMobileHeight: 20 + // inline bars height (no bottom labels)
      4 // margins
  },

  initialize: function () {
    this._timeSeriesModel = this.options.timeSeriesModel;
    this._dataviewModel = this.options.dataviewModel;
    this._rangeFilter = this.options.rangeFilter;
    this._originalData = this._dataviewModel.getUnfilteredDataModel();
    this._initBinds();
  },

  render: function () {
    this.clearSubViews();
    this._createHistogramView();
    return this;
  },

  selectRange: function (loBarIndex, hiBarIndex) {
    this._chartView.selectRange(loBarIndex, hiBarIndex);
  },

  resetFilter: function () {
    this._rangeFilter.unsetRange();
    this._resetFilterInDI();
  },

  _initBinds: function () {
    this._dataviewModel.bind('change:data', this._onChangeData, this);
    this.listenTo(this._timeSeriesModel, 'change:normalized', this._onNormalizedChanged);
    this.listenTo(this._rangeFilter, 'change', this._onFilterChanged);
  },

  _createHistogramView: function () {
    this._chartView = this._instantiateChartView();
    this.addView(this._chartView);
    this.$el.append(this._chartView.render().el);
    this._chartView.show();

    this._chartView.bind('on_brush_end', this._onBrushEnd, this);
    this._chartView.bind('on_reset_filter', this.resetFilter, this);
    this._chartView.model.bind('change:width', this._onChangeChartWidth, this);
    this.add_related_model(this._chartView.model);
  },

  _instantiateChartView: function () {
    return new HistogramChartView({
      type: this._getChartType(),
      chartBarColor: this._timeSeriesModel.getWidgetColor() || '#F2CC8F',
      animationSpeed: 100,
      margin: {
        top: 4,
        right: 4,
        bottom: 4,
        left: 4
      },
      hasHandles: true,
      handleWidth: 10,
      hasAxisTip: true,
      animationBarDelay: function (d, i) {
        return (i * 3);
      },
      height: this.defaults.histogramChartHeight,
      dataviewModel: this._dataviewModel,
      data: this._dataviewModel.getData(),
      originalData: this._originalData,
      displayShadowBars: !this._timeSeriesModel.get('normalized'),
      normalized: !!this._timeSeriesModel.get('normalized'),
      widgetModel: this._timeSeriesModel
    });
  },

  _getChartType: function () {
    return 'time-' + this._dataviewModel.getColumnType();
  },

  _onChangeData: function () {
    if (this._chartView) {
      this._chartView.replaceData(this._dataviewModel.getData());
      this._chartView.updateXScale();
      this._chartView.updateYScale();
    }
  },

  _onBrushEnd: function (loBarIndex, hiBarIndex) {
    var data = this._dataviewModel.getData();
    this._rangeFilter.setRange(
      data[loBarIndex].start,
      data[hiBarIndex - 1].end
    );
    this._timeSeriesModel.set({lo_index: loBarIndex, hi_index: hiBarIndex});
  },

  _onChangeChartWidth: function () {
    var isMobileSize = $(window).width() < this.defaults.mobileThreshold;

    this._chartView.toggleLabels(!isMobileSize);

    var height = isMobileSize
      ? this.defaults.histogramChartMobileHeight
      : this.defaults.histogramChartHeight;
    this._chartView.model.set('height', height);
  },

  _onNormalizedChanged: function () {
    if (this._chartView) {
      this._chartView.setNormalized(this._timeSeriesModel.get('normalized'));
    }
  },

  _resetFilterInDI: function () {
    this._timeSeriesModel.set({
      min: undefined,
      max: undefined,
      lo_index: undefined,
      hi_index: undefined
    }, { silent: true });
    this._chartView.removeSelection();
  },

  _onFilterChanged: function () {
    if (!this._rangeFilter.has('min') && !this._rangeFilter.has('max')) {
      this._resetFilterInDI();
    }
  }
});
<commit_msg>Reset filter when column is changed<commit_after>var $ = require('jquery');
var cdb = require('cartodb.js');
var HistogramChartView = require('../histogram/chart');

/**
 * Time-series histogram view.
 */
module.exports = cdb.core.View.extend({
  defaults: {
    mobileThreshold: 960, // px; should match CSS media-query
    histogramChartHeight: 48 + // inline bars height
      4 + // bottom margin
      16 + // labels
      4, // margins
    histogramChartMobileHeight: 20 + // inline bars height (no bottom labels)
      4 // margins
  },

  initialize: function () {
    this._timeSeriesModel = this.options.timeSeriesModel;
    this._dataviewModel = this.options.dataviewModel;
    this._rangeFilter = this.options.rangeFilter;
    this._originalData = this._dataviewModel.getUnfilteredDataModel();
    this._initBinds();
  },

  render: function () {
    this.clearSubViews();
    this._createHistogramView();
    return this;
  },

  selectRange: function (loBarIndex, hiBarIndex) {
    this._chartView.selectRange(loBarIndex, hiBarIndex);
  },

  resetFilter: function () {
    this._rangeFilter.unsetRange();
    this._resetFilterInDI();
  },

  _initBinds: function () {
    this.listenTo(this._dataviewModel, 'change:data', this._onChangeData, this);
    this.listenTo(this._dataviewModel, 'change:column', this.resetFilter, this);
    this.listenTo(this._timeSeriesModel, 'change:normalized', this._onNormalizedChanged);
    this.listenTo(this._rangeFilter, 'change', this._onFilterChanged);
  },

  _createHistogramView: function () {
    this._chartView = this._instantiateChartView();
    this.addView(this._chartView);
    this.$el.append(this._chartView.render().el);
    this._chartView.show();

    this.listenTo(this._chartView, 'on_brush_end', this._onBrushEnd, this);
    this.listenTo(this._chartView, 'on_reset_filter', this.resetFilter, this);
    this.listenTo(this._chartView, 'change:width', this._onChangeChartWidth, this);
  },

  _instantiateChartView: function () {
    return new HistogramChartView({
      type: this._getChartType(),
      chartBarColor: this._timeSeriesModel.getWidgetColor() || '#F2CC8F',
      animationSpeed: 100,
      margin: {
        top: 4,
        right: 4,
        bottom: 4,
        left: 4
      },
      hasHandles: true,
      handleWidth: 10,
      hasAxisTip: true,
      animationBarDelay: function (d, i) {
        return (i * 3);
      },
      height: this.defaults.histogramChartHeight,
      dataviewModel: this._dataviewModel,
      data: this._dataviewModel.getData(),
      originalData: this._originalData,
      displayShadowBars: !this._timeSeriesModel.get('normalized'),
      normalized: !!this._timeSeriesModel.get('normalized'),
      widgetModel: this._timeSeriesModel
    });
  },

  _getChartType: function () {
    return 'time-' + this._dataviewModel.getColumnType();
  },

  _onChangeData: function () {
    if (this._chartView) {
      this._chartView.replaceData(this._dataviewModel.getData());
      this._chartView.updateXScale();
      this._chartView.updateYScale();
    }
  },

  _onBrushEnd: function (loBarIndex, hiBarIndex) {
    var data = this._dataviewModel.getData();
    this._rangeFilter.setRange(
      data[loBarIndex].start,
      data[hiBarIndex - 1].end
    );
    this._timeSeriesModel.set({lo_index: loBarIndex, hi_index: hiBarIndex});
  },

  _onChangeChartWidth: function () {
    var isMobileSize = $(window).width() < this.defaults.mobileThreshold;

    this._chartView.toggleLabels(!isMobileSize);

    var height = isMobileSize
      ? this.defaults.histogramChartMobileHeight
      : this.defaults.histogramChartHeight;
    this._chartView.model.set('height', height);
  },

  _onNormalizedChanged: function () {
    if (this._chartView) {
      this._chartView.setNormalized(this._timeSeriesModel.get('normalized'));
    }
  },

  _resetFilterInDI: function () {
    this._timeSeriesModel.set({
      min: undefined,
      max: undefined,
      lo_index: undefined,
      hi_index: undefined
    }, { silent: true });
    this._chartView.removeSelection();
  },

  _onFilterChanged: function () {
    if (!this._rangeFilter.has('min') && !this._rangeFilter.has('max')) {
      this._resetFilterInDI();
    }
  }
});
<|endoftext|>


<commit_before>/* global tabris:false debugClient:false WebSocket: false */
(function() {

  const FLUSH_THROTTLE_INTERVAL = 100;
  const AUTO_RECONNECT_INTERVAL = 2000;
  const MAX_RECONNECT_ATTEMPTS = 5;
  const CODE_NORMAL_CLOSURE = 1000;
  const CODE_OUTDATED_CONNECTION_CLOSURE = 4900;
  const NORMAL_CLOSURE_MESSAGE = 'Connection to debug websocket was closed.';
  const CONNECTION_PROBLEM_MESSAGE = 'Connection to debug websocket could not be established.';
  const messageTypes = Object.freeze({
    evaluate: 'evaluate',
    reloadApp: 'reload-app',
    toggleDevToolbar: 'toggle-dev-toolbar',
    clearStorage: 'clear-storage',
    loadStorage: 'load-storage',
    requestStorage: 'request-storage',
    printStorage: 'print-storage',
    printUiTree: 'print-ui-tree'
  });

  debugClient.RemoteConsole = class RemoteConsole {

    /**
     * @param {{createWebSocket: () => WebSocket}} webSocketFactory
     */
    constructor(webSocketFactory) {
      this._webSocketFactory = webSocketFactory;
      /** @type {WebSocket} */
      this._webSocket = null;
      this._reconnectScheduled = false;
      this._reconnectAttempts = 0;
      this._buffer = [];
      this._isDisposed = false;
      this._connect();
      this._throttledFlush = throttle(this._flush, FLUSH_THROTTLE_INTERVAL, {leading: false, trailing: true});
    }

    log(data) {
      this._sendBuffered('log', {level: 'log', message: data});
    }

    info(data) {
      this._sendBuffered('log', {level: 'info', message: data});
    }

    error(data) {
      this._sendBuffered('log', {level: 'error', message: data});
    }

    warn(data) {
      this._sendBuffered('log', {level: 'warn', message: data});
    }

    debug(data) {
      this._sendBuffered('log', {level: 'debug', message: data});
    }

    message(data) {
      this._sendBuffered('log', {level: 'message', message: data});
    }

    returnValue(data) {
      this._sendBuffered('log', {level: 'returnValue', message: data});
    }

    logRequest(entry) {
      this._sendBuffered('log-request', entry);
    }

    dispose() {
      this._disposeSocket();
      this._buffer = null;
      this._isDisposed = true;
    }

    _connect() {
      if (this._isConnectionOpen() || this._isDisposed) {
        return;
      }
      this._webSocket = this._webSocketFactory.createWebSocket();
      this._webSocket.onopen = () => this._handleSockedOpen();
      this._webSocket.onmessage = event => this._handleServerMessage(event);
      this._webSocket.onclose = event => this._handleSocketClosed(event);
    }

    _scheduleReconnect() {
      if (this._isDisposed || this._reconnectScheduled) {
        return;
      }
      if (++this._reconnectAttempts > MAX_RECONNECT_ATTEMPTS) {
        console.info(CONNECTION_PROBLEM_MESSAGE);
        this.dispose();
        return;
      }
      this._disposeSocket();
      this._reconnectScheduled = true;
      setTimeout(() => {
        this._reconnectScheduled = false;
        this._connect();
      }, AUTO_RECONNECT_INTERVAL);
    }

    _handleSockedOpen() {
      const success = this._sendImmediate('connect', {
        platform: tabris.device.platform,
        model: tabris.device.model
      });
      if (success) {
        this._reconnectAttempts = 0;
      }
    }

    /**
     * @param {MessageEvent} event
     */
    _handleServerMessage(event) {
      let message;
      try {
        message = JSON.parse(event.data);
      } catch(e) {
        throw new Error('Error parsing server message: ' + e);
      }
      if (message.type === messageTypes.evaluate) {
        this._evaluate(message.value);
      } else if (message.type === messageTypes.reloadApp) {
        tabris.app.reload();
      } else if (message.type === messageTypes.toggleDevToolbar) {
        if (typeof tabris.devTools !== 'object') {
          this.message(
            `Toggling UI toolbar is supported from Tabris.js 3.4.0. You are using version ${tabris.version}.`
          );
          return;
        }
        tabris.devTools.isUiVisible() ? tabris.devTools.hideUi() : tabris.devTools.showUi();
      } else if (message.type === messageTypes.printUiTree) {
        console.dirxml(tabris.drawer);
        console.dirxml(tabris.contentView);
      } else if (message.type === messageTypes.clearStorage) {
        tabris.localStorage.clear();
        if (tabris.device.platform === 'iOS') {
          tabris.secureStorage.clear();
        }
      } else if (message.type === messageTypes.requestStorage) {
        this._sendStorage();
      } else if (message.type === messageTypes.loadStorage) {
        this._loadStorage(message.value);
      } else if (message.type === messageTypes.printStorage) {
        if (tabris.device.platform === 'iOS') {
          try {
            tabris.secureStorage.key(0); // will crash in Tabris.js <3.4, see eclipsesource/tabris-js/issues/2017
            console.info('SecureStorage:');
            console.dirxml(tabris.secureStorage);
            console.log();
            console.info('LocalStorage:');
          } catch(e) {}
        }
        console.dirxml(tabris.localStorage);
      } else {
        throw new Error('Server message not supported.');
      }
    }

    _loadStorage({storage, path}) {
      const hasValidPlatform = storage.platform && typeof storage.platform === 'string';
      if (hasValidPlatform && storage.platform.toLowerCase() !== tabris.device.platform.toLowerCase()) {
        this.message(`Cannot load storage from ${path} . The storage platform does not match the device platform.`);
        return;
      }
      this._replaceStorage('localStorage', storage.localStorage, path);
      this._replaceStorage('secureStorage', storage.secureStorage, path);
    }

    _replaceStorage(storageName, contents, path) {
      const storage = tabris[storageName];
      if (typeof storage !== 'object' || typeof storage.clear !== 'function') {
        return;
      }
      storage.clear();
      for (const [key, value] of Object.entries(contents)) {
        storage.setItem(key, value);
      }
      this.message(`Loaded ${storageName} successfully from ${path} .`);
    }

    _sendStorage() {
      const storage = {
        platform: tabris.device.platform,
        localStorage: this._serializeStorage(tabris.localStorage)
      };
      if (tabris.device.platform === 'iOS') {
        storage.secureStorage = this._serializeStorage(tabris.secureStorage);
      }
      this._sendBuffered('storage', storage);
    }

    _serializeStorage(storage) {
      const result = {};
      let storageLength;
      try {
        storageLength = storage.length;
      } catch(e) {
        // TODO: eclipsesource/tabris-js/issues/2017
        return result;
      }
      for (let i = 0; i < storageLength; i++) {
        const key = storage.key(i);
        result[key] = storage.getItem(key);
      }
      return result;
    }

    _evaluate(command) {
      try {
        const geval = eval;
        const result = geval(command);
        // VT100 escape code for grey color
        this.returnValue(tabris.format(result));
      } catch (ex) {
        if (ex instanceof Error) {
          const stackStr = ex.toString();
          const stack = stackStr.split('\n');
          const cutOff = stack.findIndex(line => line.indexOf('_handleServerMessage') !== -1);
          if (cutOff > 0) {
            console.warn(stack.slice(0, cutOff).join('\n'));
          } else {
            console.warn(stackStr);
          }
        } else {
          console.warn('Non-Error thrown:');
          console.warn(ex);
        }
      } finally {
        this._sendBuffered('action-response', {enablePrompt: true});
      }
    }

    /**
     * @param {CloseEvent} event
     */
    _handleSocketClosed(event) {
      if (event.code === CODE_NORMAL_CLOSURE || event.code === CODE_OUTDATED_CONNECTION_CLOSURE) {
        console.info(NORMAL_CLOSURE_MESSAGE);
      } else {
        this._scheduleReconnect();
      }
    }

    /**
     * @param {string} type
     * @param {any} parameter
     */
    _sendBuffered(type, parameter) {
      if (this._isDisposed) {
        return;
      }
      this._buffer.push({type, parameter});
      this._throttledFlush();
    }

    /**
     * @param {string} type
     * @param {any} parameter
     */
    _sendImmediate(type, parameter) {
      return this._sendMessages([{type, parameter}]);
    }

    _flush() {
      const success = this._sendMessages(this._buffer);
      if (success) {
        this._buffer = [];
      }
      return success;
    }

    /**
     * @param {{type: string, parameter: any}[]} messages
     */
    _sendMessages(messages) {
      this._disposeCheck();
      if (!this._isConnectionOpen()) {
        return false;
      }
      try {
        this._webSocket.send(JSON.stringify(messages));
        return true;
      } catch (ex) {
        console.warn(ex);
        this._scheduleReconnect();
      }
      return false;
    }

    _isConnectionOpen() {
      return this._webSocket && this._webSocket.readyState === WebSocket.OPEN;
    }

    _disposeSocket() {
      if (this._webSocket) {
        this._webSocket.onclose = null;
        this._webSocket.onopen = null;
        this._webSocket.onmessage = null;
        this._webSocket.close();
        this._webSocket = null;
      }
    }

    _disposeCheck() {
      if (this._isDisposed) {
        throw new Error('RemoteConsole is disposed');
      }
    }

  };

  // Based on the Underscore.js implementation of throttle.
  // Source: https://stackoverflow.com/a/27078401
  /**
   *
   * @template {Function} T
   * @param {T} func
   * @param {number} wait
   * @param {{leading: boolean, trailing: boolean}} options
   * @returns {T}
   */
  function throttle(func, wait, options) {
    let context, args, result;
    let timeout = null;
    let previous = 0;
    if (!options) {
      options = {};
    }
    const later = function() {
      previous = options.leading === false ? 0 : Date.now();
      timeout = null;
      result = func.apply(context, args);
      if (!timeout) {
        context = args = null;
      }
    };
    return function() {
      const now = Date.now();
      if (!previous && options.leading === false) {
        previous = now;
      }
      const remaining = wait - (now - previous);
      context = this;
      args = arguments;
      if (remaining <= 0 || remaining > wait) {
        if (timeout) {
          clearTimeout(timeout);
          timeout = null;
        }
        previous = now;
        result = func.apply(context, args);
        if (!timeout) {
          context = args = null;
        }
      } else if (!timeout && options.trailing !== false) {
        timeout = setTimeout(later, remaining);
      }
      return result;
    };
  }

})();
<commit_msg>Fix unhandled RemoteConsole disposal error<commit_after>/* global tabris:false debugClient:false WebSocket: false */
(function() {

  const FLUSH_THROTTLE_INTERVAL = 100;
  const AUTO_RECONNECT_INTERVAL = 2000;
  const MAX_RECONNECT_ATTEMPTS = 5;
  const CODE_NORMAL_CLOSURE = 1000;
  const CODE_OUTDATED_CONNECTION_CLOSURE = 4900;
  const NORMAL_CLOSURE_MESSAGE = 'Connection to debug websocket was closed.';
  const CONNECTION_PROBLEM_MESSAGE = 'Connection to debug websocket could not be established.';
  const messageTypes = Object.freeze({
    evaluate: 'evaluate',
    reloadApp: 'reload-app',
    toggleDevToolbar: 'toggle-dev-toolbar',
    clearStorage: 'clear-storage',
    loadStorage: 'load-storage',
    requestStorage: 'request-storage',
    printStorage: 'print-storage',
    printUiTree: 'print-ui-tree'
  });

  debugClient.RemoteConsole = class RemoteConsole {

    /**
     * @param {{createWebSocket: () => WebSocket}} webSocketFactory
     */
    constructor(webSocketFactory) {
      this._webSocketFactory = webSocketFactory;
      /** @type {WebSocket} */
      this._webSocket = null;
      this._reconnectScheduled = false;
      this._reconnectAttempts = 0;
      this._buffer = [];
      this._isDisposed = false;
      this._connect();
      this._throttledFlush = throttle(this._flush, FLUSH_THROTTLE_INTERVAL, {leading: false, trailing: true});
    }

    log(data) {
      this._sendBuffered('log', {level: 'log', message: data});
    }

    info(data) {
      this._sendBuffered('log', {level: 'info', message: data});
    }

    error(data) {
      this._sendBuffered('log', {level: 'error', message: data});
    }

    warn(data) {
      this._sendBuffered('log', {level: 'warn', message: data});
    }

    debug(data) {
      this._sendBuffered('log', {level: 'debug', message: data});
    }

    message(data) {
      this._sendBuffered('log', {level: 'message', message: data});
    }

    returnValue(data) {
      this._sendBuffered('log', {level: 'returnValue', message: data});
    }

    logRequest(entry) {
      this._sendBuffered('log-request', entry);
    }

    dispose() {
      this._disposeSocket();
      this._buffer = null;
      this._isDisposed = true;
    }

    _connect() {
      if (this._isConnectionOpen() || this._isDisposed) {
        return;
      }
      this._webSocket = this._webSocketFactory.createWebSocket();
      this._webSocket.onopen = () => this._handleSockedOpen();
      this._webSocket.onmessage = event => this._handleServerMessage(event);
      this._webSocket.onclose = event => this._handleSocketClosed(event);
    }

    _scheduleReconnect() {
      if (this._isDisposed || this._reconnectScheduled) {
        return;
      }
      if (++this._reconnectAttempts > MAX_RECONNECT_ATTEMPTS) {
        console.info(CONNECTION_PROBLEM_MESSAGE);
        this.dispose();
        return;
      }
      this._disposeSocket();
      this._reconnectScheduled = true;
      setTimeout(() => {
        this._reconnectScheduled = false;
        this._connect();
      }, AUTO_RECONNECT_INTERVAL);
    }

    _handleSockedOpen() {
      const success = this._sendImmediate('connect', {
        platform: tabris.device.platform,
        model: tabris.device.model
      });
      if (success) {
        this._reconnectAttempts = 0;
      }
    }

    /**
     * @param {MessageEvent} event
     */
    _handleServerMessage(event) {
      let message;
      try {
        message = JSON.parse(event.data);
      } catch(e) {
        throw new Error('Error parsing server message: ' + e);
      }
      if (message.type === messageTypes.evaluate) {
        this._evaluate(message.value);
      } else if (message.type === messageTypes.reloadApp) {
        tabris.app.reload();
      } else if (message.type === messageTypes.toggleDevToolbar) {
        if (typeof tabris.devTools !== 'object') {
          this.message(
            `Toggling UI toolbar is supported from Tabris.js 3.4.0. You are using version ${tabris.version}.`
          );
          return;
        }
        tabris.devTools.isUiVisible() ? tabris.devTools.hideUi() : tabris.devTools.showUi();
      } else if (message.type === messageTypes.printUiTree) {
        console.dirxml(tabris.drawer);
        console.dirxml(tabris.contentView);
      } else if (message.type === messageTypes.clearStorage) {
        tabris.localStorage.clear();
        if (tabris.device.platform === 'iOS') {
          tabris.secureStorage.clear();
        }
      } else if (message.type === messageTypes.requestStorage) {
        this._sendStorage();
      } else if (message.type === messageTypes.loadStorage) {
        this._loadStorage(message.value);
      } else if (message.type === messageTypes.printStorage) {
        if (tabris.device.platform === 'iOS') {
          try {
            tabris.secureStorage.key(0); // will crash in Tabris.js <3.4, see eclipsesource/tabris-js/issues/2017
            console.info('SecureStorage:');
            console.dirxml(tabris.secureStorage);
            console.log();
            console.info('LocalStorage:');
          } catch(e) {}
        }
        console.dirxml(tabris.localStorage);
      } else {
        throw new Error('Server message not supported.');
      }
    }

    _loadStorage({storage, path}) {
      const hasValidPlatform = storage.platform && typeof storage.platform === 'string';
      if (hasValidPlatform && storage.platform.toLowerCase() !== tabris.device.platform.toLowerCase()) {
        this.message(`Cannot load storage from ${path} . The storage platform does not match the device platform.`);
        return;
      }
      this._replaceStorage('localStorage', storage.localStorage, path);
      this._replaceStorage('secureStorage', storage.secureStorage, path);
    }

    _replaceStorage(storageName, contents, path) {
      const storage = tabris[storageName];
      if (typeof storage !== 'object' || typeof storage.clear !== 'function') {
        return;
      }
      storage.clear();
      for (const [key, value] of Object.entries(contents)) {
        storage.setItem(key, value);
      }
      this.message(`Loaded ${storageName} successfully from ${path} .`);
    }

    _sendStorage() {
      const storage = {
        platform: tabris.device.platform,
        localStorage: this._serializeStorage(tabris.localStorage)
      };
      if (tabris.device.platform === 'iOS') {
        storage.secureStorage = this._serializeStorage(tabris.secureStorage);
      }
      this._sendBuffered('storage', storage);
    }

    _serializeStorage(storage) {
      const result = {};
      let storageLength;
      try {
        storageLength = storage.length;
      } catch(e) {
        // TODO: eclipsesource/tabris-js/issues/2017
        return result;
      }
      for (let i = 0; i < storageLength; i++) {
        const key = storage.key(i);
        result[key] = storage.getItem(key);
      }
      return result;
    }

    _evaluate(command) {
      try {
        const geval = eval;
        const result = geval(command);
        // VT100 escape code for grey color
        this.returnValue(tabris.format(result));
      } catch (ex) {
        if (ex instanceof Error) {
          const stackStr = ex.toString();
          const stack = stackStr.split('\n');
          const cutOff = stack.findIndex(line => line.indexOf('_handleServerMessage') !== -1);
          if (cutOff > 0) {
            console.warn(stack.slice(0, cutOff).join('\n'));
          } else {
            console.warn(stackStr);
          }
        } else {
          console.warn('Non-Error thrown:');
          console.warn(ex);
        }
      } finally {
        this._sendBuffered('action-response', {enablePrompt: true});
      }
    }

    /**
     * @param {CloseEvent} event
     */
    _handleSocketClosed(event) {
      if (event.code === CODE_NORMAL_CLOSURE || event.code === CODE_OUTDATED_CONNECTION_CLOSURE) {
        console.info(NORMAL_CLOSURE_MESSAGE);
      } else {
        this._scheduleReconnect();
      }
    }

    /**
     * @param {string} type
     * @param {any} parameter
     */
    _sendBuffered(type, parameter) {
      if (this._isDisposed) {
        return;
      }
      this._buffer.push({type, parameter});
      this._throttledFlush();
    }

    /**
     * @param {string} type
     * @param {any} parameter
     */
    _sendImmediate(type, parameter) {
      return this._sendMessages([{type, parameter}]);
    }

    _flush() {
      const success = this._sendMessages(this._buffer);
      if (success) {
        this._buffer = [];
      }
      return success;
    }

    /**
     * @param {{type: string, parameter: any}[]} messages
     */
    _sendMessages(messages) {
      if (this._isDisposed) {
        return false;
      }
      if (!this._isConnectionOpen()) {
        return false;
      }
      try {
        this._webSocket.send(JSON.stringify(messages));
        return true;
      } catch (ex) {
        console.warn(ex);
        this._scheduleReconnect();
      }
      return false;
    }

    _isConnectionOpen() {
      return this._webSocket && this._webSocket.readyState === WebSocket.OPEN;
    }

    _disposeSocket() {
      if (this._webSocket) {
        this._webSocket.onclose = null;
        this._webSocket.onopen = null;
        this._webSocket.onmessage = null;
        this._webSocket.close();
        this._webSocket = null;
      }
    }

  };

  // Based on the Underscore.js implementation of throttle.
  // Source: https://stackoverflow.com/a/27078401
  /**
   *
   * @template {Function} T
   * @param {T} func
   * @param {number} wait
   * @param {{leading: boolean, trailing: boolean}} options
   * @returns {T}
   */
  function throttle(func, wait, options) {
    let context, args, result;
    let timeout = null;
    let previous = 0;
    if (!options) {
      options = {};
    }
    const later = function() {
      previous = options.leading === false ? 0 : Date.now();
      timeout = null;
      result = func.apply(context, args);
      if (!timeout) {
        context = args = null;
      }
    };
    return function() {
      const now = Date.now();
      if (!previous && options.leading === false) {
        previous = now;
      }
      const remaining = wait - (now - previous);
      context = this;
      args = arguments;
      if (remaining <= 0 || remaining > wait) {
        if (timeout) {
          clearTimeout(timeout);
          timeout = null;
        }
        previous = now;
        result = func.apply(context, args);
        if (!timeout) {
          context = args = null;
        }
      } else if (!timeout && options.trailing !== false) {
        timeout = setTimeout(later, remaining);
      }
      return result;
    };
  }

})();
<|endoftext|>


<commit_before>import './bootstrap'

import Map from './map'
import Pjax from './pjax'
import YandexMetrika from './yandex-metrika'

import './events'
import './highlight'
import './life'
import './nav-reveal'
import './shortcuts'
import './yandex-dns'

let throttle = require('lodash/throttle')

class Application {
  constructor() {
    const options = window['AppOptions']

    this.locale = options.locale
    this.map = new Map(this.locale)
    this.metrika = new YandexMetrika(options.yandexMetrikaId)
    this.pjax = new Pjax()

    this.ajaxProgress()
    this.csrfToken()
    this.onPjaxComplete()
    this.onPjaxSend()

    $(() => {
      this.initOnReadyAndPjax()
      this.lazyLoadImages()
    })
  }

  ajaxProgress() {}

  autosizeTextareas() {
    autosize($('.js-autosize-textarea'))
  }

  csrfToken() {
    $.ajaxSetup({
      headers: { 'X-CSRF-TOKEN': window['AppOptions'].csrfToken }
    })
  }

  initVue() {
    new Vue({
      el: '#pjax_container'
    })
  }

  lazyLoadImages() {
    const offset = 1000
    const breakpoint = 1200

    let $w = $(window)
    let $body = $(document.body)
    let $images

    const width = $w.width()

    function initLazyLoad() {
      $images = $('.js-lazy')

      $w.off('.js-lazy')
        .on('scroll.js-lazy resize.js-lazy', throttle(performLazyLoad, 500))

      performLazyLoad()
    }

    function performLazyLoad() {
      const scrolled_down = $w.scrollTop() + $w.height() + offset
      const scrolled_up = $w.scrollTop() - offset

      $images = $images.filter(function() {
        let e = $(this)
        let type = $(this).data('lazy-type') || 'image'
        const image_offset = e.offset().top

        if (image_offset < scrolled_down && image_offset > scrolled_up) {
          e.removeClass('js-lazy')

          if (type === 'image') {
            const src = width > breakpoint ? (e.data('src2x') || e.data('src')) : e.data('src')
            e.attr('src', src)
          } else if (type === 'fotorama') {
            e.fotorama()
          } else if (type === 'fotorama-2x') {
            if (width > breakpoint) {
              $('a', e).each(function() {
                $(this).attr('href', $(this).data('src2x'))
              })
            }

            e.fotorama()
          }

          return false
        }

        return true
      })

      if (!$images.length) {
        $w.off('.js-lazy')
      }
    }

    $body.off('reset.js-lazy').on('reset.js-lazy', initLazyLoad)

    initLazyLoad()
  }

  initOnReadyAndPjax(pjax = false) {
    this.initVue()
    this.autosizeTextareas()
  }

  onPjaxComplete() {
    $(document).on('pjax:complete', () => {
      $('.js-flash-notification .alert').alert('close')

      this.metrika.pjaxHit()
      this.pjax.onComplete()

      this.initOnReadyAndPjax(true)

      // Нужно вызывать после this.initVue(), иначе
      // перестает работать метод offset() на картинках
      $(document.body).trigger('reset.js-lazy')
    })
  }

  onPjaxSend() {
    $(document).on('pjax:send', (e) => this.pjax.onSend(e))
  }
}

window.App = new Application
<commit_msg>Заготовка для обработчика ошибок<commit_after>import './bootstrap'

import Map from './map'
import Pjax from './pjax'
import YandexMetrika from './yandex-metrika'

import './events'
import './highlight'
import './life'
import './nav-reveal'
import './shortcuts'
import './yandex-dns'

let throttle = require('lodash/throttle')

class Application {
  constructor() {
    const options = window['AppOptions']

    this.locale = options.locale
    this.map = new Map(this.locale)
    this.metrika = new YandexMetrika(options.yandexMetrikaId)
    this.pjax = new Pjax()

    this.ajaxProgress()
    this.csrfToken()
    this.onPjaxComplete()
    this.onPjaxSend()

    $(() => {
      this.initOnReadyAndPjax()
      this.lazyLoadImages()
    })
  }

  ajaxProgress() {}

  autosizeTextareas() {
    autosize($('.js-autosize-textarea'))
  }

  csrfToken() {
    $.ajaxSetup({
      headers: { 'X-CSRF-TOKEN': window['AppOptions'].csrfToken }
    })
  }

  /*
  errorHandler() {
    window.addEventListener('error', function (e) {
      console.log(e)
    })
  }
  */

  initVue() {
    new Vue({
      el: '#pjax_container'
    })
  }

  lazyLoadImages() {
    const offset = 1000
    const breakpoint = 1200

    let $w = $(window)
    let $body = $(document.body)
    let $images

    const width = $w.width()

    function initLazyLoad() {
      $images = $('.js-lazy')

      $w.off('.js-lazy')
        .on('scroll.js-lazy resize.js-lazy', throttle(performLazyLoad, 500))

      performLazyLoad()
    }

    function performLazyLoad() {
      const scrolled_down = $w.scrollTop() + $w.height() + offset
      const scrolled_up = $w.scrollTop() - offset

      $images = $images.filter(function() {
        let e = $(this)
        let type = $(this).data('lazy-type') || 'image'
        const image_offset = e.offset().top

        if (image_offset < scrolled_down && image_offset > scrolled_up) {
          e.removeClass('js-lazy')

          if (type === 'image') {
            const src = width > breakpoint ? (e.data('src2x') || e.data('src')) : e.data('src')
            e.attr('src', src)
          } else if (type === 'fotorama') {
            e.fotorama()
          } else if (type === 'fotorama-2x') {
            if (width > breakpoint) {
              $('a', e).each(function() {
                $(this).attr('href', $(this).data('src2x'))
              })
            }

            e.fotorama()
          }

          return false
        }

        return true
      })

      if (!$images.length) {
        $w.off('.js-lazy')
      }
    }

    $body.off('reset.js-lazy').on('reset.js-lazy', initLazyLoad)

    initLazyLoad()
  }

  initOnReadyAndPjax(pjax = false) {
    this.initVue()
    this.autosizeTextareas()
  }

  onPjaxComplete() {
    $(document).on('pjax:complete', () => {
      $('.js-flash-notification .alert').alert('close')

      this.metrika.pjaxHit()
      this.pjax.onComplete()

      this.initOnReadyAndPjax(true)

      // Нужно вызывать после this.initVue(), иначе
      // перестает работать метод offset() на картинках
      $(document.body).trigger('reset.js-lazy')
    })
  }

  onPjaxSend() {
    $(document).on('pjax:send', (e) => this.pjax.onSend(e))
  }
}

window.App = new Application
<|endoftext|>


<commit_before>$(function() {
    // Ajax Setup
    $.ajaxPrefilter(function(options, originalOptions, jqXHR) {
        var token;
        if (! options.crossDomain) {
            token = $('meta[name="token"]').attr('content');
            if (token) {
                jqXHR.setRequestHeader('X-CSRF-Token', token);
            }
        }

        return jqXHR;
    });

    $.ajaxSetup({
        beforeSend: function (xhr) {
            xhr.setRequestHeader('Accept', 'application/json');
            // xhr.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
        },
        statusCode: {
            401: function () {
                window.location.href = '/';
            },
            403: function () {
                window.location.href = '/';
            }
        }
    });

    // Prevent double form submission
    $('form').submit(function() {
        var $form = $(this);
        $form.find(':submit').prop('disabled', true);
    });

    // Autosizing of textareas.
    autosize($('textarea.autosize'));

    // Mock the DELETE form requests.
    $('[data-method]').not(".disabled").append(function() {
        var methodForm = "\n";
        methodForm += "<form action='" + $(this).attr('href') + "' method='POST' style='display:none'>\n";
        methodForm += "<input type='hidden' name='_method' value='" + $(this).attr('data-method') + "'>\n";
        methodForm += "<input type='hidden' name='_token' value='" + $('meta[name=token]').attr('content') + "'>\n";
        methodForm += "</form>\n";
        return methodForm;
    })
        .removeAttr('href')
        .on('click', function() {
            var button = $(this);

            if (button.hasClass('confirm-action')) {
                askConfirmation(function() {
                    button.find("form").submit();
                });
            } else {
                button.find("form").submit();
            }
        });

    // Messenger config
    Messenger.options = {
        extraClasses: 'messenger-fixed messenger-on-top',
        theme: 'air'
    };

    // App setup
    window.Cachet = {};

    moment.locale(Global.locale);

    $('abbr.timeago').each(function () {
        var $el = $(this);
        $el
            .livestamp($el.data('timeago'))
            .tooltip();
    });

    window.Cachet.Notifier = function () {
        this.notify = function (message, type, options) {
            if (_.isPlainObject(message)) {
                message = message.detail;
            }
            type = (typeof type === 'undefined' || type === 'error') ? 'error' : type;

            var defaultOptions = {
                message: message,
                type: type,
                showCloseButton: true
            };

            options = _.extend(defaultOptions, options);

            Messenger().post(options);
        };
    };

    $(".sidebar-toggler").click(function(e) {
        e.preventDefault();
        $(".wrapper").toggleClass("toggled");
    });

    $('.color-code').minicolors({
        control: 'hue',
        defaultValue: $(this).val() || '',
        inline: false,
        letterCase: 'lowercase',
        opacity: false,
        position: 'bottom left',
        theme: 'bootstrap'
    });

    $('[data-toggle="tooltip"]').tooltip();

    $('button.close').on('click', function() {
        $(this).parents('div.alert').addClass('hide');
    });

    $('form[name=IncidentForm] select[name=component_id]').on('change', function() {
        var $option = $(this).find('option:selected');
        var $componentStatus = $('#component-status');

        if (parseInt($option.val(), 10) !== 0) {
            if ($componentStatus.hasClass('hidden')) {
                $componentStatus.removeClass('hidden');
            }
        } else {
            $componentStatus.addClass('hidden');
        }
    });

    // Date picker.
    $('input[rel=datepicker]').datetimepicker({
        format: "DD/MM/YYYY HH:mm",
        minDate: new Date(), // Don't allow dates before today.
        sideBySide: true,
        icons: {
            time: 'ion-clock',
            date: 'ion-android-calendar',
            up: 'ion-ios-arrow-up',
            down: 'ion-ios-arrow-down',
            previous: 'ion-ios-arrow-left',
            next: 'ion-ios-arrow-right',
            today: 'ion-android-home',
            clear: 'ion-trash-a',
        }
    });

    $('input[rel=datepicker-any]').datetimepicker({
        format: "DD/MM/YYYY HH:mm",
        sideBySide: true,
        icons: {
            time: 'ion-clock',
            date: 'ion-android-calendar',
            up: 'ion-ios-arrow-up',
            down: 'ion-ios-arrow-down',
            previous: 'ion-ios-arrow-left',
            next: 'ion-ios-arrow-right',
            today: 'ion-android-home',
            clear: 'ion-trash-a',
        }
    });

    // Sortable models.
    var orderableLists = document.querySelectorAll('[data-orderable-list]');

    $.each(orderableLists, function (k, list) {
        var url = $(list).data('orderableList');
        var notifier = new Cachet.Notifier();

        new Sortable(list, {
            group: 'omega',
            handle: '.drag-handle',
            onUpdate: function() {
                var orderedIds = $.map(list.querySelectorAll('[data-orderable-id]'), function(elem) {
                    return $(elem).data('orderable-id');
                });

                $.ajax({
                    async: true,
                    url: url,
                    type: 'POST',
                    data: {
                        ids: orderedIds
                    },
                    success: function() {
                        notifier.notify('Ordering updated.', 'success');
                    },
                    error: function() {
                        notifier.notify('Ordering not updated.', 'error');
                    }
                });
            }
        });
    });

    // Toggle inline component statuses.
    $('form.component-inline').on('click', 'input[type=radio]', function() {
        var $form = $(this).parents('form');
        var formData = $form.serializeObject();

        $.ajax({
            async: true,
            url: '/dashboard/api/components/' + formData.component_id,
            type: 'POST',
            data: formData,
            success: function(component) {
                (new Cachet.Notifier()).notify($form.data('messenger'), 'success');
            },
            error: function(a, b, c) {
                (new Cachet.Notifier()).notify('Something went wrong updating the component.');
            }
        });
    });

    // Incident management
    $('select[name=template]').on('change', function() {
        var $this = $(this).find('option:selected'),
            slug   = $this.val();

        // Only fetch the template if we've picked one.
        if (slug) {
            $.ajax({
                async: true,
                data: {
                    slug: slug
                },
                url: '/dashboard/api/incidents/templates',
                success: function(tpl) {
                    var $form = $('form[role=form]');
                    $form.find('input[name=name]').val(tpl.name);
                    $form.find('textarea[name=message]').val(tpl.template);
                },
                error: function() {
                    (new Cachet.Notifier()).notify('There was an error finding that template.');
                }
            });
        }
    });

    // Banner removal JS
    $('#remove-banner').click(function(){
        $('#banner-view').remove();
        $('input[name=remove_banner]').val('1');
    });

    $('.group-name').on('click', function () {
        var $this = $(this);

        $this.find('.group-toggle').toggleClass('ion-ios-minus-outline').toggleClass('ion-ios-plus-outline');

        $this.next('.group-items').toggleClass('hide');
    });

    $('.select-group').on('click', function () {
        var $parentGroup = $(this).closest('ul.list-group');
        $parentGroup.find('input[type=checkbox]').prop('checked', true);
        $parentGroup.find('.group-items').removeClass('hide')
        $parentGroup.find('.group-toggle').addClass('ion-ios-minus-outline').removeClass('ion-ios-plus-outline');
        event.stopPropagation();
        return false;
    });

    $('.deselect-group').on('click', function () {
        var $parentGroup = $(this).closest('ul.list-group');
        $parentGroup.find('input[type=checkbox]').prop('checked', false);
        $parentGroup.find('.group-items').addClass('hide');
        $parentGroup.find('.group-toggle').removeClass('ion-ios-minus-outline').addClass('ion-ios-plus-outline');
        event.stopPropagation();
        return false;
    });

    // Setup wizard
    $('.wizard-next').on('click', function () {
        var $form   = $('#setup-form'),
            $btn    = $(this),
            current = $btn.data('currentBlock'),
            next    = $btn.data('nextBlock');

        $btn.button('loading');

        // Only validate going forward. If current group is invalid, do not go further
        if (next > current) {
            var url = '/setup/step' + current;
            $.post(url, $form.serializeObject())
                .done(function(response) {
                    goToStep(current, next);
                })
                .fail(function(response) {
                    var errors = _.toArray(response.responseJSON.errors);
                    _.each(errors, function(error) {
                        (new Cachet.Notifier()).notify(error);
                    });
                })
                .always(function() {
                    $btn.button('reset');
                });

            return false;
        } else {
            goToStep(current, next);
            $btn.button('reset');
        }
    });

    // Sparkline
    if ($.fn.sparkline) {
        var sparkLine = function () {
            $('.sparkline').each(function () {
                var data = $(this).data();
                data.valueSpots = {
                    '0:': data.spotColor
                };

                $(this).sparkline(data.data, data);
                var composite = data.compositedata;

                if (composite) {
                    var stlColor = $(this).attr("data-stack-line-color"),
                        stfColor = $(this).attr("data-stack-fill-color"),
                        sptColor = $(this).attr("data-stack-spot-color"),
                        sptRadius = $(this).attr("data-stack-spot-radius");

                    $(this).sparkline(composite, {
                        composite: true,
                        lineColor: stlColor,
                        fillColor: stfColor,
                        spotColor: sptColor,
                        highlightSpotColor: sptColor,
                        spotRadius: sptRadius,
                        valueSpots: {
                            '0:': sptColor
                        }
                    });
                };
            });
        };

        sparkLine(false);
    }

    function goToStep(current, next) {
        // validation was ok. We can go on next step.
        $('.block-' + current)
          .removeClass('show')
          .addClass('hidden');

        $('.block-' + next)
          .removeClass('hidden')
          .addClass('show');

        $('.steps .step')
            .removeClass("active")
            .filter(":lt(" + (next) + ")")
            .addClass("active");
    }

    // Password strength
    $('.password-strength').strengthify();

    // Check for updates.
    if ($('#update-alert').length > 0) {
        $.ajax({
            async: true,
            dataType: 'json',
            url: '/api/v1/version',
        }).done(function (result) {
            if (result.meta.on_latest === false) {
                $('#update-alert').removeClass('hidden');
            }
        });
    }
});

function askConfirmation(callback, cancellCallback) {
    swal({
        type: "warning",
        title: "Confirm your action",
        text: "Are you sure you want to do this?",
        buttonsStyling: false,
        reverseButtons: true,
        confirmButtonText: "Yes",
        confirmButtonClass: "btn btn-lg btn-danger",
        cancelButtonClass: "btn btn-lg btn-default",
        showCancelButton: true,
        focusCancel: true
    }).then(() => {
        if (_.isFunction(callback)) callback();
    }, () => {
        if (_.isFunction(cancellCallback)) cancellCallback();
    });
}
<commit_msg>Don't use ES6 syntax yet<commit_after>$(function() {
    // Ajax Setup
    $.ajaxPrefilter(function(options, originalOptions, jqXHR) {
        var token;
        if (! options.crossDomain) {
            token = $('meta[name="token"]').attr('content');
            if (token) {
                jqXHR.setRequestHeader('X-CSRF-Token', token);
            }
        }

        return jqXHR;
    });

    $.ajaxSetup({
        beforeSend: function (xhr) {
            xhr.setRequestHeader('Accept', 'application/json');
            // xhr.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
        },
        statusCode: {
            401: function () {
                window.location.href = '/';
            },
            403: function () {
                window.location.href = '/';
            }
        }
    });

    // Prevent double form submission
    $('form').submit(function() {
        var $form = $(this);
        $form.find(':submit').prop('disabled', true);
    });

    // Autosizing of textareas.
    autosize($('textarea.autosize'));

    // Mock the DELETE form requests.
    $('[data-method]').not(".disabled").append(function() {
        var methodForm = "\n";
        methodForm += "<form action='" + $(this).attr('href') + "' method='POST' style='display:none'>\n";
        methodForm += "<input type='hidden' name='_method' value='" + $(this).attr('data-method') + "'>\n";
        methodForm += "<input type='hidden' name='_token' value='" + $('meta[name=token]').attr('content') + "'>\n";
        methodForm += "</form>\n";
        return methodForm;
    })
        .removeAttr('href')
        .on('click', function() {
            var button = $(this);

            if (button.hasClass('confirm-action')) {
                askConfirmation(function() {
                    button.find("form").submit();
                });
            } else {
                button.find("form").submit();
            }
        });

    // Messenger config
    Messenger.options = {
        extraClasses: 'messenger-fixed messenger-on-top',
        theme: 'air'
    };

    // App setup
    window.Cachet = {};

    moment.locale(Global.locale);

    $('abbr.timeago').each(function () {
        var $el = $(this);
        $el
            .livestamp($el.data('timeago'))
            .tooltip();
    });

    window.Cachet.Notifier = function () {
        this.notify = function (message, type, options) {
            if (_.isPlainObject(message)) {
                message = message.detail;
            }
            type = (typeof type === 'undefined' || type === 'error') ? 'error' : type;

            var defaultOptions = {
                message: message,
                type: type,
                showCloseButton: true
            };

            options = _.extend(defaultOptions, options);

            Messenger().post(options);
        };
    };

    $(".sidebar-toggler").click(function(e) {
        e.preventDefault();
        $(".wrapper").toggleClass("toggled");
    });

    $('.color-code').minicolors({
        control: 'hue',
        defaultValue: $(this).val() || '',
        inline: false,
        letterCase: 'lowercase',
        opacity: false,
        position: 'bottom left',
        theme: 'bootstrap'
    });

    $('[data-toggle="tooltip"]').tooltip();

    $('button.close').on('click', function() {
        $(this).parents('div.alert').addClass('hide');
    });

    $('form[name=IncidentForm] select[name=component_id]').on('change', function() {
        var $option = $(this).find('option:selected');
        var $componentStatus = $('#component-status');

        if (parseInt($option.val(), 10) !== 0) {
            if ($componentStatus.hasClass('hidden')) {
                $componentStatus.removeClass('hidden');
            }
        } else {
            $componentStatus.addClass('hidden');
        }
    });

    // Date picker.
    $('input[rel=datepicker]').datetimepicker({
        format: "DD/MM/YYYY HH:mm",
        minDate: new Date(), // Don't allow dates before today.
        sideBySide: true,
        icons: {
            time: 'ion-clock',
            date: 'ion-android-calendar',
            up: 'ion-ios-arrow-up',
            down: 'ion-ios-arrow-down',
            previous: 'ion-ios-arrow-left',
            next: 'ion-ios-arrow-right',
            today: 'ion-android-home',
            clear: 'ion-trash-a',
        }
    });

    $('input[rel=datepicker-any]').datetimepicker({
        format: "DD/MM/YYYY HH:mm",
        sideBySide: true,
        icons: {
            time: 'ion-clock',
            date: 'ion-android-calendar',
            up: 'ion-ios-arrow-up',
            down: 'ion-ios-arrow-down',
            previous: 'ion-ios-arrow-left',
            next: 'ion-ios-arrow-right',
            today: 'ion-android-home',
            clear: 'ion-trash-a',
        }
    });

    // Sortable models.
    var orderableLists = document.querySelectorAll('[data-orderable-list]');

    $.each(orderableLists, function (k, list) {
        var url = $(list).data('orderableList');
        var notifier = new Cachet.Notifier();

        new Sortable(list, {
            group: 'omega',
            handle: '.drag-handle',
            onUpdate: function() {
                var orderedIds = $.map(list.querySelectorAll('[data-orderable-id]'), function(elem) {
                    return $(elem).data('orderable-id');
                });

                $.ajax({
                    async: true,
                    url: url,
                    type: 'POST',
                    data: {
                        ids: orderedIds
                    },
                    success: function() {
                        notifier.notify('Ordering updated.', 'success');
                    },
                    error: function() {
                        notifier.notify('Ordering not updated.', 'error');
                    }
                });
            }
        });
    });

    // Toggle inline component statuses.
    $('form.component-inline').on('click', 'input[type=radio]', function() {
        var $form = $(this).parents('form');
        var formData = $form.serializeObject();

        $.ajax({
            async: true,
            url: '/dashboard/api/components/' + formData.component_id,
            type: 'POST',
            data: formData,
            success: function(component) {
                (new Cachet.Notifier()).notify($form.data('messenger'), 'success');
            },
            error: function(a, b, c) {
                (new Cachet.Notifier()).notify('Something went wrong updating the component.');
            }
        });
    });

    // Incident management
    $('select[name=template]').on('change', function() {
        var $this = $(this).find('option:selected'),
            slug   = $this.val();

        // Only fetch the template if we've picked one.
        if (slug) {
            $.ajax({
                async: true,
                data: {
                    slug: slug
                },
                url: '/dashboard/api/incidents/templates',
                success: function(tpl) {
                    var $form = $('form[role=form]');
                    $form.find('input[name=name]').val(tpl.name);
                    $form.find('textarea[name=message]').val(tpl.template);
                },
                error: function() {
                    (new Cachet.Notifier()).notify('There was an error finding that template.');
                }
            });
        }
    });

    // Banner removal JS
    $('#remove-banner').click(function(){
        $('#banner-view').remove();
        $('input[name=remove_banner]').val('1');
    });

    $('.group-name').on('click', function () {
        var $this = $(this);

        $this.find('.group-toggle').toggleClass('ion-ios-minus-outline').toggleClass('ion-ios-plus-outline');

        $this.next('.group-items').toggleClass('hide');
    });

    $('.select-group').on('click', function () {
        var $parentGroup = $(this).closest('ul.list-group');
        $parentGroup.find('input[type=checkbox]').prop('checked', true);
        $parentGroup.find('.group-items').removeClass('hide')
        $parentGroup.find('.group-toggle').addClass('ion-ios-minus-outline').removeClass('ion-ios-plus-outline');
        event.stopPropagation();
        return false;
    });

    $('.deselect-group').on('click', function () {
        var $parentGroup = $(this).closest('ul.list-group');
        $parentGroup.find('input[type=checkbox]').prop('checked', false);
        $parentGroup.find('.group-items').addClass('hide');
        $parentGroup.find('.group-toggle').removeClass('ion-ios-minus-outline').addClass('ion-ios-plus-outline');
        event.stopPropagation();
        return false;
    });

    // Setup wizard
    $('.wizard-next').on('click', function () {
        var $form   = $('#setup-form'),
            $btn    = $(this),
            current = $btn.data('currentBlock'),
            next    = $btn.data('nextBlock');

        $btn.button('loading');

        // Only validate going forward. If current group is invalid, do not go further
        if (next > current) {
            var url = '/setup/step' + current;
            $.post(url, $form.serializeObject())
                .done(function(response) {
                    goToStep(current, next);
                })
                .fail(function(response) {
                    var errors = _.toArray(response.responseJSON.errors);
                    _.each(errors, function(error) {
                        (new Cachet.Notifier()).notify(error);
                    });
                })
                .always(function() {
                    $btn.button('reset');
                });

            return false;
        } else {
            goToStep(current, next);
            $btn.button('reset');
        }
    });

    // Sparkline
    if ($.fn.sparkline) {
        var sparkLine = function () {
            $('.sparkline').each(function () {
                var data = $(this).data();
                data.valueSpots = {
                    '0:': data.spotColor
                };

                $(this).sparkline(data.data, data);
                var composite = data.compositedata;

                if (composite) {
                    var stlColor = $(this).attr("data-stack-line-color"),
                        stfColor = $(this).attr("data-stack-fill-color"),
                        sptColor = $(this).attr("data-stack-spot-color"),
                        sptRadius = $(this).attr("data-stack-spot-radius");

                    $(this).sparkline(composite, {
                        composite: true,
                        lineColor: stlColor,
                        fillColor: stfColor,
                        spotColor: sptColor,
                        highlightSpotColor: sptColor,
                        spotRadius: sptRadius,
                        valueSpots: {
                            '0:': sptColor
                        }
                    });
                };
            });
        };

        sparkLine(false);
    }

    function goToStep(current, next) {
        // validation was ok. We can go on next step.
        $('.block-' + current)
          .removeClass('show')
          .addClass('hidden');

        $('.block-' + next)
          .removeClass('hidden')
          .addClass('show');

        $('.steps .step')
            .removeClass("active")
            .filter(":lt(" + (next) + ")")
            .addClass("active");
    }

    // Password strength
    $('.password-strength').strengthify();

    // Check for updates.
    if ($('#update-alert').length > 0) {
        $.ajax({
            async: true,
            dataType: 'json',
            url: '/api/v1/version',
        }).done(function (result) {
            if (result.meta.on_latest === false) {
                $('#update-alert').removeClass('hidden');
            }
        });
    }
});

function askConfirmation(callback, cancelCallback) {
    swal({
        type: "warning",
        title: "Confirm your action",
        text: "Are you sure you want to do this?",
        buttonsStyling: false,
        reverseButtons: true,
        confirmButtonText: "Yes",
        confirmButtonClass: "btn btn-lg btn-danger",
        cancelButtonClass: "btn btn-lg btn-default",
        showCancelButton: true,
        focusCancel: true
    }).then(function () {
        if (_.isFunction(callback)) callback();
    }, function () {
        if (_.isFunction(cancelCallback)) cancelCallback();
    });
}
<|endoftext|>


<commit_before>/*
Copyright 2016 Rory Claasen

Licensed 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.
*/
$(document).ready(function() {
	pingDatabase();
});

function pingDatabaseForce() {
	Cookies.remove('database-ping');
	pingDatabase();
}

function pingDatabase() {
	if (Cookies.get('database-ping') == undefined || Cookies.get('database-ping') == 'false' || Cookies.get('database-ping') == 'sent') {
		notif('info', 'Database', 'Ping!');
		setTimeout(function() {
			console.log("[JAVASCRIPT] Server ping sending, will be waking up from idle");
			Cookies.set('database-ping', 'sent', { expires: 1 });
			$.ajax({
				url: "http://database.roryclaasen.me/ping.php",
				dataType: "text",
				statusCode: {
					200: function (response) {
						Cookies.set('database-ping', 'true', { expires: 1 });
						console.log("[JAVASCRIPT] Server pong received");
						// iziToast.destroy();
						notif('success', 'Database', 'Received');
					},
					404: function (response) {
						Cookies.set('database-ping', 'false', { expires: 1 });
						console.log("[JAVASCRIPT] Server pong NOT received");
						// iziToast.destroy();
						notif('warning', 'Database', 'Failed');
					}
				}
			});
		}, 1000);
	}
}

$(window).bind('scroll', function () {
	if ($(window).scrollTop() > 24) {
		$('#navigation').addClass('fixed');
		$('div.content').addClass('offset');
	} else {
		$('#navigation').removeClass('fixed');
		$('div.content').removeClass('offset');
	}
});

function notif(type, title, message) {
	var defaultMap = {
		position: 'bottomLeft',
		title: title,
		message: message,
		zindex: 99999,
		transitionOut: 'flipOutX',
	};
	console.log("[JAVASCRIPT] Showing notification");
	console.log(defaultMap);
	switch(type) {
		case "success": {
			iziToast.success(defaultMap);
			break;
		}
		case "warning": {
			iziToast.warning(defaultMap);
			break;
		}
		case "error": {
			iziToast.error(defaultMap);
			break;
		}
		default: {
			iziToast.info(defaultMap);
			break;
		}
	}
}
<commit_msg>hiding infomatiom<commit_after>/*
Copyright 2016 Rory Claasen

Licensed 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.
*/
$(document).ready(function() {
	pingDatabase();
});

function pingDatabaseForce() {
	Cookies.remove('database-ping');
	pingDatabase();
}

function pingDatabase() {
	if (Cookies.get('database-ping') == undefined || Cookies.get('database-ping') == 'false' || Cookies.get('database-ping') == 'sent') {
		notif('info', 'Database', 'Ping!');
		setTimeout(function() {
			console.log("[JAVASCRIPT] Server ping sending, will be waking up from idle");
			Cookies.set('database-ping', 'sent', { expires: 1 });
			$.ajax({
				url: "http://database.roryclaasen.me/ping.php",
				dataType: "text",
				statusCode: {
					200: function (response) {
						Cookies.set('database-ping', 'true', { expires: 1 });
						console.log("[JAVASCRIPT] Server pong received");
						// iziToast.destroy();
						notif('success', 'Database', 'Received');
					},
					404: function (response) {
						Cookies.set('database-ping', 'false', { expires: 1 });
						console.log("[JAVASCRIPT] Server pong NOT received");
						// iziToast.destroy();
						notif('warning', 'Database', 'Failed');
					}
				}
			});
		}, 1000);
	}
}

$(window).bind('scroll', function () {
	if ($(window).scrollTop() > 24) {
		$('#navigation').addClass('fixed');
		$('div.content').addClass('offset');
	} else {
		$('#navigation').removeClass('fixed');
		$('div.content').removeClass('offset');
	}
});

function notif(type, title, message) {
	var defaultMap = {
		position: 'bottomLeft',
		title: title,
		message: message,
		zindex: 99999,
		transitionOut: 'flipOutX',
	};
	// console.log("[JAVASCRIPT] Showing notification");
	// console.log(defaultMap);
	switch(type) {
		case "success": {
			iziToast.success(defaultMap);
			break;
		}
		case "warning": {
			iziToast.warning(defaultMap);
			break;
		}
		case "error": {
			iziToast.error(defaultMap);
			break;
		}
		default: {
			iziToast.info(defaultMap);
			break;
		}
	}
}
<|endoftext|>