<commit_before>"use strict";

var SvgFile = require('./svg-file');

class Svgo extends require('./worker-messenger') {
  constructor() {
    super('js/svgo-worker.js');
    this._multiPass = false;
    this._abortOnNextIteration = false;
    this._currentJob = Promise.resolve();
  }

  load(svgText) {
    return this._requestResponse({
      action: 'load',
      data: svgText
    }).then(({width, height}) => {
      return new SvgFile(svgText, width, height);
    });
  }

  process(settings, iterationCallback) {
    return this._currentJob = this.abortCurrent().then(async _ => {
      this._abortOnNextIteration = false;

      var result = await this._requestResponse({
        action: 'process',
        settings
      });

      var resultFile = new SvgFile(result.data, result.dimensions.width, result.dimensions.height);

      iterationCallback(resultFile);

      if (settings.multipass) {
        while (result = await this.nextPass()) {
          if (this._abortOnNextIteration) {
            throw Error('abort');
          }
          resultFile = new SvgFile(result.data, result.dimensions.width, result.dimensions.height);
          iterationCallback(resultFile);
        }
      }

      // return final result
      return resultFile;
    });
  }

  nextPass() {
    return this._requestResponse({
      action: 'nextPass'
    });
  }

  async abortCurrent() {
    this._abortOnNextIteration = true;

    try {
      await this._currentJob;
    } catch(e){}
  }
}

module.exports = Svgo;<commit_msg>Svgo: Remove unused property `_multiPass`, `settings.multipass` is used instead.<commit_after>"use strict";

var SvgFile = require('./svg-file');

class Svgo extends require('./worker-messenger') {
  constructor() {
    super('js/svgo-worker.js');
    this._abortOnNextIteration = false;
    this._currentJob = Promise.resolve();
  }

  load(svgText) {
    return this._requestResponse({
      action: 'load',
      data: svgText
    }).then(({width, height}) => {
      return new SvgFile(svgText, width, height);
    });
  }

  process(settings, iterationCallback) {
    return this._currentJob = this.abortCurrent().then(async _ => {
      this._abortOnNextIteration = false;

      var result = await this._requestResponse({
        action: 'process',
        settings
      });

      var resultFile = new SvgFile(result.data, result.dimensions.width, result.dimensions.height);

      iterationCallback(resultFile);

      if (settings.multipass) {
        while (result = await this.nextPass()) {
          if (this._abortOnNextIteration) {
            throw Error('abort');
          }
          resultFile = new SvgFile(result.data, result.dimensions.width, result.dimensions.height);
          iterationCallback(resultFile);
        }
      }

      // return final result
      return resultFile;
    });
  }

  nextPass() {
    return this._requestResponse({
      action: 'nextPass'
    });
  }

  async abortCurrent() {
    this._abortOnNextIteration = true;

    try {
      await this._currentJob;
    } catch(e){}
  }
}

module.exports = Svgo;<|endoftext|>


<commit_before>(function($, UI) {

    "use strict";

    var $win           = $(window),
        scrollspies    = [],
        checkScrollSpy = function() {
            for(var i=0; i < scrollspies.length; i++) {
                UI.support.requestAnimationFrame.apply(window, [scrollspies[i].check]);
            }
        };

    UI.component('scrollspy', {

        defaults: {
            "cls"        : "uk-scrollspy-inview",
            "initcls"    : "uk-scrollspy-init-inview",
            "topoffset"  : 0,
            "leftoffset" : 0,
            "repeat"     : false,
            "delay"      : 0
        },

        init: function() {

            var $this = this, idle, inviewstate, initinview,
                fn = function(){

                    var inview = UI.Utils.isInView($this.element, $this.options);

                    if(inview && !inviewstate) {

                        if(idle) clearTimeout(idle);

                        if(!initinview) {
                            $this.element.addClass($this.options.initcls);
                            $this.offset = $this.element.offset();
                            initinview = true;

                            $this.trigger("uk.scrollspy.init");
                        }

                        idle = setTimeout(function(){

                            if(inview) {
                                $this.element.addClass("uk-scrollspy-inview").addClass($this.options.cls).width();
                            }
                        }, $this.options.delay);

                        inviewstate = true;
                        $this.trigger("uk.scrollspy.inview");
                    }

                    if (!inview && inviewstate && $this.options.repeat) {
                        $this.element.removeClass("uk-scrollspy-inview").removeClass($this.options.cls);
                        inviewstate = false;

                        $this.trigger("uk.scrollspy.outview");
                    }
                };

            fn();

            this.check = fn;
            scrollspies.push(this);
        }
    });

    var scrollspynavs = [],
        checkScrollSpyNavs = function() {
            for(var i=0; i < scrollspynavs.length; i++) {
                UI.support.requestAnimationFrame.apply(window, [scrollspynavs[i].check]);
            }
        };

    UI.component('scrollspynav', {

        defaults: {
            "cls"          : 'uk-active',
            "closest"      : false,
            "topoffset"    : 0,
            "leftoffset"   : 0,
            "smoothscroll" : false
        },

        init: function() {

            var $this = this;

            var ids     = [],
                links   = this.element.find("a[href^='#']").each(function(){ ids.push($(this).attr("href")); }),
                targets = $(ids.join(","));

            var $this = this, inviews, fn = function(){

                inviews = [];

                for(var i=0 ; i < targets.length ; i++) {
                    if(UI.Utils.isInView(targets.eq(i), $this.options)) {
                        inviews.push(targets.eq(i));
                    }
                }

                if(inviews.length) {

                    var scrollTop = $win.scrollTop(),
                        target = (function(){
                            for(var i=0; i< inviews.length;i++){
                                if(inviews[i].offset().top >= scrollTop){
                                    return inviews[i];
                                }
                            }
                        })();

                    if(!target) return;

                    if($this.options.closest) {
                        links.closest($this.options.closest).removeClass($this.options.cls).end().filter("a[href='#"+target.attr("id")+"']").closest($this.options.closest).addClass($this.options.cls);
                    } else {
                        links.removeClass($this.options.cls).filter("a[href='#"+target.attr("id")+"']").addClass($this.options.cls);
                    }
                }
            };

            if(this.options.smoothscroll && UI["smoothScroll"]) {
                links.each(function(){
                    UI.smoothScroll(this, $this.options.smoothscroll);
                });
            }

            fn();

            this.element.data("scrollspynav", this);

            this.check = fn;
            scrollspynavs.push(this);

        }
    });

    var fnCheck = function(){
        checkScrollSpy();
        checkScrollSpyNavs();
    };

    // listen to scroll and resize
    $win.on("scroll", fnCheck).on("resize orientationchange", UI.Utils.debounce(fnCheck, 50));

    // init code
    $(document).on("uk-domready", function(e) {
        $("[data-uk-scrollspy]").each(function() {

            var element = $(this);

            if (!element.data("scrollspy")) {
                var obj = UI.scrollspy(element, UI.Utils.options(element.attr("data-uk-scrollspy")));
            }
        });

        $("[data-uk-scrollspy-nav]").each(function() {

            var element = $(this);

            if (!element.data("scrollspynav")) {
                var obj = UI.scrollspynav(element, UI.Utils.options(element.attr("data-uk-scrollspy-nav")));
            }
        });
    });

})(jQuery, jQuery.UIkit);<commit_msg>cleanup<commit_after>(function($, UI) {

    "use strict";

    var $win           = $(window),
        scrollspies    = [],
        checkScrollSpy = function() {
            for(var i=0; i < scrollspies.length; i++) {
                UI.support.requestAnimationFrame.apply(window, [scrollspies[i].check]);
            }
        };

    UI.component('scrollspy', {

        defaults: {
            "cls"        : "uk-scrollspy-inview",
            "initcls"    : "uk-scrollspy-init-inview",
            "topoffset"  : 0,
            "leftoffset" : 0,
            "repeat"     : false,
            "delay"      : 0
        },

        init: function() {

            var $this = this, idle, inviewstate, initinview,
                fn = function(){

                    var inview = UI.Utils.isInView($this.element, $this.options);

                    if(inview && !inviewstate) {

                        if(idle) clearTimeout(idle);

                        if(!initinview) {
                            $this.element.addClass($this.options.initcls);
                            $this.offset = $this.element.offset();
                            initinview = true;

                            $this.trigger("uk.scrollspy.init");
                        }

                        idle = setTimeout(function(){

                            if(inview) {
                                $this.element.addClass("uk-scrollspy-inview").addClass($this.options.cls).width();
                            }
                        }, $this.options.delay);

                        inviewstate = true;
                        $this.trigger("uk.scrollspy.inview");
                    }

                    if (!inview && inviewstate && $this.options.repeat) {
                        $this.element.removeClass("uk-scrollspy-inview").removeClass($this.options.cls);
                        inviewstate = false;

                        $this.trigger("uk.scrollspy.outview");
                    }
                };

            fn();

            this.check = fn;
            scrollspies.push(this);
        }
    });

    var scrollspynavs = [],
        checkScrollSpyNavs = function() {
            for(var i=0; i < scrollspynavs.length; i++) {
                UI.support.requestAnimationFrame.apply(window, [scrollspynavs[i].check]);
            }
        };

    UI.component('scrollspynav', {

        defaults: {
            "cls"          : 'uk-active',
            "closest"      : false,
            "topoffset"    : 0,
            "leftoffset"   : 0,
            "smoothscroll" : false
        },

        init: function() {

            var ids     = [],
                links   = this.element.find("a[href^='#']").each(function(){ ids.push($(this).attr("href")); }),
                targets = $(ids.join(","));

            var $this = this, inviews, fn = function(){

                inviews = [];

                for(var i=0 ; i < targets.length ; i++) {
                    if(UI.Utils.isInView(targets.eq(i), $this.options)) {
                        inviews.push(targets.eq(i));
                    }
                }

                if(inviews.length) {

                    var scrollTop = $win.scrollTop(),
                        target = (function(){
                            for(var i=0; i< inviews.length;i++){
                                if(inviews[i].offset().top >= scrollTop){
                                    return inviews[i];
                                }
                            }
                        })();

                    if(!target) return;

                    if($this.options.closest) {
                        links.closest($this.options.closest).removeClass($this.options.cls).end().filter("a[href='#"+target.attr("id")+"']").closest($this.options.closest).addClass($this.options.cls);
                    } else {
                        links.removeClass($this.options.cls).filter("a[href='#"+target.attr("id")+"']").addClass($this.options.cls);
                    }
                }
            };

            if(this.options.smoothscroll && UI["smoothScroll"]) {
                links.each(function(){
                    UI.smoothScroll(this, $this.options.smoothscroll);
                });
            }

            fn();

            this.element.data("scrollspynav", this);

            this.check = fn;
            scrollspynavs.push(this);

        }
    });

    var fnCheck = function(){
        checkScrollSpy();
        checkScrollSpyNavs();
    };

    // listen to scroll and resize
    $win.on("scroll", fnCheck).on("resize orientationchange", UI.Utils.debounce(fnCheck, 50));

    // init code
    $(document).on("uk-domready", function(e) {
        $("[data-uk-scrollspy]").each(function() {

            var element = $(this);

            if (!element.data("scrollspy")) {
                var obj = UI.scrollspy(element, UI.Utils.options(element.attr("data-uk-scrollspy")));
            }
        });

        $("[data-uk-scrollspy-nav]").each(function() {

            var element = $(this);

            if (!element.data("scrollspynav")) {
                var obj = UI.scrollspynav(element, UI.Utils.options(element.attr("data-uk-scrollspy-nav")));
            }
        });
    });

})(jQuery, jQuery.UIkit);<|endoftext|>


<commit_before>/**
 * @copyright Devexperts
 *
 * @requires DX
 * @requires DX.Dom
 * @requires DX.Bem
 * @requires DX.Event
 * @requires DropDown
 * @namespace
 */

var Selectbox = (function(DX, window, document, undefined) {
	'use strict';

	var CN_SELECTBOX = 'selectBox',
		CN_INNER = CN_SELECTBOX + '--inner',
		CN_LABEL = CN_SELECTBOX + '--label',
		CN_ARROW = CN_SELECTBOX + '--arrow',
		TN_OPTGROUP = 'optgroup',
		KEY_UP_CODE = 38,
		KEY_DOWN_CODE = 40,
		ENTER_KEY_CODE = 13,
		M_ACTIVE = 'active',
		M_FOCUSED = 'focused',
		UPDATE_DELAY = 100,
		tmpl = [
			'
',
			'',
			'',
			'
'
		].join(''),
		emptyOption = {
			value: '',
			label: '',
			textContent: '',
			className: ''
		};

	function map(collection, callback) {
		return Array.prototype.map.call(collection, callback);
	}

	function createDataObj(select) {
		return map(select.children, function(element) {
			return (element.tagName.toLowerCase() === TN_OPTGROUP) ? parseOptgroup(element) : parseOption(element);
		});
	}

	function parseOptgroup(optgroup) {
		return {
			title: optgroup.label,
			options: map(optgroup.children, function(option) {
				return parseOption(option);
			})
		};
	}

	function parseOption(option) {
		return {
			value: option.value,
			text: option.label || option.innerHTML,
			modifiers: splitClassName(option),
			data: DX.Dom.getData(option)
		};
	}

	function splitClassName(element) {
		return element ? element.className.split(' ') : [];
	}

	/**
	 * Creates new selectbox component
	 * @constructor Selectbox
	 * @param {HTMLSelectElement} select
	 */
	return function Selectbox(select) {
		var block,
			label,
			dropDown,
			updateDelay,
			currentOption,
			permanentBlockClassNames,
			selectId;

		/**
		 * Triggers when electbox is created
		 *
		 * @event selectbox:created
		 */
		function init() {
			var dropDownClassName = splitClassName(select);

			permanentBlockClassNames = select.className;
			select.className = '';

			selectId = select.id || DX.String.createRandomId();
			select.id = '';

			dropDownClassName.push(CN_SELECTBOX);

			initAppearance();
			dropDown = new DropDown(block, {
				modifiers: dropDownClassName
			});
			updateData();
			initListeners();

			DX.Event.trigger(select, Selectbox.E_CREATED, {
				detail: {
					eventTarget: select,
					block: block,
					dropDown: dropDown.getBlock()
				}
			});
		}

		function initAppearance() {
			var parent = DX.Dom.getParent(select),
				selectedIndex = select.selectedIndex;

			block = createBlock();
			label = block.querySelector('.' + CN_LABEL);

			parent.insertBefore(block, select);
			block.appendChild(select);
			select.selectedIndex = selectedIndex;
		}

		function createBlock() {
			return DX.Dom.createElement('div', {
				id: selectId,
				innerHTML: tmpl
			});
		}

		function updateData() {
			var data = createDataObj(select);

			dropDown.setDataList(data);
			setIndexBySelectedIndex();
		}

		function updateBlockClassNames() {
			block.className = permanentBlockClassNames + ' ' + DX.Bem.createModifiedClassName(CN_SELECTBOX, splitClassName(currentOption));
		}

		/**
		 * Triggers when selectbox is changed
		 *
		 * @event selectbox:changed
		 */
		function initListeners() {
			var dropDownBlock = dropDown.getBlock();

			select.addEventListener('focus', setFocusState);
			select.addEventListener('blur', removeFocusState);

			select.addEventListener('change', function(e) {
				setIndexBySelectedIndex();
				setFocusState();
			});

			block.addEventListener('touchend', function(e) {
				toggleDropdown();

				e.preventDefault();
			}, true);

			block.addEventListener('click', function(e) {
				toggleDropdown();
			}, true);

			dropDownBlock.addEventListener(DropDown.E_CHANGED, function() {
				var index = dropDown.getSelectedIndex();

				select.selectedIndex = index;
				setSelectedByIndex(index);
				DX.Event.trigger(select, Selectbox.E_CHANGED);
			}, true);

			dropDownBlock.addEventListener(DropDown.E_SHOWN, setActiveState);
			dropDownBlock.addEventListener(DropDown.E_HIDDEN, removeActiveState);
			select.addEventListener(Selectbox.E_CHANGE_VALUE, function() {
				setIndexBySelectedIndex();
			}, true);
			select.addEventListener('DOMNodeInserted', optionListModificationHandler);
			select.addEventListener('DOMNodeRemoved', optionListModificationHandler);
		}
		/**
		 * Show dropdown
		 * @method showDropDown
		 */
		function showDropdown() {
			if (!isDisabled()) {
				dropDown.show();
			}
		}
		/**
		 * Hide dropdown
		 * @method hideDropDown
		 */
		function hideDropdown() {
			dropDown.hide();
		}

		function keyDownHandler(e) {
			var key;

			if (dropDown.isShown()) {

				key = e.key || e.which;
				var newIndex = dropDown.getHoveredIndex();

				if (key === KEY_UP_CODE || key === 'Up') {
					newIndex--;
					dropDown.setHoveredIndex(newIndex);
				} else if (key === KEY_DOWN_CODE || key === 'Down') {
					newIndex++;
					dropDown.setHoveredIndex(newIndex);
				} else if (key === ENTER_KEY_CODE || key === 'Enter') {
					var triggerChangeEvent = true;
					dropDown.hide();
					dropDown.setSelectedIndex(newIndex, triggerChangeEvent);
				}

			}
		}
		function toggleDropdown() {
			if (isActiveState()) {
				hideDropdown();
			} else {
				showDropdown();
			}
		}

		function isDisabled() {
			return select.disabled;
		}

		function setActiveState() {
			DX.Bem.addModifier(block, M_ACTIVE);
			document.addEventListener(DX.Event.KEY_DOWN, keyDownHandler);
		}

		function setFocusState() {
			DX.Bem.addModifier(block, M_FOCUSED);
		}

		function removeFocusState() {
			DX.Bem.removeModifier(block, M_FOCUSED);
		}

		function removeActiveState() {
			DX.Bem.removeModifier(block, M_ACTIVE);
			document.removeEventListener(DX.Event.KEY_DOWN, keyDownHandler);
		}

		function isActiveState() {
			return DX.Bem.hasModifier(block, M_ACTIVE);
		}

		function optionListModificationHandler() {
			window.clearTimeout(updateDelay);

			updateDelay = window.setTimeout(updateData, UPDATE_DELAY);
		}
		/**
		 * Should be fired after external select index change
		 *
		 * @event selectbox:changevalue
		 */
		function setIndexBySelectedIndex() {
			var index = select.selectedIndex;

			setSelectedByIndex(index);
			dropDown.setSelectedIndex(index);
		}

		function setSelectedByIndex(index) {
			var option = select.options[index];

			if (option) {
				currentOption = option;
			} else {
				currentOption = emptyOption;
			}

			setLabel(currentOption.label || currentOption.textContent);
			updateBlockClassNames();

			if (isDisabled()) {
				Selectbox.disable(select);
			} else {
				Selectbox.enable(select);
			}
		}

		function setLabel(str) {
			label.textContent  = str;
		}
		/**
		 * Get current label
		 * @method getText
		 * @returns {String}
		 */
		function getText() {
			return currentOption.label || currentOption.textContent;
		}
		/**
		 * Get current value
		 * @method getValue
		 * @returns {Number|String}
		 */
		function getValue() {
			return currentOption.value || currentOption.textContent;
		}

		this.getValue = getValue;
		this.getText = getText;
		this.showDropdown = showDropdown;
		this.hideDropdown = hideDropdown;
		/**
		 * Get HTMLNode containing selectbox
		 * @method getBlock
		 * @returns {Node}
		 */
		this.getBlock = function() {
			return block;
		};
		/**
		 * Get element which listens to events
		 * @method getEventTarget
		 * @returns {Node}
		 */
		this.getEventTarget = function() {
			return select;
		};

		init();
	};
})(DX, window, document);

/** @constant
 * @type {string}
 * @default
 * @memberof Selectbox
 */
Selectbox.E_CREATED = 'selectbox:created';
/** @constant
 * @type {string}
 * @default
 * @memberof Selectbox
 */
Selectbox.E_CHANGED = 'selectbox:changed';
/** @constant
 * @type {string}
 * @default
 * @memberof Selectbox
 */
Selectbox.E_CHANGE_VALUE = 'selectbox:changevalue';

/**
 * Disable selectbox
 * @method disable
 * @static
 * @memberof Selectbox
 * @param {Node} HTMLNode containing select block
 */
Selectbox.disable = function disableSelectbox(select) {
	'use strict';

	var block = DX.Dom.getParent(select);

	DX.Bem.addModifier(block, 'disabled');
	select.disabled = true;
};
/**
 * Enable selectbox
 * @method enable
 * @static
 * @memberof Selectbox
 * @param {Node} HTMLNode containing select block
 */
Selectbox.enable = function enableSelectbox(select) {
	'use strict';

	var block = DX.Dom.getParent(select);

	DX.Bem.removeModifier(block, 'disabled');
	select.disabled = false;
};<commit_msg>addable modificator focus on change value<commit_after>/**
 * @copyright Devexperts
 *
 * @requires DX
 * @requires DX.Dom
 * @requires DX.Bem
 * @requires DX.Event
 * @requires DropDown
 * @namespace
 */

var Selectbox = (function(DX, window, document, undefined) {
	'use strict';

	var CN_SELECTBOX = 'selectBox',
		CN_INNER = CN_SELECTBOX + '--inner',
		CN_LABEL = CN_SELECTBOX + '--label',
		CN_ARROW = CN_SELECTBOX + '--arrow',
		TN_OPTGROUP = 'optgroup',
		KEY_UP_CODE = 38,
		KEY_DOWN_CODE = 40,
		ENTER_KEY_CODE = 13,
		M_ACTIVE = 'active',
		M_FOCUSED = 'focused',
		UPDATE_DELAY = 100,
		tmpl = [
			'
',
			'',
			'',
			'
'
		].join(''),
		emptyOption = {
			value: '',
			label: '',
			textContent: '',
			className: ''
		};

	function map(collection, callback) {
		return Array.prototype.map.call(collection, callback);
	}

	function createDataObj(select) {
		return map(select.children, function(element) {
			return (element.tagName.toLowerCase() === TN_OPTGROUP) ? parseOptgroup(element) : parseOption(element);
		});
	}

	function parseOptgroup(optgroup) {
		return {
			title: optgroup.label,
			options: map(optgroup.children, function(option) {
				return parseOption(option);
			})
		};
	}

	function parseOption(option) {
		return {
			value: option.value,
			text: option.label || option.innerHTML,
			modifiers: splitClassName(option),
			data: DX.Dom.getData(option)
		};
	}

	function splitClassName(element) {
		return element ? element.className.split(' ') : [];
	}

	/**
	 * Creates new selectbox component
	 * @constructor Selectbox
	 * @param {HTMLSelectElement} select
	 */
	return function Selectbox(select) {
		var block,
			label,
			dropDown,
			hasFocus,
			updateDelay,
			currentOption,
			permanentBlockClassNames,
			selectId;

		/**
		 * Triggers when electbox is created
		 *
		 * @event selectbox:created
		 */
		function init() {
			var dropDownClassName = splitClassName(select);

			permanentBlockClassNames = select.className;
			select.className = '';

			selectId = select.id || DX.String.createRandomId();
			select.id = '';

			dropDownClassName.push(CN_SELECTBOX);

			initAppearance();
			dropDown = new DropDown(block, {
				modifiers: dropDownClassName
			});
			updateData();
			initListeners();

			DX.Event.trigger(select, Selectbox.E_CREATED, {
				detail: {
					eventTarget: select,
					block: block,
					dropDown: dropDown.getBlock()
				}
			});
		}

		function initAppearance() {
			var parent = DX.Dom.getParent(select),
				selectedIndex = select.selectedIndex;

			block = createBlock();
			label = block.querySelector('.' + CN_LABEL);

			parent.insertBefore(block, select);
			block.appendChild(select);
			select.selectedIndex = selectedIndex;
		}

		function createBlock() {
			return DX.Dom.createElement('div', {
				id: selectId,
				innerHTML: tmpl
			});
		}

		function updateData() {
			var data = createDataObj(select);

			dropDown.setDataList(data);
			setIndexBySelectedIndex();
		}

		function updateBlockClassNames() {
			var classNames = [permanentBlockClassNames, DX.Bem.createModifiedClassName(CN_SELECTBOX, splitClassName(currentOption))];

			if (hasFocus) {
				classNames.push(DX.Bem.createModifiedClassName(CN_SELECTBOX, splitClassName(M_FOCUSED)));
			}

			block.className = classNames.join(' ');
		}

		/**
		 * Triggers when selectbox is changed
		 *
		 * @event selectbox:changed
		 */
		function initListeners() {
			var dropDownBlock = dropDown.getBlock();

			select.addEventListener('focus', function(){
				setFocusState();
				hasFocus = true;
			});

			select.addEventListener('blur', function() {
				removeFocusState();
				hasFocus = false;
			});

			select.addEventListener('change', function(e) {
				setIndexBySelectedIndex();
			});

			block.addEventListener('touchend', function(e) {
				toggleDropdown();

				e.preventDefault();
			}, true);

			block.addEventListener('click', function(e) {
				toggleDropdown();
			}, true);

			dropDownBlock.addEventListener(DropDown.E_CHANGED, function() {
				var index = dropDown.getSelectedIndex();

				select.selectedIndex = index;
				setSelectedByIndex(index);
				DX.Event.trigger(select, Selectbox.E_CHANGED);
			}, true);

			dropDownBlock.addEventListener(DropDown.E_SHOWN, setActiveState);
			dropDownBlock.addEventListener(DropDown.E_HIDDEN, removeActiveState);
			select.addEventListener(Selectbox.E_CHANGE_VALUE, function() {
				setIndexBySelectedIndex();
			}, true);
			select.addEventListener('DOMNodeInserted', optionListModificationHandler);
			select.addEventListener('DOMNodeRemoved', optionListModificationHandler);
		}
		/**
		 * Show dropdown
		 * @method showDropDown
		 */
		function showDropdown() {
			if (!isDisabled()) {
				dropDown.show();
			}
		}
		/**
		 * Hide dropdown
		 * @method hideDropDown
		 */
		function hideDropdown() {
			dropDown.hide();
		}

		function keyDownHandler(e) {
			var key;

			if (dropDown.isShown()) {

				key = e.key || e.which;
				var newIndex = dropDown.getHoveredIndex();

				if (key === KEY_UP_CODE || key === 'Up') {
					newIndex--;
					dropDown.setHoveredIndex(newIndex);
				} else if (key === KEY_DOWN_CODE || key === 'Down') {
					newIndex++;
					dropDown.setHoveredIndex(newIndex);
				} else if (key === ENTER_KEY_CODE || key === 'Enter') {
					var triggerChangeEvent = true;
					dropDown.hide();
					dropDown.setSelectedIndex(newIndex, triggerChangeEvent);
				}

			}
		}
		function toggleDropdown() {
			if (isActiveState()) {
				hideDropdown();
			} else {
				showDropdown();
			}
		}

		function isDisabled() {
			return select.disabled;
		}

		function setActiveState() {
			DX.Bem.addModifier(block, M_ACTIVE);
			document.addEventListener(DX.Event.KEY_DOWN, keyDownHandler);
		}

		function setFocusState() {
			DX.Bem.addModifier(block, M_FOCUSED);
		}

		function removeFocusState() {
			DX.Bem.removeModifier(block, M_FOCUSED);
		}

		function removeActiveState() {
			DX.Bem.removeModifier(block, M_ACTIVE);
			document.removeEventListener(DX.Event.KEY_DOWN, keyDownHandler);
		}

		function isActiveState() {
			return DX.Bem.hasModifier(block, M_ACTIVE);
		}

		function optionListModificationHandler() {
			window.clearTimeout(updateDelay);

			updateDelay = window.setTimeout(updateData, UPDATE_DELAY);
		}
		/**
		 * Should be fired after external select index change
		 *
		 * @event selectbox:changevalue
		 */
		function setIndexBySelectedIndex() {
			var index = select.selectedIndex;

			setSelectedByIndex(index);
			dropDown.setSelectedIndex(index);
		}

		function setSelectedByIndex(index) {
			var option = select.options[index];

			if (option) {
				currentOption = option;
			} else {
				currentOption = emptyOption;
			}

			setLabel(currentOption.label || currentOption.textContent);
			updateBlockClassNames();

			if (isDisabled()) {
				Selectbox.disable(select);
			} else {
				Selectbox.enable(select);
			}
		}

		function setLabel(str) {
			label.textContent  = str;
		}
		/**
		 * Get current label
		 * @method getText
		 * @returns {String}
		 */
		function getText() {
			return currentOption.label || currentOption.textContent;
		}
		/**
		 * Get current value
		 * @method getValue
		 * @returns {Number|String}
		 */
		function getValue() {
			return currentOption.value || currentOption.textContent;
		}

		this.getValue = getValue;
		this.getText = getText;
		this.showDropdown = showDropdown;
		this.hideDropdown = hideDropdown;
		/**
		 * Get HTMLNode containing selectbox
		 * @method getBlock
		 * @returns {Node}
		 */
		this.getBlock = function() {
			return block;
		};
		/**
		 * Get element which listens to events
		 * @method getEventTarget
		 * @returns {Node}
		 */
		this.getEventTarget = function() {
			return select;
		};

		init();
	};
})(DX, window, document);

/** @constant
 * @type {string}
 * @default
 * @memberof Selectbox
 */
Selectbox.E_CREATED = 'selectbox:created';
/** @constant
 * @type {string}
 * @default
 * @memberof Selectbox
 */
Selectbox.E_CHANGED = 'selectbox:changed';
/** @constant
 * @type {string}
 * @default
 * @memberof Selectbox
 */
Selectbox.E_CHANGE_VALUE = 'selectbox:changevalue';

/**
 * Disable selectbox
 * @method disable
 * @static
 * @memberof Selectbox
 * @param {Node} HTMLNode containing select block
 */
Selectbox.disable = function disableSelectbox(select) {
	'use strict';

	var block = DX.Dom.getParent(select);

	DX.Bem.addModifier(block, 'disabled');
	select.disabled = true;
};
/**
 * Enable selectbox
 * @method enable
 * @static
 * @memberof Selectbox
 * @param {Node} HTMLNode containing select block
 */
Selectbox.enable = function enableSelectbox(select) {
	'use strict';

	var block = DX.Dom.getParent(select);

	DX.Bem.removeModifier(block, 'disabled');
	select.disabled = false;
};<|endoftext|>


<commit_before>var util2 = require('util2');
var myutil = require('myutil');
var safe = require('safe');
var Emitter = require('emitter');
var Vector2 = require('vector2');
var Feature = require('platform/feature');
var Accel = require('ui/accel');
var WindowStack = require('ui/windowstack');
var Propable = require('ui/propable');
var Stage = require('ui/stage');
var simply = require('ui/simply');

var buttons = [
  'back',
  'up',
  'select',
  'down',
];

var configProps = [
  'fullscreen',
  'style',
  'scrollable',
  'backgroundColor',
];

var statusProps = [
  'status',
  'separator',
  'color',
  'backgroundColor',
];

var actionProps = [
  'action',
  'up',
  'select',
  'back',
  'backgroundColor',
];

var accessorProps = configProps;

var nestedProps = [
  'action',
  'status',
];

var defaults = {
  status: false,
  backgroundColor: 'black',
  scrollable: false,
  paging: Feature.round(true, false),
};

var nextId = 1;

var checkProps = function(def) {
  if (!def) return;
  if ('fullscreen' in def && safe.warnFullscreen !== false) {
    safe.warn('`fullscreen` has been deprecated by `status` which allows settings\n\t' +
              'its color and separator in a similar manner to the `action` property.\n\t' +
              'Remove usages of `fullscreen` to enable usage of `status`.', 2);
    safe.warnFullscreen = false;
  }
};

var Window = function(windowDef) {
  checkProps(windowDef);
  this.state = myutil.shadow(defaults, windowDef || {});
  this.state.id = nextId++;
  this._buttonInit();
  this._items = [];
  this._dynamic = true;
  this._size = new Vector2();
  this.size(); // calculate and set the size
};

Window._codeName = 'window';

util2.copy(Emitter.prototype, Window.prototype);

util2.copy(Propable.prototype, Window.prototype);

util2.copy(Stage.prototype, Window.prototype);

Propable.makeAccessors(accessorProps, Window.prototype);

Propable.makeNestedAccessors(nestedProps, Window.prototype);

Window.prototype._id = function() {
  return this.state.id;
};

Window.prototype._prop = function(def, clear, pushing) {
  checkProps(def);
  Stage.prototype._prop.call(this, def, clear, pushing);
};

Window.prototype._hide = function(broadcast) {
  if (broadcast === false) { return; }
  simply.impl.windowHide(this._id());
};

Window.prototype.hide = function() {
  WindowStack.remove(this, true);
  return this;
};

Window.prototype._show = function(pushing) {
  this._prop(this.state, true, pushing || false);
  this._buttonConfig({});
  if (this._dynamic) {
    Stage.prototype._show.call(this, pushing);
  }
};

Window.prototype.show = function() {
  WindowStack.push(this);
  return this;
};

Window.prototype._insert = function() {
  if (this._dynamic) {
    Stage.prototype._insert.apply(this, arguments);
  }
};

Window.prototype._remove = function() {
  if (this._dynamic) {
    Stage.prototype._remove.apply(this, arguments);
  }
};

Window.prototype._clearStatus = function() {
  statusProps.forEach(Propable.unset.bind(this.state.status));
};

Window.prototype._clearAction = function() {
  actionProps.forEach(Propable.unset.bind(this.state.action));
};

Window.prototype._clear = function(flags_) {
  var flags = myutil.toFlags(flags_);
  if (myutil.flag(flags, 'action')) {
    this._clearAction();
  }
  if (myutil.flag(flags, 'status')) {
    this._clearStatus();
  }
  if (flags_ === true || flags_ === undefined) {
    Propable.prototype._clear.call(this);
  }
};

Window.prototype._action = function(actionDef) {
  if (this === WindowStack.top()) {
    simply.impl.windowActionBar(actionDef);
  }
};

Window.prototype._status = function(statusDef) {
  if (this === WindowStack.top()) {
    simply.impl.windowStatusBar(statusDef);
  }
};

var isBackEvent = function(type, subtype) {
  return ((type === 'click' || type === 'longClick') && subtype === 'back');
};

Window.prototype.onAddHandler = function(type, subtype) {
  if (isBackEvent(type, subtype)) {
    this._buttonAutoConfig();
  }
  if (type === 'accelData') {
    Accel.autoSubscribe();
  }
};

Window.prototype.onRemoveHandler = function(type, subtype) {
  if (!type || isBackEvent(type, subtype)) {
    this._buttonAutoConfig();
  }
  if (!type || type === 'accelData') {
    Accel.autoSubscribe();
  }
};

Window.prototype._buttonInit = function() {
  this._button = {
    config: {},
    configMode: 'auto',
  };
  for (var i = 0, ii = buttons.length; i < ii; i++) {
    var button = buttons[i];
    if (button !== 'back') {
      this._button.config[buttons[i]] = true;
    }
  }
};

/**
 * The button configuration parameter for {@link simply.buttonConfig}.
 * The button configuration allows you to enable to disable buttons without having to register or unregister handlers if that is your preferred style.
 * You may also enable the back button manually as an alternative to registering a click handler with 'back' as its subtype using {@link simply.on}.
 * @typedef {object} simply.buttonConf
 * @property {boolean} [back] - Whether to enable the back button. Initializes as false. Simply.js can also automatically register this for you based on the amount of click handlers with subtype 'back'.
 * @property {boolean} [up] - Whether to enable the up button. Initializes as true. Note that this is disabled when using {@link simply.scrollable}.
 * @property {boolean} [select] - Whether to enable the select button. Initializes as true.
 * @property {boolean} [down] - Whether to enable the down button. Initializes as true. Note that this is disabled when using {@link simply.scrollable}.
 */

/**
 * Changes the button configuration.
 * See {@link simply.buttonConfig}
 * @memberOf simply
 * @param {simply.buttonConfig} buttonConf - An object defining the button configuration.
 */
Window.prototype._buttonConfig = function(buttonConf, auto) {
  if (buttonConf === undefined) {
    var config = {};
    for (var i = 0, ii = buttons.length; i < ii; ++i) {
      var name = buttons[i];
      config[name] = this._button.config[name];
    }
    return config;
  }
  for (var k in buttonConf) {
    if (buttons.indexOf(k) !== -1) {
      if (k === 'back') {
        this._button.configMode = buttonConf.back && !auto ? 'manual' : 'auto';
      }
      this._button.config[k] = buttonConf[k];
    }
  }
  if (simply.impl.windowButtonConfig) {
    return simply.impl.windowButtonConfig(this._button.config);
  }
};

Window.prototype.buttonConfig = function(buttonConf) {
  this._buttonConfig(buttonConf);
};

Window.prototype._buttonAutoConfig = function() {
  if (!this._button || this._button.configMode !== 'auto') {
    return;
  }
  var singleBackCount = this.listenerCount('click', 'back');
  var longBackCount = this.listenerCount('longClick', 'back');
  var useBack = singleBackCount + longBackCount > 0;
  if (useBack !== this._button.config.back) {
    this._button.config.back = useBack;
    return this._buttonConfig(this._button.config, true);
  }
};

Window.prototype.size = function() {
  var state = this.state;
  var size = this._size.copy(Feature.resolution());
  if ('status' in state && state.status !== false) {
    size.y -= Feature.statusBarHeight();
  } else if ('fullscreen' in state && state.fullscreen === false) {
    size.y -= Feature.statusBarHeight();
  }
  if ('action' in state && state.action !== false) {
    size.x -= Feature.actionBarWidth();
  }
  return size;
};

Window.prototype._toString = function() {
  return '[' + this.constructor._codeName + ' ' + this._id() + ']';
};

Window.prototype._emit = function(type, subtype, e) {
  e.window = this;
  var klass = this.constructor;
  if (klass) {
    e[klass._codeName] = this;
  }
  if (this.emit(type, subtype, e) === false) {
    return false;
  }
};

Window.prototype._emitShow = function(type) {
  return this._emit(type, null, {});
};

Window.emit = function(type, subtype, e) {
  var wind = WindowStack.top();
  if (wind) {
    return wind._emit(type, subtype, e);
  }
};

/**
 * Simply.js button click event. This can either be a single click or long click.
 * Use the event type 'click' or 'longClick' to subscribe to these events.
 * @typedef simply.clickEvent
 * @property {string} button - The button that was pressed: 'back', 'up', 'select', or 'down'. This is also the event subtype.
 */

Window.emitClick = function(type, button) {
  var e = {
    button: button,
  };
  return Window.emit(type, button, e);
};

module.exports = Window;
<commit_msg>Add window paging accessor<commit_after>var util2 = require('util2');
var myutil = require('myutil');
var safe = require('safe');
var Emitter = require('emitter');
var Vector2 = require('vector2');
var Feature = require('platform/feature');
var Accel = require('ui/accel');
var WindowStack = require('ui/windowstack');
var Propable = require('ui/propable');
var Stage = require('ui/stage');
var simply = require('ui/simply');

var buttons = [
  'back',
  'up',
  'select',
  'down',
];

var configProps = [
  'fullscreen',
  'style',
  'scrollable',
  'paging',
  'backgroundColor',
];

var statusProps = [
  'status',
  'separator',
  'color',
  'backgroundColor',
];

var actionProps = [
  'action',
  'up',
  'select',
  'back',
  'backgroundColor',
];

var accessorProps = configProps;

var nestedProps = [
  'action',
  'status',
];

var defaults = {
  status: false,
  backgroundColor: 'black',
  scrollable: false,
  paging: Feature.round(true, false),
};

var nextId = 1;

var checkProps = function(def) {
  if (!def) return;
  if ('fullscreen' in def && safe.warnFullscreen !== false) {
    safe.warn('`fullscreen` has been deprecated by `status` which allows settings\n\t' +
              'its color and separator in a similar manner to the `action` property.\n\t' +
              'Remove usages of `fullscreen` to enable usage of `status`.', 2);
    safe.warnFullscreen = false;
  }
};

var Window = function(windowDef) {
  checkProps(windowDef);
  this.state = myutil.shadow(defaults, windowDef || {});
  this.state.id = nextId++;
  this._buttonInit();
  this._items = [];
  this._dynamic = true;
  this._size = new Vector2();
  this.size(); // calculate and set the size
};

Window._codeName = 'window';

util2.copy(Emitter.prototype, Window.prototype);

util2.copy(Propable.prototype, Window.prototype);

util2.copy(Stage.prototype, Window.prototype);

Propable.makeAccessors(accessorProps, Window.prototype);

Propable.makeNestedAccessors(nestedProps, Window.prototype);

Window.prototype._id = function() {
  return this.state.id;
};

Window.prototype._prop = function(def, clear, pushing) {
  checkProps(def);
  Stage.prototype._prop.call(this, def, clear, pushing);
};

Window.prototype._hide = function(broadcast) {
  if (broadcast === false) { return; }
  simply.impl.windowHide(this._id());
};

Window.prototype.hide = function() {
  WindowStack.remove(this, true);
  return this;
};

Window.prototype._show = function(pushing) {
  this._prop(this.state, true, pushing || false);
  this._buttonConfig({});
  if (this._dynamic) {
    Stage.prototype._show.call(this, pushing);
  }
};

Window.prototype.show = function() {
  WindowStack.push(this);
  return this;
};

Window.prototype._insert = function() {
  if (this._dynamic) {
    Stage.prototype._insert.apply(this, arguments);
  }
};

Window.prototype._remove = function() {
  if (this._dynamic) {
    Stage.prototype._remove.apply(this, arguments);
  }
};

Window.prototype._clearStatus = function() {
  statusProps.forEach(Propable.unset.bind(this.state.status));
};

Window.prototype._clearAction = function() {
  actionProps.forEach(Propable.unset.bind(this.state.action));
};

Window.prototype._clear = function(flags_) {
  var flags = myutil.toFlags(flags_);
  if (myutil.flag(flags, 'action')) {
    this._clearAction();
  }
  if (myutil.flag(flags, 'status')) {
    this._clearStatus();
  }
  if (flags_ === true || flags_ === undefined) {
    Propable.prototype._clear.call(this);
  }
};

Window.prototype._action = function(actionDef) {
  if (this === WindowStack.top()) {
    simply.impl.windowActionBar(actionDef);
  }
};

Window.prototype._status = function(statusDef) {
  if (this === WindowStack.top()) {
    simply.impl.windowStatusBar(statusDef);
  }
};

var isBackEvent = function(type, subtype) {
  return ((type === 'click' || type === 'longClick') && subtype === 'back');
};

Window.prototype.onAddHandler = function(type, subtype) {
  if (isBackEvent(type, subtype)) {
    this._buttonAutoConfig();
  }
  if (type === 'accelData') {
    Accel.autoSubscribe();
  }
};

Window.prototype.onRemoveHandler = function(type, subtype) {
  if (!type || isBackEvent(type, subtype)) {
    this._buttonAutoConfig();
  }
  if (!type || type === 'accelData') {
    Accel.autoSubscribe();
  }
};

Window.prototype._buttonInit = function() {
  this._button = {
    config: {},
    configMode: 'auto',
  };
  for (var i = 0, ii = buttons.length; i < ii; i++) {
    var button = buttons[i];
    if (button !== 'back') {
      this._button.config[buttons[i]] = true;
    }
  }
};

/**
 * The button configuration parameter for {@link simply.buttonConfig}.
 * The button configuration allows you to enable to disable buttons without having to register or unregister handlers if that is your preferred style.
 * You may also enable the back button manually as an alternative to registering a click handler with 'back' as its subtype using {@link simply.on}.
 * @typedef {object} simply.buttonConf
 * @property {boolean} [back] - Whether to enable the back button. Initializes as false. Simply.js can also automatically register this for you based on the amount of click handlers with subtype 'back'.
 * @property {boolean} [up] - Whether to enable the up button. Initializes as true. Note that this is disabled when using {@link simply.scrollable}.
 * @property {boolean} [select] - Whether to enable the select button. Initializes as true.
 * @property {boolean} [down] - Whether to enable the down button. Initializes as true. Note that this is disabled when using {@link simply.scrollable}.
 */

/**
 * Changes the button configuration.
 * See {@link simply.buttonConfig}
 * @memberOf simply
 * @param {simply.buttonConfig} buttonConf - An object defining the button configuration.
 */
Window.prototype._buttonConfig = function(buttonConf, auto) {
  if (buttonConf === undefined) {
    var config = {};
    for (var i = 0, ii = buttons.length; i < ii; ++i) {
      var name = buttons[i];
      config[name] = this._button.config[name];
    }
    return config;
  }
  for (var k in buttonConf) {
    if (buttons.indexOf(k) !== -1) {
      if (k === 'back') {
        this._button.configMode = buttonConf.back && !auto ? 'manual' : 'auto';
      }
      this._button.config[k] = buttonConf[k];
    }
  }
  if (simply.impl.windowButtonConfig) {
    return simply.impl.windowButtonConfig(this._button.config);
  }
};

Window.prototype.buttonConfig = function(buttonConf) {
  this._buttonConfig(buttonConf);
};

Window.prototype._buttonAutoConfig = function() {
  if (!this._button || this._button.configMode !== 'auto') {
    return;
  }
  var singleBackCount = this.listenerCount('click', 'back');
  var longBackCount = this.listenerCount('longClick', 'back');
  var useBack = singleBackCount + longBackCount > 0;
  if (useBack !== this._button.config.back) {
    this._button.config.back = useBack;
    return this._buttonConfig(this._button.config, true);
  }
};

Window.prototype.size = function() {
  var state = this.state;
  var size = this._size.copy(Feature.resolution());
  if ('status' in state && state.status !== false) {
    size.y -= Feature.statusBarHeight();
  } else if ('fullscreen' in state && state.fullscreen === false) {
    size.y -= Feature.statusBarHeight();
  }
  if ('action' in state && state.action !== false) {
    size.x -= Feature.actionBarWidth();
  }
  return size;
};

Window.prototype._toString = function() {
  return '[' + this.constructor._codeName + ' ' + this._id() + ']';
};

Window.prototype._emit = function(type, subtype, e) {
  e.window = this;
  var klass = this.constructor;
  if (klass) {
    e[klass._codeName] = this;
  }
  if (this.emit(type, subtype, e) === false) {
    return false;
  }
};

Window.prototype._emitShow = function(type) {
  return this._emit(type, null, {});
};

Window.emit = function(type, subtype, e) {
  var wind = WindowStack.top();
  if (wind) {
    return wind._emit(type, subtype, e);
  }
};

/**
 * Simply.js button click event. This can either be a single click or long click.
 * Use the event type 'click' or 'longClick' to subscribe to these events.
 * @typedef simply.clickEvent
 * @property {string} button - The button that was pressed: 'back', 'up', 'select', or 'down'. This is also the event subtype.
 */

Window.emitClick = function(type, button) {
  var e = {
    button: button,
  };
  return Window.emit(type, button, e);
};

module.exports = Window;
<|endoftext|>


<commit_before>/*jslint nomen: true */
/*globals define: true */

define([
  // Libraries
  'jquery',
  'lib/lodash',
  'backbone',
  'lib/leaflet/leaflet.google',
  'moment',
  'lib/tinypubsub',
  'lib/kissmetrics',

  // LocalData
  'settings',
  'api',

  // Views
  'views/responses/list',

  // Models
  'models/responses'
],

function($, _, Backbone, L, moment, events, _kmq, settings, api, ResponseListView, Responses) {
  'use strict';

  function indexToColor(index) {
    if (index >= 0) {
      return settings.colorRange[index + 1];
    }
    return settings.colorRange[0];
  }

  function getFeatureStyle(feature) {
    if (feature.properties === undefined || feature.properties.color === undefined) {
      return settings.styleTemplate;
    }

    var style = {};
    _.extend(style, settings.styleTemplate, {
      color: feature.properties.color,
      fillColor: feature.properties.color
    });
    return style;
  }

  function zoneStyle(feature) {
    return {
      color: feature.properties.color,
      opacity: 0.3,
      fillColor: feature.properties.color,
      fillOpacity: 0.15,
      weight: 2
    };
  }

  var MapView = Backbone.View.extend({

    map: null,
    responses: null,
    survey: null,
    paginationView: null,
    selectedLayer: null,
    filtered: false,
    selectedObject: {},
    markers: {},
    filter: null,

    initialize: function(options) {
      console.log("Init map view");
      _.bindAll(this, 'render', 'selectObject', 'renderObject', 'renderObjects',
        'getResponsesInBounds', 'updateMapStyleBasedOnZoom', 'updateObjectStyles',
        'styleFeature', 'setupPolygon');

      this.responses = options.responses;
      // TODO: if we add the filter logic to the responses collection, we can
      // more cleanly trigger off its events.
      this.listenTo(this.responses, 'reset', this.render);
      this.listenTo(this.responses, 'addSet', this.render);

      this.survey = options.survey;

      // We track the results on the map using these two groups
      this.parcelIdsOnTheMap = {};
      this.objectsOnTheMap = new L.FeatureGroup();
      this.zoneLayer = new L.FeatureGroup();

      this.defaultStyle = settings.farZoomStyle;
      this.defaultPointToLayer = function (feature, latlng) {
        return new L.circleMarker(latlng, settings.farZoomStyle);
      };

      this.delayFitBounds = _.debounce(this.fitBounds, 250);

      this.render();
    },

    fitBounds: function () {
      this.map.invalidateSize(false);

      if (this.responses !== null && this.responses.length > 0) {
        try {
          this.map.fitBounds(this.objectsOnTheMap.getBounds());
        } catch (e) {
        }
      } else if (this.survey.has('zones')) {
        try {
          this.map.fitBounds(this.zoneLayer.getBounds());
        } catch (e) {
        }
      }
    },

    // Debounced version of fitBounds. Created in the initialize method.
    delayFitBounds: null,

    render: function (arg) {
      var hasResponses = this.responses !== null && this.responses.length > 0;
      var hasZones = this.survey.has('zones');

      // Don't draw a map if there are no responses and no survey zones to
      // plot.
      if (!hasResponses && !hasZones) {
        return;
      }

      if (this.map === null) {
        // Create the map.
        this.$el.html(_.template($('#map-view').html(), {}));

        // Initialize the map
        this.map = new L.map('map', {
          zoom: 15
        });

        // Set up the base map; add the parcels and done markers
        this.baseLayer = L.tileLayer('http://a.tiles.mapbox.com/v3/matth.map-zmpggdzn/{z}/{x}/{y}.png');
        this.map.addLayer(this.baseLayer);
        this.satelliteLayer = L.tileLayer('http://a.tiles.mapbox.com/v3/matth.map-yyr7jb6r/{z}/{x}/{y}.png');
        this.activeLayer = 'streets';

        this.map.addLayer(this.zoneLayer);
        this.map.addLayer(this.objectsOnTheMap);

        this.map.setView([0,0], 17); // default center
        this.updateMapStyleBasedOnZoom();
        this.map.on('zoomend', this.updateMapStyleBasedOnZoom);
      }

      if (hasZones) {
        // Plot the survey zones.
        this.plotZones();
      } else {
        // We don't start listening in initialize because we might get the
        // change event before we even have a map.
        this.listenTo(this.survey, 'change', this.plotZones);
      }

      // If there are no responses, don't bother trying to plot responses or
      // deal with filters.
      if (!hasResponses) {
        return;
      }

      // TODO: better message passing
      events.publish('loading', [true]);

      if (this.responses.filters === null) {
        this.filter = null;
      }

      if (_.isArray(arg)) {
        // We got an array of models. Let's plot just that set them.
        this.plotResponses(arg);
      } else {
        // Plot all of the responses from the responses collection.
        this.plotResponses();
      }

      events.publish('loading', [false]);
      return this;
    },

    /**
     * Set filter parameters for displaying results on the map
     * The response collection will activate this if we have an active filter
     * @param {String} question Name of the question, eg 'vacant'
     * @param {Object} answers  Possible answers to the qu
     */
    setFilter: function (question, answers) {
      this.filter = {
        question: question,
        answers: answers
      };
      this.plotResponses();
    },

    /**
     * Style a feature
     * @param  {Object} feature
     * @return {Object}         feature with style attributes
     */
    styleFeature: function(data) {
      // Set a default style
      var style = this.defaultStyle;

      // If there's no data, style this as blank
      if(data === undefined || this.filter === null) {
        return style;
      }

      // Set the default filter style
      style = _.extend({
        color: settings.colorRange[0],
        fillColor: settings.colorRange[0]
      }, settings.styleTemplate);

      // Get the answer to the currently filtered question
      var answer = data[this.filter.question];

      // If there's no answer, style this as a blank answer
      if (answer === undefined) {
        return style;
      }

      // Set the line and fill colors, then return the style
      style.color = this.filter.answers[answer].color;
      style.fillColor = style.color;
      return style;
    },

    /**
     * Set up a geojson feature for a response object with a polygon
     * @param  {Object} response a response object with a polygon
     * @return {Object}          GeoJSON feature
     */
    setupPolygon: function(response) {
      // Record the objects as rendered so we don't render it twice.
      var parcelId = response.get('parcel_id');
      this.parcelIdsOnTheMap[parcelId] = true;

      // Set up the default style
      var style = this.defaultStyle;

      // Set up the feature
      var feature = {
        type: 'Feature',
        id: response.get('id'),
        parcelId: parcelId,
        geometry: response.get('geo_info').geometry,
        style: style
      };

      // If there is a filter, attach the filtered question to the feature
      // This is used later to style the feature
      // We don't attach all the data to keep size down
      // (large collections can have 20+ mb of data)
      if (this.filter !== null) {

        // Some responses don't have associated data. It's still important that
        // they appear in the filter, since the user may be looking for empty
        // result sets.
        if(response.get('responses') === undefined) {
          feature[this.filter.question] = undefined;
        }else {
          feature[this.filter.question] = response.get('responses')[this.filter.question];
        }
      }

      // Return the feature for rendering
      return feature;
    },

    /**
     * Plot responses on the map
     * @param  {Array} responses
     */
    plotResponses: function (responses) {
      // If we aren't given an explicit set of responses to plot,
      // we'll plot all of the responses from the collection.
      if (responses === undefined) {

        // Clear out the old results first
        this.objectsOnTheMap.clearLayers();
        this.parcelIdsOnTheMap = {};

        // TODO: not great form to reassign one of the arguments
        responses = this.responses.models;
      }

      var renderedParcelTracker = this.parcelIdsOnTheMap;

      // We'll need to create GeoJSON FeatureCollection objects to pass
      // to Leaflet for rendering.
      var featureCollection = {
        type: 'FeatureCollection',
        features: []
      };

      var pointCollection = {
        type: 'FeatureCollection',
        features: []
      };

      // Populate the FeatureCollection for responses with full geometry.
      featureCollection.features = _.map(
        _.filter(responses, function (response) {

          // Get items that have geometry
          // AND aren't already on the map
          return (_.has(response.get('geo_info'), 'geometry') &&
                 !_.has(renderedParcelTracker, response.get('parcel_id'))
          );
      }), this.setupPolygon);

      // Populate the FeatureCollection for responses with only a centroid.
      pointCollection.features = _.map(
        _.filter(responses, function (response) {

        return (!_.has(response.get('geo_info'), 'geometry') &&
                _.has(response.get('geo_info'), 'centroid'));
      }), function (response) {

        // Record the objects as rendered so we don't render it twice.
        var id = response.get('id');
        renderedParcelTracker[id] = true;

        // Set up the feature
        var feature = {
          type: 'Feature',
          id: id,
          geometry: {
            type: 'Point',
            coordinates: response.get('geo_info').centroid
          }
        };

        return feature;
      });

      // If we found some full-geometry responses, set up the style and pass
      // them to Leaflet.
      if (featureCollection.features.length > 0) {
        // TODO: THE DATA IS GONE so the STYLE IS GONE.
        var featureLayer = new L.geoJson(featureCollection, {
          pointToLayer: this.defaultPointToLayer,
          style: this.styleFeature
        });
        featureLayer.on('click', this.selectObject);

        // Add the layer to the layergroup and the hashmap
        this.objectsOnTheMap.addLayer(featureLayer);
      }

      // If we found some centroid-only responses, set up the style and pass
      // them to Leaflet.
      if (pointCollection.features.length > 0) {
        var pointLayer = new L.geoJson(pointCollection, {
          pointToLayer: this.defaultPointToLayer,
          style: settings.circleMarker
        });
        pointLayer.on('click', this.selectObject);

        // Add the layer to the layergroup and the hashmap
        this.objectsOnTheMap.addLayer(pointLayer);
      }

      if (featureCollection.features.length > 0 || pointCollection.features.length > 0) {
        this.delayFitBounds();
      }
    },

    // Plot the zones of a survey
    plotZones: function () {
      if (this.survey.has('zones')) {
        var zones = this.survey.get('zones');

        // Clear the old layer
        this.zoneLayer.clearLayers();

        // Create and add the new layer
        this.zoneLayer.addLayer(new L.geoJson(zones, {
          style: zoneStyle
        }));

        // Fit the map to the zones, if appropriate.
        this.delayFitBounds();

        // Make sure objects are on top
        this.objectsOnTheMap.bringToFront();
      }
    },

    updateObjectStyles: function(style) {
      this.objectsOnTheMap.setStyle(style);
    },

    // Expects an object with properties
    // obj.parcelId: ID of the given object
    // obj.geometry: GeoJSON geometry object
    renderObject: function(obj, style, pointToLayer) {

      if (style === undefined) {
        style = this.defaultStyle;
      }

      if (pointToLayer === undefined) {
        pointToLayer = this.defaultPointToLayer;
      }

      // We don't want to re-draw parcels that are already on the map
      // So we keep a hash map with the layers so we can unrender them
      if(! _.has(this.parcelIdsOnTheMap, obj.parcelId) || obj.parcelId === ''){

        // Make sure the format fits Leaflet's geoJSON expectations
        obj.type = "Feature";

        // Create a new geojson layer and style it.
        var geojsonLayer = new L.geoJson(obj, {
          pointToLayer: pointToLayer,
          style: style
        });
        geojsonLayer.on('click', this.selectObject);

        // Add the layer to the layergroup and the hashmap
        this.objectsOnTheMap.addLayer(geojsonLayer); // was (geojsonLayer);
        this.parcelIdsOnTheMap[obj.parcelId] = geojsonLayer;
      }
    },

    renderObjects: function(results) {
      _.each(results, function(elt) {
        this.renderObject(elt);
      }, this);
    },

    updateMapStyleBasedOnZoom: function(e) {
      // Don't update the styles if there's a filter in place
      if (this.filter !== null) {
        return;
      }

      _kmq.push(['record', "Map zoomed"]);
      var zoom = this.map.getZoom();
      console.log(zoom);

      // Objects should be more detailed close up (zoom 14+)
      if(zoom > 13) {

        if(zoom > 15) {
          if(this.defaultStyle !== settings.closeZoomStyle) {
            this.defaultStyle = settings.closeZoomStyle;
            this.updateObjectStyles(settings.closeZoomStyle);
          }
        } else {
          // Mid zoom (11-14)
          // We're not that close, show the mid zoom styles
          if(this.defaultStyle !== settings.midZoomStyle) {
            this.defaultStyle = settings.midZoomStyle;
            this.updateObjectStyles(settings.midZoomStyle);
          }
        }

      }else {
        // Far zoom (>13)
        // Show a more abstract map when zoomed out
        if(this.defaultStyle !== settings.farZoomStyle) {
          this.defaultStyle = settings.farZoomStyle;
          this.updateObjectStyles(settings.farZoomStyle);
        }
      }

      // If a parcel is selected, make sure it says visually selected
      if (this.selectedLayer !== null) {s
        this.selectedLayer.setStyle(settings.selectedStyle);
      }
    },

    /**
     * Get all the responses in the current viewport
     */
    getResponsesInBounds: function(){
      console.log("Getting responses in the map");

      // Don't add any markers if the zoom is really far out.
      var zoom = this.map.getZoom();
      if(zoom < 17) {
        return;
      }

      // Get the objects in the bounds
      // And add them to the map
      api.getResponsesInBounds(this.map.getBounds(), this.addResultsToMap);
    },

    /**
     * Highlight a selected object; un-hilight any previously selected object
     * @param  {Object} event
     */
    selectObject: function(event) {
      _kmq.push(['record', "Map object selected"]);

      // Visually deselect the previous style
      if (this.selectedLayer !== null) {
        var originalStyle = this.styleFeature(this.selectedLayer.feature);
        this.selectedLayer.setStyle(originalStyle);
      }

      // Select the current layer
      this.selectedLayer = event.layer;
      this.selectedLayer.setStyle(settings.selectedStyle);

      // Let's show some info about this object.
      this.details(this.selectedLayer.feature);
    },

    /**
     * Show details for a particular feature.
     *
     * @param  {Object} options An object with a parcelId or id property
     */
    details: function(feature) {
      // Find out if we're looking up a set of parcels, or one point
      if(feature.parcelId !== undefined && feature.parcelId !== '') {
        this.sel = new Responses.Collection(this.responses.where({'parcel_id': feature.parcelId}));
      }else {
        this.sel = new Responses.Collection(this.responses.where({'id': feature.id}));
      }

      var selectedItemListView = new ResponseListView({collection: this.sel});
      $("#result-container").html(selectedItemListView.render().$el);
    }

  });

  return MapView;
});
<commit_msg>Remove log<commit_after>/*jslint nomen: true */
/*globals define: true */

define([
  // Libraries
  'jquery',
  'lib/lodash',
  'backbone',
  'lib/leaflet/leaflet.google',
  'moment',
  'lib/tinypubsub',
  'lib/kissmetrics',

  // LocalData
  'settings',
  'api',

  // Views
  'views/responses/list',

  // Models
  'models/responses'
],

function($, _, Backbone, L, moment, events, _kmq, settings, api, ResponseListView, Responses) {
  'use strict';

  function indexToColor(index) {
    if (index >= 0) {
      return settings.colorRange[index + 1];
    }
    return settings.colorRange[0];
  }

  function getFeatureStyle(feature) {
    if (feature.properties === undefined || feature.properties.color === undefined) {
      return settings.styleTemplate;
    }

    var style = {};
    _.extend(style, settings.styleTemplate, {
      color: feature.properties.color,
      fillColor: feature.properties.color
    });
    return style;
  }

  function zoneStyle(feature) {
    return {
      color: feature.properties.color,
      opacity: 0.3,
      fillColor: feature.properties.color,
      fillOpacity: 0.15,
      weight: 2
    };
  }

  var MapView = Backbone.View.extend({

    map: null,
    responses: null,
    survey: null,
    paginationView: null,
    selectedLayer: null,
    filtered: false,
    selectedObject: {},
    markers: {},
    filter: null,

    initialize: function(options) {
      console.log("Init map view");
      _.bindAll(this, 'render', 'selectObject', 'renderObject', 'renderObjects',
        'getResponsesInBounds', 'updateMapStyleBasedOnZoom', 'updateObjectStyles',
        'styleFeature', 'setupPolygon');

      this.responses = options.responses;
      // TODO: if we add the filter logic to the responses collection, we can
      // more cleanly trigger off its events.
      this.listenTo(this.responses, 'reset', this.render);
      this.listenTo(this.responses, 'addSet', this.render);

      this.survey = options.survey;

      // We track the results on the map using these two groups
      this.parcelIdsOnTheMap = {};
      this.objectsOnTheMap = new L.FeatureGroup();
      this.zoneLayer = new L.FeatureGroup();

      this.defaultStyle = settings.farZoomStyle;
      this.defaultPointToLayer = function (feature, latlng) {
        return new L.circleMarker(latlng, settings.farZoomStyle);
      };

      this.delayFitBounds = _.debounce(this.fitBounds, 250);

      this.render();
    },

    fitBounds: function () {
      this.map.invalidateSize(false);

      if (this.responses !== null && this.responses.length > 0) {
        try {
          this.map.fitBounds(this.objectsOnTheMap.getBounds());
        } catch (e) {
        }
      } else if (this.survey.has('zones')) {
        try {
          this.map.fitBounds(this.zoneLayer.getBounds());
        } catch (e) {
        }
      }
    },

    // Debounced version of fitBounds. Created in the initialize method.
    delayFitBounds: null,

    render: function (arg) {
      var hasResponses = this.responses !== null && this.responses.length > 0;
      var hasZones = this.survey.has('zones');

      // Don't draw a map if there are no responses and no survey zones to
      // plot.
      if (!hasResponses && !hasZones) {
        return;
      }

      if (this.map === null) {
        // Create the map.
        this.$el.html(_.template($('#map-view').html(), {}));

        // Initialize the map
        this.map = new L.map('map', {
          zoom: 15
        });

        // Set up the base map; add the parcels and done markers
        this.baseLayer = L.tileLayer('http://a.tiles.mapbox.com/v3/matth.map-zmpggdzn/{z}/{x}/{y}.png');
        this.map.addLayer(this.baseLayer);
        this.satelliteLayer = L.tileLayer('http://a.tiles.mapbox.com/v3/matth.map-yyr7jb6r/{z}/{x}/{y}.png');
        this.activeLayer = 'streets';

        this.map.addLayer(this.zoneLayer);
        this.map.addLayer(this.objectsOnTheMap);

        this.map.setView([0,0], 17); // default center
        this.updateMapStyleBasedOnZoom();
        this.map.on('zoomend', this.updateMapStyleBasedOnZoom);
      }

      if (hasZones) {
        // Plot the survey zones.
        this.plotZones();
      } else {
        // We don't start listening in initialize because we might get the
        // change event before we even have a map.
        this.listenTo(this.survey, 'change', this.plotZones);
      }

      // If there are no responses, don't bother trying to plot responses or
      // deal with filters.
      if (!hasResponses) {
        return;
      }

      // TODO: better message passing
      events.publish('loading', [true]);

      if (this.responses.filters === null) {
        this.filter = null;
      }

      if (_.isArray(arg)) {
        // We got an array of models. Let's plot just that set them.
        this.plotResponses(arg);
      } else {
        // Plot all of the responses from the responses collection.
        this.plotResponses();
      }

      events.publish('loading', [false]);
      return this;
    },

    /**
     * Set filter parameters for displaying results on the map
     * The response collection will activate this if we have an active filter
     * @param {String} question Name of the question, eg 'vacant'
     * @param {Object} answers  Possible answers to the qu
     */
    setFilter: function (question, answers) {
      this.filter = {
        question: question,
        answers: answers
      };
      this.plotResponses();
    },

    /**
     * Style a feature
     * @param  {Object} feature
     * @return {Object}         feature with style attributes
     */
    styleFeature: function(data) {
      // Set a default style
      var style = this.defaultStyle;

      // If there's no data, style this as blank
      if(data === undefined || this.filter === null) {
        return style;
      }

      // Set the default filter style
      style = _.extend({
        color: settings.colorRange[0],
        fillColor: settings.colorRange[0]
      }, settings.styleTemplate);

      // Get the answer to the currently filtered question
      var answer = data[this.filter.question];

      // If there's no answer, style this as a blank answer
      if (answer === undefined) {
        return style;
      }

      // Set the line and fill colors, then return the style
      style.color = this.filter.answers[answer].color;
      style.fillColor = style.color;
      return style;
    },

    /**
     * Set up a geojson feature for a response object with a polygon
     * @param  {Object} response a response object with a polygon
     * @return {Object}          GeoJSON feature
     */
    setupPolygon: function(response) {
      // Record the objects as rendered so we don't render it twice.
      var parcelId = response.get('parcel_id');
      this.parcelIdsOnTheMap[parcelId] = true;

      // Set up the default style
      var style = this.defaultStyle;

      // Set up the feature
      var feature = {
        type: 'Feature',
        id: response.get('id'),
        parcelId: parcelId,
        geometry: response.get('geo_info').geometry,
        style: style
      };

      // If there is a filter, attach the filtered question to the feature
      // This is used later to style the feature
      // We don't attach all the data to keep size down
      // (large collections can have 20+ mb of data)
      if (this.filter !== null) {

        // Some responses don't have associated data. It's still important that
        // they appear in the filter, since the user may be looking for empty
        // result sets.
        if(response.get('responses') === undefined) {
          feature[this.filter.question] = undefined;
        }else {
          feature[this.filter.question] = response.get('responses')[this.filter.question];
        }
      }

      // Return the feature for rendering
      return feature;
    },

    /**
     * Plot responses on the map
     * @param  {Array} responses
     */
    plotResponses: function (responses) {
      // If we aren't given an explicit set of responses to plot,
      // we'll plot all of the responses from the collection.
      if (responses === undefined) {

        // Clear out the old results first
        this.objectsOnTheMap.clearLayers();
        this.parcelIdsOnTheMap = {};

        // TODO: not great form to reassign one of the arguments
        responses = this.responses.models;
      }

      var renderedParcelTracker = this.parcelIdsOnTheMap;

      // We'll need to create GeoJSON FeatureCollection objects to pass
      // to Leaflet for rendering.
      var featureCollection = {
        type: 'FeatureCollection',
        features: []
      };

      var pointCollection = {
        type: 'FeatureCollection',
        features: []
      };

      // Populate the FeatureCollection for responses with full geometry.
      featureCollection.features = _.map(
        _.filter(responses, function (response) {

          // Get items that have geometry
          // AND aren't already on the map
          return (_.has(response.get('geo_info'), 'geometry') &&
                 !_.has(renderedParcelTracker, response.get('parcel_id'))
          );
      }), this.setupPolygon);

      // Populate the FeatureCollection for responses with only a centroid.
      pointCollection.features = _.map(
        _.filter(responses, function (response) {

        return (!_.has(response.get('geo_info'), 'geometry') &&
                _.has(response.get('geo_info'), 'centroid'));
      }), function (response) {

        // Record the objects as rendered so we don't render it twice.
        var id = response.get('id');
        renderedParcelTracker[id] = true;

        // Set up the feature
        var feature = {
          type: 'Feature',
          id: id,
          geometry: {
            type: 'Point',
            coordinates: response.get('geo_info').centroid
          }
        };

        return feature;
      });

      // If we found some full-geometry responses, set up the style and pass
      // them to Leaflet.
      if (featureCollection.features.length > 0) {
        // TODO: THE DATA IS GONE so the STYLE IS GONE.
        var featureLayer = new L.geoJson(featureCollection, {
          pointToLayer: this.defaultPointToLayer,
          style: this.styleFeature
        });
        featureLayer.on('click', this.selectObject);

        // Add the layer to the layergroup and the hashmap
        this.objectsOnTheMap.addLayer(featureLayer);
      }

      // If we found some centroid-only responses, set up the style and pass
      // them to Leaflet.
      if (pointCollection.features.length > 0) {
        var pointLayer = new L.geoJson(pointCollection, {
          pointToLayer: this.defaultPointToLayer,
          style: settings.circleMarker
        });
        pointLayer.on('click', this.selectObject);

        // Add the layer to the layergroup and the hashmap
        this.objectsOnTheMap.addLayer(pointLayer);
      }

      if (featureCollection.features.length > 0 || pointCollection.features.length > 0) {
        this.delayFitBounds();
      }
    },

    // Plot the zones of a survey
    plotZones: function () {
      if (this.survey.has('zones')) {
        var zones = this.survey.get('zones');

        // Clear the old layer
        this.zoneLayer.clearLayers();

        // Create and add the new layer
        this.zoneLayer.addLayer(new L.geoJson(zones, {
          style: zoneStyle
        }));

        // Fit the map to the zones, if appropriate.
        this.delayFitBounds();

        // Make sure objects are on top
        this.objectsOnTheMap.bringToFront();
      }
    },

    updateObjectStyles: function(style) {
      this.objectsOnTheMap.setStyle(style);
    },

    // Expects an object with properties
    // obj.parcelId: ID of the given object
    // obj.geometry: GeoJSON geometry object
    renderObject: function(obj, style, pointToLayer) {

      if (style === undefined) {
        style = this.defaultStyle;
      }

      if (pointToLayer === undefined) {
        pointToLayer = this.defaultPointToLayer;
      }

      // We don't want to re-draw parcels that are already on the map
      // So we keep a hash map with the layers so we can unrender them
      if(! _.has(this.parcelIdsOnTheMap, obj.parcelId) || obj.parcelId === ''){

        // Make sure the format fits Leaflet's geoJSON expectations
        obj.type = "Feature";

        // Create a new geojson layer and style it.
        var geojsonLayer = new L.geoJson(obj, {
          pointToLayer: pointToLayer,
          style: style
        });
        geojsonLayer.on('click', this.selectObject);

        // Add the layer to the layergroup and the hashmap
        this.objectsOnTheMap.addLayer(geojsonLayer); // was (geojsonLayer);
        this.parcelIdsOnTheMap[obj.parcelId] = geojsonLayer;
      }
    },

    renderObjects: function(results) {
      _.each(results, function(elt) {
        this.renderObject(elt);
      }, this);
    },

    updateMapStyleBasedOnZoom: function(e) {
      // Don't update the styles if there's a filter in place
      if (this.filter !== null) {
        return;
      }

      _kmq.push(['record', "Map zoomed"]);
      var zoom = this.map.getZoom();

      // Objects should be more detailed close up (zoom 14+)
      if(zoom > 13) {

        if(zoom > 15) {
          if(this.defaultStyle !== settings.closeZoomStyle) {
            this.defaultStyle = settings.closeZoomStyle;
            this.updateObjectStyles(settings.closeZoomStyle);
          }
        } else {
          // Mid zoom (11-14)
          // We're not that close, show the mid zoom styles
          if(this.defaultStyle !== settings.midZoomStyle) {
            this.defaultStyle = settings.midZoomStyle;
            this.updateObjectStyles(settings.midZoomStyle);
          }
        }

      }else {
        // Far zoom (>13)
        // Show a more abstract map when zoomed out
        if(this.defaultStyle !== settings.farZoomStyle) {
          this.defaultStyle = settings.farZoomStyle;
          this.updateObjectStyles(settings.farZoomStyle);
        }
      }

      // If a parcel is selected, make sure it says visually selected
      if (this.selectedLayer !== null) {s
        this.selectedLayer.setStyle(settings.selectedStyle);
      }
    },

    /**
     * Get all the responses in the current viewport
     */
    getResponsesInBounds: function(){
      console.log("Getting responses in the map");

      // Don't add any markers if the zoom is really far out.
      var zoom = this.map.getZoom();
      if(zoom < 17) {
        return;
      }

      // Get the objects in the bounds
      // And add them to the map
      api.getResponsesInBounds(this.map.getBounds(), this.addResultsToMap);
    },

    /**
     * Highlight a selected object; un-hilight any previously selected object
     * @param  {Object} event
     */
    selectObject: function(event) {
      _kmq.push(['record', "Map object selected"]);

      // Visually deselect the previous style
      if (this.selectedLayer !== null) {
        var originalStyle = this.styleFeature(this.selectedLayer.feature);
        this.selectedLayer.setStyle(originalStyle);
      }

      // Select the current layer
      this.selectedLayer = event.layer;
      this.selectedLayer.setStyle(settings.selectedStyle);

      // Let's show some info about this object.
      this.details(this.selectedLayer.feature);
    },

    /**
     * Show details for a particular feature.
     *
     * @param  {Object} options An object with a parcelId or id property
     */
    details: function(feature) {
      // Find out if we're looking up a set of parcels, or one point
      if(feature.parcelId !== undefined && feature.parcelId !== '') {
        this.sel = new Responses.Collection(this.responses.where({'parcel_id': feature.parcelId}));
      }else {
        this.sel = new Responses.Collection(this.responses.where({'id': feature.id}));
      }

      var selectedItemListView = new ResponseListView({collection: this.sel});
      $("#result-container").html(selectedItemListView.render().$el);
    }

  });

  return MapView;
});
<|endoftext|>


<commit_before>var express = require('express');
var cors = require('cors');

var situations = require('../controllers/situations');
var teleservices = require('../controllers/teleservices');

module.exports = function(api) {
    api.route('/situations').post(situations.create);

    var route = new express.Router({ mergeParams: true });
    route.use(situations.validateAccess);

    route.get('/', situations.show);
    route.get('/openfisca-response', situations.openfiscaResponse);
    route.get('/legacy-openfisca-request', situations.openfiscaRequestFromLegacy);

    // Enable CORS for openfisca-tracer
    route.options('/openfisca-request', cors());
    route.get('/openfisca-request', cors({ origin: '*' }), situations.openfiscaRequest);

    route.post('/openfisca-test', situations.openfiscaTest);
    route.get('/openfisca-trace', situations.openfiscaTrace);

    teleservices.names.forEach(function(name) {
        route.get('/' + name,
            teleservices.metadataResponseGenerator(teleservices[name])
        );
    });

    api.options('/situations/via/:signedPayload', cors());
    api.get('/situations/via/:signedPayload',
        cors({ origin: '*' }),
        teleservices.checkCredentials,
        teleservices.attachPayloadSituation,
        teleservices.verifyRequest,
        teleservices.exportRepresentation);
    api.use('/situations/:situationId', route);

    /*
    ** Param injection
    */
    api.param('situationId', situations.situation);
    api.param('signedPayload', teleservices.decodePayload);
};
<commit_msg>Ajoute cookie-parser dans l'API<commit_after>var express = require('express');
var cookieParser = require('cookie-parser');
var cors = require('cors');

var situations = require('../controllers/situations');
var teleservices = require('../controllers/teleservices');

module.exports = function(api) {
    api.route('/situations').post(situations.create);

    var route = new express.Router({ mergeParams: true });
    route.use(cookieParser());
    route.use(situations.validateAccess);

    route.get('/', situations.show);
    route.get('/openfisca-response', situations.openfiscaResponse);
    route.get('/legacy-openfisca-request', situations.openfiscaRequestFromLegacy);

    // Enable CORS for openfisca-tracer
    route.options('/openfisca-request', cors());
    route.get('/openfisca-request', cors({ origin: '*' }), situations.openfiscaRequest);

    route.post('/openfisca-test', situations.openfiscaTest);
    route.get('/openfisca-trace', situations.openfiscaTrace);

    teleservices.names.forEach(function(name) {
        route.get('/' + name,
            teleservices.metadataResponseGenerator(teleservices[name])
        );
    });

    api.options('/situations/via/:signedPayload', cors());
    api.get('/situations/via/:signedPayload',
        cors({ origin: '*' }),
        teleservices.checkCredentials,
        teleservices.attachPayloadSituation,
        teleservices.verifyRequest,
        teleservices.exportRepresentation);
    api.use('/situations/:situationId', route);

    /*
    ** Param injection
    */
    api.param('situationId', situations.situation);
    api.param('signedPayload', teleservices.decodePayload);
};
<|endoftext|>