<commit_before>/*
 * GeoMOOSE Site Configuration
 * This file should never be modified! Please use over rides
 * in the mapbook!
 */

var CONFIGURATION = {
	'links_bar_html' : "",
	'waiting_html' : 'Loading...',
	'mapserver_url' : "",
	'control_panel_side' : 'left',
	'mapfile_root' : "",
	'mapbook' : "php/getmapbook.php",
	'fractional_zoom' : false,
	'scales' : [1,2,4,8,16,32,64,128,256,500,1000,5000],
	'max_extent' : [189783.560000,4816309.330000,761653.524114,5472346.500000],
	'initial_extent' : [410438.542057,5125011.551363637,540998.542057,5163644.278636363],
	'projection' : "EPSG:26915",
	'ground_units' : 'm',
	'coordinate_display' : {
		xy: true,	/* Ground Units */
		latlon: true,	/* Longitude and Latitude */
		usng: true	/* U.S. National Grid */
	},
	'catalog_name' : 'Catalog',
	'group_checkboxes' : true,
	'flashy_bits' : true,
	'zoomto' : {},
	'jumpto_scales' : {
		'1:12000' : 12000,
		'1:2400' : 2400,
		'1:1200' : 1200,
		'1:120' : 120
	},
	'startup_service' : false,
	'toolbar' : {
		'show_labels' : false
	},
	'layer_control_order' : ['activate', 'popups', 'up','down','fade','unfade','refresh','cycle','legend','metadata'],
	'layer_controls' : {
		'activate' : {on: false, tip: 'Activate ${layer} for use with layer contextual tools.'},
		'popups' : {on: false, tip: 'Show Floating Popups for ${layer}'},
		/* Up and down are essentially deprecated, Duck 06/09/2012 */
		'up' : {on: false, tip: 'Move ${layer} up on the stack'},
		'down' : {on: false, tip: 'Move ${layer} down on the stack'},
		'fade' : {on: true, change_percent: 10, tip: 'Fade ${layer}'},
		'unfade' : {on: true, change_percent: 10, tip: 'Unfade ${layer}'},
		'refresh' : {on: false, tip: 'Manually refresh ${layer}'},
		'cycle' : {on: false, tip: 'Refresh ${layer} every 10 seconds', seconds: 10},
		'legend' : {on: true, tip: 'Show legend for ${layer}'},
		'metadata' : {on: true, tip: 'Show metadata for ${layer}'}
	},
	'layer_options' : {
		'transitionEffect': "null",
		'buffer': 0,
		'ratio': 1
	},
	'catalog' : {
		'toggle_controls' : true,
		'show_controls' :  true
	},
	'reference_map' : {
		'enabled' : true,
		'maximized' : true,
		'width': 250,
		'height':  100,
		'minimum_ratio' : 10,
		'maximum_ratio' : 20
	},
	'cursor' : {
		pan: "url(cursors/pan.gif) 9 9,url(cursors/pan.cur),move",
		zoomout: "url(cursors/zoomout2.gif) 5 5,url(cursors/zoomout2.cur),crosshair",
		zoomin: "url(cursors/zoomin2.gif) 5 5,url(cursors/zoomin2.cur),crosshair",
		measure: "url(cursors/measure2.gif),url(cursors/measure2.cur),crosshair",
		measurearea: "url(cursors/measure2.gif),url(cursors/measure2.cur),crosshair"
	},
	'scale_line' : {
		'enabled' : true,
		'top_units' : 'ft',
		'bottom_units' : 'mi',
		'width' : 200
	},
	'measure_tool' : {
		'precision' : 3,
		'style' : {
			"Point" : {
				pointRadius: 4,
				graphicName: "square",
				fillColor: "white",
				fillOpacity: 1,
				strokeWidth: 1,
				strokeOpacity: 1,
				strokeColor: "#333333"
			},
			"Line" : {
				strokeWidth: 2,
				strokeColor: "#FF0000"
			},
			"Polygon" : {
				strokeWidth: 2,
				strokeColor: "#FF0000",
				fillColor: "#00FF00",
				fillOpacity: .5
			}
		},
		'line_units' : 'ft',
		'area_units' : 'acre',
		'show_heading' : false
	},
	default_tool: 'pan',
	popups: {
		left_offset: 43
	},
	services: {
		disable_hidden_tabs: false
	},
	messages: {
		requirement_failure : 'You forgot to fill in a required field!',
		invalid_tool : 'SERVICE CONFIGURATION ERROR! Drawing tool %TOOL% not available!',
		service_config_error : 'SERVICE CONFIGURATION ERROR! You must specify a default tool when setting "show-tools" to "true"!',
		mapbook_invalid: 'Could not load a mapbook. If you are the system administrator, you should look into this, if you are not the system administrator you should tell them about this. Suggestions:

The mapbook may not be returning XML.

The mapbook may be returning malformed XML.

PHP may not be responding.

',
		mapbook_version : 'Specified mapbook version %VERSION% is older than 2.0. No promises this will work.',
		mapbook_param_error : 'Error reading the value of "%FIELD%", please check your mapbook configuration.',
		service_return_error : 'The service you called did not return valid XML. If you are the system administrator, you should look into this, if you are not the system administrator you should tell them about this.'
	}
};
<commit_msg>added comments to the config.js file to describe all options, refs: geomoose/geomoose#42<commit_after>/*
 * GeoMOOSE Site Configuration
 * This file should never be modified! Please use over rides
 * in the mapbook!
 */

var CONFIGURATION = {
	/** links_bar_html - HTML to be contained in the bar after the header and before the toolbar. */
	'links_bar_html' : "",
	/** waiting_html - Message to use when a user is waiting for something to load. */
	'waiting_html' : 'Loading...',
	/** mapserver_url - The URL to use for MapServer-type map sources. */
	'mapserver_url' : "",
	/** control_panel_side - Change which side on which the control panel resides. */
	'control_panel_side' : 'left',
	/** mapfile_root - The filesystem path on which all mapfiles reside. Used for Mapserver-type map sources. */
	'mapfile_root' : "",
	/** mapbook - The URL to the mapbook. Defaults to php/getmapbook.php. */
	'mapbook' : "php/getmapbook.php",
	/** fractional_zoom - Toggles whether 'in between' zooms or strict steps should be supported. */
	'fractional_zoom' : false,
	/** scales - List of scales to be used in the zoom-ladder. */
	'scales' : [1,2,4,8,16,32,64,128,256,500,1000,5000],
	/** max_extent - The maximum bounding box of the map. */
	'max_extent' : [189783.560000,4816309.330000,761653.524114,5472346.500000],
	/** initial_extent - The initial bounding box view of the map. */
	'initial_extent' : [410438.542057,5125011.551363637,540998.542057,5163644.278636363],
	/** projection - The projection to be used for the map. */
	'projection' : "EPSG:26915",
	/** ground_units - Set the ground units of the map. Defaults to 'm'. */
	'ground_units' : 'm',
	'coordinate_display' : {
		xy: true,	/** coordinate_display.xy - Toggle whether Ground Units will be displayed in the coordinate display. Defaults true. */
		latlon: true,	/** coordinate_display.latlon - Toggle whether Longitude and Latitude will be displayed in the coordinate display. Defaults true. */
		usng: true	/** coordinate_display.usng - Toggle whether U.S. National Grid will be displayed in the coordinate display. Defaults true. */
	},
	/** catalog_name - Set the catalog's label. Defaults to 'Catalog' */
	'catalog_name' : 'Catalog',
	/** group_checkboxes - Allow entire groups to be toggled on or off with a global checkbox. Defaults to true.  */
	'group_checkboxes' : true,
	/** flashy_bits - Toggles Animations on or off. Defaults to true*/
	'flashy_bits' : true,
	/** zoomto - Object representing the zoom to drop downs. */
	'zoomto' : {},
	/** jumpto_scales - Object containing the label:scale values for the scale jumper. */
	'jumpto_scales' : {
		'1:12000' : 12000,
		'1:2400' : 2400,
		'1:1200' : 1200,
		'1:120' : 120
	},
	/** startup_service - Service to call at startup. Defaults to false */
	'startup_service' : false,
	'toolbar' : {
		'show_labels' : false /** toolbar.show_labels - Toggles whether the toolbar should render all labels by default. Defaults to false. */
	},
	/** layer_control_order - Changes the display order of the layer controls.  Stored as an array. */
	'layer_control_order' : ['activate', 'popups', 'up','down','fade','unfade','refresh','cycle','legend','metadata'],
	/** layer_controls - object containing objects describing the layer controls. */
	'layer_controls' : {
		'activate' : {on: false, tip: 'Activate ${layer} for use with layer contextual tools.'},
		'popups' : {on: false, tip: 'Show Floating Popups for ${layer}'},
		/* Up and down are essentially deprecated, Duck 06/09/2012 */
		'up' : {on: false, tip: 'Move ${layer} up on the stack'},
		'down' : {on: false, tip: 'Move ${layer} down on the stack'},
		'fade' : {on: true, change_percent: 10, tip: 'Fade ${layer}'},
		'unfade' : {on: true, change_percent: 10, tip: 'Unfade ${layer}'},
		'refresh' : {on: false, tip: 'Manually refresh ${layer}'},
		'cycle' : {on: false, tip: 'Refresh ${layer} every 10 seconds', seconds: 10},
		'legend' : {on: true, tip: 'Show legend for ${layer}'},
		'metadata' : {on: true, tip: 'Show metadata for ${layer}'}
	},
	'layer_options' : {
		/** layer_options.transitionEffect - OpenLayers defined transition effect for all layers. Defaults to null. */
		'transitionEffect': "null",
		/** layer_options.buffer - OpenLayers vector layer buffer radius. Defaults to 0. */
		'buffer': 0,
		/** layer_options.ratio - OpenLayers "ratio" for vector layers. Defaults to 1. */
		'ratio': 1
	},
	'catalog' : {
		/** catalog.toggle_controls - Turn on or off the ability to hide the layer controls in the catalog. Defaults to true. */
		'toggle_controls' : true,
		/** catalog.show_controls - Turn on or off the controls in the catalog. Defaults to true. */
		'show_controls' :  true
	},
	/** reference_map - Not yet implemented. */
	'reference_map' : {
		'enabled' : true,
		'maximized' : true,
		'width': 250,
		'height':  100,
		'minimum_ratio' : 10,
		'maximum_ratio' : 20
	},
	/** cursor - Object of CSS cursor definitions */
	'cursor' : {
		/** cursor.pan - CSS for pan tool. */
		pan: "url(cursors/pan.gif) 9 9,url(cursors/pan.cur),move",
		/** cursor.zoomout - CSS for zoomout tool. */
		zoomout: "url(cursors/zoomout2.gif) 5 5,url(cursors/zoomout2.cur),crosshair",
		/** cursor.zoomin - CSS for zoomin tool. */
		zoomin: "url(cursors/zoomin2.gif) 5 5,url(cursors/zoomin2.cur),crosshair",
		/** cursor.measure - CSS for measure tool. */
		measure: "url(cursors/measure2.gif),url(cursors/measure2.cur),crosshair",
		/** cursor.measurearea - CSS for measurearea tool. */
		measurearea: "url(cursors/measure2.gif),url(cursors/measure2.cur),crosshair"
	},
	'scale_line' : {
		/** scale_line.enabled - Toggle whether the scale lien should be displayed on the map. Defaults to true. */
		'enabled' : true,
		/** scale_line.top_units - What units to display on the top of the scale line. */
		'top_units' : 'ft',
		/** scale_line.bottom_units - What units to display on the bottom of the scale line. */
		'bottom_units' : 'mi',
		/** scale_line.width - The width, in pixels, of the scale line. */
		'width' : 200
	},
	/** measure_tool - options for the measure tools. */
	'measure_tool' : {
		/** measure_tool.precision - Number of digits to display in the readings. */
		'precision' : 3,
		/** measure_tool.style - OpenLayers style object. */
		'style' : {
			"Point" : {
				pointRadius: 4,
				graphicName: "square",
				fillColor: "white",
				fillOpacity: 1,
				strokeWidth: 1,
				strokeOpacity: 1,
				strokeColor: "#333333"
			},
			"Line" : {
				strokeWidth: 2,
				strokeColor: "#FF0000"
			},
			"Polygon" : {
				strokeWidth: 2,
				strokeColor: "#FF0000",
				fillColor: "#00FF00",
				fillOpacity: .5
			}
		},
		/** measure_tool.line_units - Default units to use for Line measurements. */
		'line_units' : 'ft',
		/** measure_tool.area_units - Default units to use for Area measurements. */
		'area_units' : 'acre'
	},
	/** default_tool - Tool to default to when GeoMOOSE.activateDefaultTool is called. */
	default_tool: 'pan',
	/** popups - options for controlling the behavior of popups. */
	popups: {
		/** popups.left_offset - This is used in conjunction with popup CSS styling to properly position the mouse after a popup has been dragged. */
		left_offset: 43
	},
	/** services - Options for controlling the behaviour of services. */
	services: {
		/** services.disable_hidden_tabs - Toggle whether to disable tables when they have been hidden. Defaults to false. */
		disable_hidden_tabs: false
	},
	/** messages - Object of error messages. */
	messages: {
		/** messages.requirement_failure - Shown when the user does not fill in a required feild. */
		requirement_failure : 'You forgot to fill in a required field!',
		/** messages.invalid_tool - Shown when a user tries to start a tool which is not properly configured. */
		invalid_tool : 'SERVICE CONFIGURATION ERROR! Drawing tool %TOOL% not available!',
		/** messages.service_config_error - Shown when an admin does not properly configure the tools for a service. */
		service_config_error : 'SERVICE CONFIGURATION ERROR! You must specify a default tool when setting "show-tools" to "true"!',
		/** messages.mapbook_invalid - Shown when the server does not return a valid mapbook contents. */
		mapbook_invalid: 'Could not load a mapbook. If you are the system administrator, you should look into this, if you are not the system administrator you should tell them about this. Suggestions:

The mapbook may not be returning XML.

The mapbook may be returning malformed XML.

PHP may not be responding.

',
		/** message.mapbook_version - Shown when a user tries to load a 1.X version of the mapbook. */
		mapbook_version : 'Specified mapbook version %VERSION% is older than 2.0. No promises this will work.',
		/** mapbook_param_error - Shown when the mapbook contains an invalid <param> tag. */
		mapbook_param_error : 'Error reading the value of "%FIELD%", please check your mapbook configuration.',
		/** messages.service_return_error - Shown when a service failes to return valid XML. */
		service_return_error : 'The service you called did not return valid XML. If you are the system administrator, you should look into this, if you are not the system administrator you should tell them about this.'
	}
};
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

GEN('#include "chrome/test/data/webui/ntp4_browsertest.h"');

/**
 * TestFixture for NTP4 WebUI testing.
 * @extends {testing.Test}
 * @constructor
 */
function NTP4WebUITest() {}

NTP4WebUITest.prototype = {
  __proto__: testing.Test.prototype,

  /** @override */
  browsePreload: 'chrome://newtab',
};

// Test loading new tab page and selecting each card doesn't have console
// errors.
TEST_F('NTP4WebUITest', 'TestBrowsePages', function() {
  // This tests the ntp4 new tab page which is not used on touch builds.
  var cardSlider = ntp.getCardSlider();
  assertNotEquals(null, cardSlider);
  for (var i = 0; i < cardSlider.cardCount; i++) {
    cardSlider.selectCard(i);
    expectEquals(i, cardSlider.currentCard);
  }
});

TEST_F('NTP4WebUITest', 'NTPHasThumbnails', function() {
  var mostVisited = document.querySelectorAll('.most-visited');
  assertEquals(8, mostVisited.length, 'There should be 8 most visited tiles.');

  var apps = document.querySelectorAll('.app');
  if (window.templateData.showApps)
    assertGE(apps.length, 1, 'There should be at least one app.');
  else
    assertEquals(0, apps.length, 'There should be no apps.');
});

TEST_F('NTP4WebUITest', 'NTPHasNavDots', function() {
  var navDots = document.querySelectorAll('.dot');
  if (window.templateData.showApps)
    assertGE(navDots.length, 2, 'There should be at least two navdots.');
  else
    assertEquals(1, navDots.length, 'There should be exactly one navdot.');
});

TEST_F('NTP4WebUITest', 'NTPHasSelectedPageAndDot', function() {
  var selectedDot = document.querySelectorAll('.dot.selected');
  assertEquals(1, selectedDot.length,
               'There should be exactly one selected dot.');

  var selectedTilePage = document.querySelectorAll('.tile-page.selected-card');
  assertEquals(1, selectedTilePage.length,
               'There should be exactly one selected tile page.');
});

TEST_F('NTP4WebUITest', 'NTPHasNoLoginNameWhenSignedOut', function() {
  var userName = document.querySelector('#login-status-header .profile-name');
  assertEquals(null, userName, 'Login name shouldn\'t exist when signed out.');
});

/**
 * Test fixture for NTP4 WebUI testing with login.
 * @extends {NTP4WebUITest}
 * @constructor
 */
function NTP4LoggedInWebUITest() {}

NTP4LoggedInWebUITest.prototype = {
  __proto__: NTP4WebUITest.prototype,

  /** @override */
  typedefCppFixture: 'NTP4LoggedInWebUITest',

  /** @override */
  testGenPreamble: function() {
    GEN('  SetLoginName("user@gmail.com");');
  },
};

// The following test is irrelevant to Chrome on Chrome OS.
GEN('#if !defined(OS_CHROMEOS)');

TEST_F('NTP4LoggedInWebUITest', 'NTPHasLoginNameWhenSignedIn', function() {
  var userName = document.querySelector('#login-status-header .profile-name');
  assertNotEquals(userName, null, 'The logged-in user name can\'t be found.');
  assertEquals('user@gmail.com', userName.textContent,
               'The user name should be present on the new tab.');
});

GEN('#endif');
<commit_msg>Disabled a flaky test.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

GEN('#include "chrome/test/data/webui/ntp4_browsertest.h"');

/**
 * TestFixture for NTP4 WebUI testing.
 * @extends {testing.Test}
 * @constructor
 */
function NTP4WebUITest() {}

NTP4WebUITest.prototype = {
  __proto__: testing.Test.prototype,

  /** @override */
  browsePreload: 'chrome://newtab',
};

// Test loading new tab page and selecting each card doesn't have console
// errors.
TEST_F('NTP4WebUITest', 'TestBrowsePages', function() {
  // This tests the ntp4 new tab page which is not used on touch builds.
  var cardSlider = ntp.getCardSlider();
  assertNotEquals(null, cardSlider);
  for (var i = 0; i < cardSlider.cardCount; i++) {
    cardSlider.selectCard(i);
    expectEquals(i, cardSlider.currentCard);
  }
});

TEST_F('NTP4WebUITest', 'NTPHasThumbnails', function() {
  var mostVisited = document.querySelectorAll('.most-visited');
  assertEquals(8, mostVisited.length, 'There should be 8 most visited tiles.');

  var apps = document.querySelectorAll('.app');
  if (window.templateData.showApps)
    assertGE(apps.length, 1, 'There should be at least one app.');
  else
    assertEquals(0, apps.length, 'There should be no apps.');
});

TEST_F('NTP4WebUITest', 'NTPHasNavDots', function() {
  var navDots = document.querySelectorAll('.dot');
  if (window.templateData.showApps)
    assertGE(navDots.length, 2, 'There should be at least two navdots.');
  else
    assertEquals(1, navDots.length, 'There should be exactly one navdot.');
});

// http://crbug.com/118514
TEST_F('NTP4WebUITest', 'FLAKY_NTPHasSelectedPageAndDot', function() {
  var selectedDot = document.querySelectorAll('.dot.selected');
  assertEquals(1, selectedDot.length,
               'There should be exactly one selected dot.');

  var selectedTilePage = document.querySelectorAll('.tile-page.selected-card');
  assertEquals(1, selectedTilePage.length,
               'There should be exactly one selected tile page.');
});

TEST_F('NTP4WebUITest', 'NTPHasNoLoginNameWhenSignedOut', function() {
  var userName = document.querySelector('#login-status-header .profile-name');
  assertEquals(null, userName, 'Login name shouldn\'t exist when signed out.');
});

/**
 * Test fixture for NTP4 WebUI testing with login.
 * @extends {NTP4WebUITest}
 * @constructor
 */
function NTP4LoggedInWebUITest() {}

NTP4LoggedInWebUITest.prototype = {
  __proto__: NTP4WebUITest.prototype,

  /** @override */
  typedefCppFixture: 'NTP4LoggedInWebUITest',

  /** @override */
  testGenPreamble: function() {
    GEN('  SetLoginName("user@gmail.com");');
  },
};

// The following test is irrelevant to Chrome on Chrome OS.
GEN('#if !defined(OS_CHROMEOS)');

TEST_F('NTP4LoggedInWebUITest', 'NTPHasLoginNameWhenSignedIn', function() {
  var userName = document.querySelector('#login-status-header .profile-name');
  assertNotEquals(userName, null, 'The logged-in user name can\'t be found.');
  assertEquals('user@gmail.com', userName.textContent,
               'The user name should be present on the new tab.');
});

GEN('#endif');
<|endoftext|>


<commit_before>/* global $ch, $$CHOP */
$ch.define('event', function () {
  'use strict';
  var events = {};
  var watches = {};

  $$CHOP.originalSourceFunction = $$CHOP.source;

  var objLength = function (obj) {
    var num = 0;
    for (var item in obj) {
      if (obj.hasOwnProperty(item)) {
        ++num;
      }
    }

    return num;
  };

  var getEventType = function (element) {
    var event = 'change';
    if (element.tagName.toUpperCase() === 'TEXTAREA') {
      event = 'keypress';
    }
    else if (element.tagName.toUpperCase() === 'INPUT') {
      var typeAttribute = element.getAttribute('type').toUpperCase();
      var isTextInput = typeAttribute === 'TEXT' || typeAttribute === 'PASSWORD';
      if (isTextInput) {
        event = 'keypress';
      }
    }
    return event;
  };

  $$CHOP.event = {};

  $$CHOP.event = {
    watches: watches,

    listen: function (evt, callback) {
      if (arguments.length === 0) {
        throw new Error('$ch.event.listen requires at least one parameter.');
      }
      else if (arguments.length === 1) {
        var result;
        if (events[evt] !== undefined) {
          result = events[evt]._callback;
        } else {
          result = undefined;
        }
        return result;
      }

      events[evt] = {};
      events[evt]._event = evt;
      events[evt]._callback = callback;
    },

    emit: function (evt, data) {
      if (arguments.length === 0) {
        throw new Error('$ch.event.emit requires at least one parameter.');
      }

      if (events[evt] === undefined) {
        return undefined;
      }

      events[evt]._callback(data);
    },

    watch: function (name, callback) {
      if (arguments.length !== 2) {
        throw new Error('$ch.event.watch requires two parameters. i.e. data-source name, callback.');
      }

      var data = $$CHOP.sources[name].data;
      var value;
      if ($$CHOP._isArray(data)) {
        value = data.slice(0);
      }
      else if (typeof data === 'object') {
        value = Object.create(data);
      }
      else {
        value = data;
      }

      watches[name] = {
        source: name,
        old: value,
        callback: callback
      };

      var watchChange = function () {
        var changes = {};
        var current = $$CHOP.sources[name].data;
        changes.current = current;
        changes.old = watches[name].old;
        callback(changes);
        watches[name].old = current;
      };

      watches[name].watchChange = watchChange;

      for (var index = 0, len = $$CHOP.sources[name].els.length; index !== len; ++index) {
        var element = $$CHOP.sources[name].els[index];
        var event = getEventType(element);
        element.addEventListener(event, watchChange);
      }
    },

    unwatch: function (name) {
      if (arguments.length !== 1) {
        throw new Error('$ch.event.unwatch has to be given a data-source name parameter.');
      }

      for (var index = 0, len = $$CHOP.sources[name].els.length; index !== len; ++index) {
        var element = $$CHOP.sources[name].els[index];
        var event = getEventType(element);
        element.removeEventListener(event, watches[name].watchChange);
      }
      delete watches[name];
    }
  };

  var checkWath = function (key) {
    var item = watches[key];
    if (item === undefined) {
      return undefined;
    }

    var current = $$CHOP.sources[item.source].data;
    var hasChanged = false;
    if (typeof current !== typeof item.old) {
      hasChanged = true;
    }

    else if ($$CHOP._isArray(current)) {
      if (current.length !== item.old.length) {
        hasChanged = true;
      } else {
        for (var index = 0, len = current.length; index !== len; ++index) {
          if (current[index] !== item.old[index]) {
            hasChanged = true;
            break;
          }
        }
      }
    }

    else if (typeof current === 'object' && hasChanged === false) {
      if (objLength(current) !== objLength(item.old)) {
        hasChanged = true;
      } else {
        $$CHOP.each(current, function (key) {
          if (current[key] !== item.old[key]) {
            hasChanged = true;
          }
        });
      }
    }

    else if (current !== item.old && hasChanged === false){
      hasChanged = true;
    }

    if (hasChanged === true) {
      item.current = current;
      item.old = current;
      item.callback(item);
    }

  };

  var eventSource = function (name, data) {
    if (arguments.length === 0) {
      return $$CHOP.originalSourceFunction();
    }
    else if (arguments.length === 1) {
      return $$CHOP.originalSourceFunction(name);
    }
    else {
      $$CHOP.originalSourceFunction(name, data);
      checkWath(name);
    }
  };

  $$CHOP.source = eventSource;
});
<commit_msg>watch keyup<commit_after>/* global $ch, $$CHOP */
$ch.define('event', function () {
  'use strict';
  var events = {};
  var watches = {};

  $$CHOP.originalSourceFunction = $$CHOP.source;

  var objLength = function (obj) {
    var num = 0;
    for (var item in obj) {
      if (obj.hasOwnProperty(item)) {
        ++num;
      }
    }

    return num;
  };

  var getEventType = function (element) {
    var event = 'change';
    if (element.tagName.toUpperCase() === 'TEXTAREA') {
      event = 'keyup';
    }
    else if (element.tagName.toUpperCase() === 'INPUT') {
      var typeAttribute = element.getAttribute('type').toUpperCase();
      var isTextInput = typeAttribute === 'TEXT' || typeAttribute === 'PASSWORD';
      if (isTextInput) {
        event = 'keyup';
      }
    }
    return event;
  };

  $$CHOP.event = {};

  $$CHOP.event = {
    watches: watches,

    listen: function (evt, callback) {
      if (arguments.length === 0) {
        throw new Error('$ch.event.listen requires at least one parameter.');
      }
      else if (arguments.length === 1) {
        var result;
        if (events[evt] !== undefined) {
          result = events[evt]._callback;
        } else {
          result = undefined;
        }
        return result;
      }

      events[evt] = {};
      events[evt]._event = evt;
      events[evt]._callback = callback;
    },

    emit: function (evt, data) {
      if (arguments.length === 0) {
        throw new Error('$ch.event.emit requires at least one parameter.');
      }

      if (events[evt] === undefined) {
        return undefined;
      }

      events[evt]._callback(data);
    },

    watch: function (name, callback) {
      if (arguments.length !== 2) {
        throw new Error('$ch.event.watch requires two parameters. i.e. data-source name, callback.');
      }

      var data = $$CHOP.sources[name].data;
      var value;
      if ($$CHOP._isArray(data)) {
        value = data.slice(0);
      }
      else if (typeof data === 'object') {
        value = Object.create(data);
      }
      else {
        value = data;
      }

      watches[name] = {
        source: name,
        old: value,
        callback: callback
      };

      var watchChange = function () {
        var changes = {};
        var current = $$CHOP.sources[name].data;
        changes.current = current;
        changes.old = watches[name].old;
        callback(changes);
        watches[name].old = current;
      };

      watches[name].watchChange = watchChange;

      for (var index = 0, len = $$CHOP.sources[name].els.length; index !== len; ++index) {
        var element = $$CHOP.sources[name].els[index];
        var event = getEventType(element);
        element.addEventListener(event, watchChange);
      }
    },

    unwatch: function (name) {
      if (arguments.length !== 1) {
        throw new Error('$ch.event.unwatch has to be given a data-source name parameter.');
      }

      for (var index = 0, len = $$CHOP.sources[name].els.length; index !== len; ++index) {
        var element = $$CHOP.sources[name].els[index];
        var event = getEventType(element);
        element.removeEventListener(event, watches[name].watchChange);
      }
      delete watches[name];
    }
  };

  var checkWath = function (key) {
    var item = watches[key];
    if (item === undefined) {
      return undefined;
    }

    var current = $$CHOP.sources[item.source].data;
    var hasChanged = false;
    if (typeof current !== typeof item.old) {
      hasChanged = true;
    }

    else if ($$CHOP._isArray(current)) {
      if (current.length !== item.old.length) {
        hasChanged = true;
      } else {
        for (var index = 0, len = current.length; index !== len; ++index) {
          if (current[index] !== item.old[index]) {
            hasChanged = true;
            break;
          }
        }
      }
    }

    else if (typeof current === 'object' && hasChanged === false) {
      if (objLength(current) !== objLength(item.old)) {
        hasChanged = true;
      } else {
        $$CHOP.each(current, function (key) {
          if (current[key] !== item.old[key]) {
            hasChanged = true;
          }
        });
      }
    }

    else if (current !== item.old && hasChanged === false){
      hasChanged = true;
    }

    if (hasChanged === true) {
      item.current = current;
      item.old = current;
      item.callback(item);
    }

  };

  var eventSource = function (name, data) {
    if (arguments.length === 0) {
      return $$CHOP.originalSourceFunction();
    }
    else if (arguments.length === 1) {
      return $$CHOP.originalSourceFunction(name);
    }
    else {
      $$CHOP.originalSourceFunction(name, data);
      checkWath(name);
    }
  };

  $$CHOP.source = eventSource;
});
<|endoftext|>


<commit_before>'use strict';

module.exports = {
  rules: {
    'block-indentation': 2,
    'no-debugger': true,
    'no-log': true,
    'html-comments': true,
    'nested-interactive': true,
    'self-closing-void-elements': true,
    'triple-curlies': true,
    'link-rel-noopener': true,
    'invalid-interactive': true,
    'img-alt-attributes': true,
    'style-concatenation': true,
    'deprecated-inline-view-helper': true,
    'unless-helper': false,
    'unused-block-params': true
  }
};
<commit_msg>Enable `unless-helper` rule.<commit_after>'use strict';

module.exports = {
  rules: {
    'block-indentation': 2,
    'no-debugger': true,
    'no-log': true,
    'html-comments': true,
    'nested-interactive': true,
    'self-closing-void-elements': true,
    'triple-curlies': true,
    'link-rel-noopener': true,
    'invalid-interactive': true,
    'img-alt-attributes': true,
    'style-concatenation': true,
    'deprecated-inline-view-helper': true,
    'unless-helper': true,
    'unused-block-params': true
  }
};
<|endoftext|>


<commit_before>/* SQB
 ------------------------
 (c) 2017-present Panates
 SQB may be freely distributed under the MIT license.
 For details and documentation:
 https://panates.github.io/sqb/
 */

/* Internal module dependencies. */

/* External module dependencies. */
const assert = require('assert');

/**
 * @class
 * @public
 */

class FieldsMeta {

  constructor(options) {
    this.fields = [];
    this.fieldNaming = options && options.fieldNaming;
  }

  add(obj) {
    if (Array.isArray(obj)) {
      for (const o of obj)
        this.add(o);
      return;
    }
    assert.ok(obj && typeof obj === 'object', 'Invalid argument');
    assert.ok(obj.name, 'Name required');
    assert.ok(!this.has(obj.name), obj.name + ' already defined');
    if (obj.name && this.fieldNaming) {
      obj.name = this.fieldNaming === 'lowercase' ? obj.name.toLowerCase() :
          (this.fieldNaming === 'uppercase' ? obj.name.toUpperCase() :
              obj.name);
    }
    this.fields.push(obj);
  }

  has(fieldName) {
    return this.indexOf(fieldName) >= 0;
  }

  indexOf(fieldName) {
    fieldName = String(fieldName).toUpperCase();
    for (let i = 0; i < this.fields.length; i++) {
      const o = this.fields[i];
      if (o.name.toUpperCase() === fieldName)
        return i;
    }
    return -1;
  }

  get(field) {
    const i = (typeof field === 'number' ? field : this.indexOf(field));
    return i >= 0 ? this.fields[i] : undefined;
  }

  asArray() {
    return JSON.parse(JSON.stringify(this.fields));
  }

  asObject(options) {
    const out = {};
    for (let i = 0; i < this.fields.length; i++) {
      const f = this.fields[i];
      let name = f.name;
      if (options && options.fieldNaming !== this.fieldNaming)
        name = options.fieldNaming === 'lowercase' ? name.toLowerCase() :
            (options.fieldNaming === 'uppercase' ? name.toUpperCase() : name);
      const o = out[name] = Object.assign({}, f);
      delete o.name;
      o.index = i;
    }
    return out;
  }

  getValue(fieldName, row) {
    if (Array.isArray(row)) {
      const i = this.indexOf(fieldName);
      return i >= 0 ? row[i] : undefined;
    }
    if (typeof row === 'object')
      return row[fieldName];
  }

  setValue(fieldName, value, row) {
    if (Array.isArray(row)) {
      const i = this.indexOf(fieldName);
      if (i >= 0)
        row[i] = value;
    }
    if (typeof row === 'object' && row[fieldName] !== undefined)
      row[fieldName] = value;
  }

  toJSON() {
    return this.fields;
  }

}

module.exports = FieldsMeta;
<commit_msg>Fix fieldNaming issue<commit_after>/* SQB
 ------------------------
 (c) 2017-present Panates
 SQB may be freely distributed under the MIT license.
 For details and documentation:
 https://panates.github.io/sqb/
 */

/* Internal module dependencies. */

/* External module dependencies. */
const assert = require('assert');

/**
 * @class
 * @public
 */

class FieldsMeta {

  constructor(options) {
    this.fields = [];
    this.fieldNaming = options && options.fieldNaming;
  }

  add(obj) {
    if (Array.isArray(obj)) {
      for (const o of obj)
        this.add(o);
      return;
    }
    assert.ok(obj && typeof obj === 'object', 'Invalid argument');
    assert.ok(obj.name, 'Name required');
    assert.ok(!this.has(obj.name), obj.name + ' already defined');
    if (obj.name && this.fieldNaming) {
      obj.name = this.fieldNaming === 'lowercase' ? obj.name.toLowerCase() :
          (this.fieldNaming === 'uppercase' ? obj.name.toUpperCase() :
              obj.name);
    }
    this.fields.push(obj);
  }

  has(fieldName) {
    return this.indexOf(fieldName) >= 0;
  }

  indexOf(fieldName) {
    fieldName = String(fieldName).toUpperCase();
    for (let i = 0; i < this.fields.length; i++) {
      const o = this.fields[i];
      if (o.name.toUpperCase() === fieldName)
        return i;
    }
    return -1;
  }

  get(field) {
    const i = (typeof field === 'number' ? field : this.indexOf(field));
    return i >= 0 ? this.fields[i] : undefined;
  }

  asArray() {
    return JSON.parse(JSON.stringify(this.fields));
  }

  asObject(options) {
    const out = {};
    for (let i = 0; i < this.fields.length; i++) {
      const f = this.fields[i];
      let name = f.name;
      if (options && options.fieldNaming &&
          options.fieldNaming !== this.fieldNaming)
        name = options.fieldNaming === 'lowercase' ? name.toLowerCase() :
            (options.fieldNaming === 'uppercase' ? name.toUpperCase() : name);
      const o = out[name] = Object.assign({}, f);
      delete o.name;
      o.index = i;
    }
    return out;
  }

  getValue(fieldName, row) {
    if (Array.isArray(row)) {
      const i = this.indexOf(fieldName);
      return i >= 0 ? row[i] : undefined;
    }
    if (typeof row === 'object')
      return row[fieldName];
  }

  setValue(fieldName, value, row) {
    if (Array.isArray(row)) {
      const i = this.indexOf(fieldName);
      if (i >= 0)
        row[i] = value;
    }
    if (typeof row === 'object' && row[fieldName] !== undefined)
      row[fieldName] = value;
  }

  toJSON() {
    return this._objectRows ? this.asObject() : this.fields;
  }

}

module.exports = FieldsMeta;
<|endoftext|>


<commit_before>/****************************************************************************
 The MIT License (MIT)

 Copyright (c) 2015 Apigee Corporation

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 ****************************************************************************/
'use strict';

var debug = require('debug')('swagger');
var Url = require('url');

module.exports = init;

function init(runner) {
  return new Middleware(runner);
}

function Middleware(runner) {
  this.runner = runner;
  this.config = runner.config.swaggerNode;

  this.metadata = function metadata() {

    return this.runner.swaggerTools.swaggerMetadata();
  };

  this.security = function security() {

    return this.runner.swaggerTools.swaggerSecurity(this.config.securityHandlers);
  };

  this.validator = function validator() {

    var validatorConfig = {
      validateResponse: !!this.config.validateResponse
    };
    return this.runner.swaggerTools.swaggerValidator(validatorConfig);
  };

  this.router = function router() {

    debug('config: %j', this.config);
    var controllers = this.config.mockMode ? this.config.mockControllersDirs : this.config.controllersDirs;

    var routerConfig = {
      useStubs: !!this.config.mockMode,
      controllers: controllers
    };
    debug('swaggerTools router config: %j', routerConfig);
    return this.runner.swaggerTools.swaggerRouter(routerConfig);
  };

  this.jsonErrorHandler = function() {

    return function jsonErrorHandler(err, req, res, next) {
      if (err && typeof err === 'object') {
        Object.defineProperty(err, 'message', {enumerable: true}); // include message property in response
        res.end(JSON.stringify(err));
      }
      next(err);
    };
  };

  this.expressCompatibilityMW = function expressCompatibilityMW() {
    return expressCompatibility;
  };

  this.swaggerDocs = function swaggerDocs() {

    var config = {
      apiDocs: '/api-docs'
    };
    return this.runner.swaggerTools.swaggerUi(config);
  };

  // enables request.swaggerNode.config
  this.helpers = function helpers() {

    var swaggerNode = {
      config: this.runner.config
    };
    return function(req, res, next) {
      req.swaggerNode = swaggerNode;
      next();
    };
  };

  this.chain = function chain(options) {

    options = options || {};

    var middlewares = [
      this.metadata(),
      this.security(),
      this.validator(),
      this.helpers(),
      this.expressCompatibilityMW(),
      this.router(),
      this.swaggerDocs()
    ];

    if (options.mapErrorsToJson) {
      middlewares.push(this.jsonErrorHandler());
    }

    return createChain(middlewares);
  };
}

function createChain(middlewares) {

  if (!middlewares || middlewares.length < 1) {
    return function(req, res, next) { next(); };
  }

  return function(req, res, next) {
    function createNext(middleware, index) {
      return function(err) {
        if (err) { return next(err); }

        var nextIndex = index + 1;
        var nextMiddleware = middlewares[nextIndex] ? createNext(middlewares[nextIndex], nextIndex) : next;
        middleware(req, res, nextMiddleware);
      };
    }
    return createNext(middlewares[0], 0)();
  };
}

function expressCompatibility(req, res, next) {

  if (!req.query) {
    req.query = Url.parse(req.url, true).query;
  }

  res.json = function(obj) {
    if (!res.getHeader('Content-Type')) {
      res.setHeader('Content-Type', 'application/json');
    }
    res.end(JSON.stringify(obj));
  };

  if (!req.get) req.get = function(name) {
    return this.headers[name];
  };

  if (!res.set) { res.set = res.setHeader; }
  if (!res.get) { res.get = res.getHeader; }
  if (!res.status) {
    res.status = function(status) {
      res.statusCode = status;
    };
  }

  next();
}
<commit_msg>name anonymous functions<commit_after>/****************************************************************************
 The MIT License (MIT)

 Copyright (c) 2015 Apigee Corporation

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 ****************************************************************************/
'use strict';

var debug = require('debug')('swagger');
var Url = require('url');

module.exports = init;

function init(runner) {
  return new Middleware(runner);
}

function Middleware(runner) {
  this.runner = runner;
  this.config = runner.config.swaggerNode;

  this.metadata = function metadata() {

    return this.runner.swaggerTools.swaggerMetadata();
  };

  this.security = function security() {

    return this.runner.swaggerTools.swaggerSecurity(this.config.securityHandlers);
  };

  this.validator = function validator() {

    var validatorConfig = {
      validateResponse: !!this.config.validateResponse
    };
    return this.runner.swaggerTools.swaggerValidator(validatorConfig);
  };

  this.router = function router() {

    debug('config: %j', this.config);
    var controllers = this.config.mockMode ? this.config.mockControllersDirs : this.config.controllersDirs;

    var routerConfig = {
      useStubs: !!this.config.mockMode,
      controllers: controllers
    };
    debug('swaggerTools router config: %j', routerConfig);
    return this.runner.swaggerTools.swaggerRouter(routerConfig);
  };

  this.jsonErrorHandler = function() {

    return function jsonErrorHandler(err, req, res, next) {
      if (err && typeof err === 'object') {
        Object.defineProperty(err, 'message', {enumerable: true}); // include message property in response
        res.end(JSON.stringify(err));
      }
      next(err);
    };
  };

  this.expressCompatibilityMW = function expressCompatibilityMW() {
    return expressCompatibility;
  };

  this.swaggerDocs = function swaggerDocs() {

    var config = {
      apiDocs: '/api-docs'
    };
    return this.runner.swaggerTools.swaggerUi(config);
  };

  // enables request.swaggerNode.config
  this.helpers = function helpers() {

    var swaggerNode = {
      config: this.runner.config
    };
    return function(req, res, next) {
      req.swaggerNode = swaggerNode;
      next();
    };
  };

  this.chain = function chain(options) {

    options = options || {};

    var middlewares = [
      this.metadata(),
      this.security(),
      this.validator(),
      this.helpers(),
      this.expressCompatibilityMW(),
      this.router(),
      this.swaggerDocs()
    ];

    if (options.mapErrorsToJson) {
      middlewares.push(this.jsonErrorHandler());
    }

    return createChain(middlewares);
  };
}

function createChain(middlewares) {

  if (!middlewares || middlewares.length < 1) {
    return function noOp(req, res, next) { next(); };
  }

  return function middlewareChain(req, res, next) {
    function createNext(middleware, index) {
      return function runNext(err) {
        if (err) { return next(err); }

        var nextIndex = index + 1;
        var nextMiddleware = middlewares[nextIndex] ? createNext(middlewares[nextIndex], nextIndex) : next;
        middleware(req, res, nextMiddleware);
      };
    }
    return createNext(middlewares[0], 0)();
  };
}

function expressCompatibility(req, res, next) {

  if (!req.query) {
    req.query = Url.parse(req.url, true).query;
  }

  res.json = function(obj) {
    if (!res.getHeader('Content-Type')) {
      res.setHeader('Content-Type', 'application/json');
    }
    res.end(JSON.stringify(obj));
  };

  if (!req.get) req.get = function(name) {
    return this.headers[name];
  };

  if (!res.set) { res.set = res.setHeader; }
  if (!res.get) { res.get = res.getHeader; }
  if (!res.status) {
    res.status = function(status) {
      res.statusCode = status;
    };
  }

  next();
}
<|endoftext|>


<commit_before>//
// Copyright (c) 2014 Masayuki Nagamachi <masayuki.nagamachi@gmail.com>
// Distributed under MIT license
//

(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    define(['underscore'], factory);
  } else if (typeof exports === 'object') {
    module.exports = factory(require('underscore'), require('backbone'));
  } else {
    factory(root._, root.Backbone);
  }
}(this, function (_, Backbone) {
  'use strict';

  function Validator(validation) {
    if (_.isEmpty(validation)) {
      throw new Error('validation must be an empty object');
    }
    return function (attributes, options) {
      var errors = {};
      for (var attr in validation) {
        var validators = validation[attr];
        for (var i = 0; i < validators.length; i++) {
          var validator = validators[i];
          var err = validator(attributes[attr], attr, this);
          if (err) {
            errors[attr] = err;
            break;
          }
        }
        if (_.has(errors, attr)) {
          this.trigger('invalid:' + attr, this, err);
        } else {
          this.trigger('valid:' + attr, this);
        }
      }
      if (_.isEmpty(errors)) {
        return;
      }
      return errors;
    };
  }

  Validator.valid = function(options) {
    return function(val, attr) {};
  };

  Validator.invalid = function(options) {
    options = _.defaults({}, options, {
      error: 'invalid'
    });
    return function(val, attr) { return options.error; };
  };

  Validator.required = function(options) {
    options = _.defaults({}, options, {
      error: 'required'
    });
    return function(val, attr) {
      if (_.isUndefined(val) || _.isNull(val) || _.isNaN(val)) {
        return options.error;
      }
      if (_.isObject(val) && _.isEmpty(val)) {
        return options.error;
      }
      if (_.isString(val) && _.isEmpty(val)) {
        return options.error;
      }
      return;
    };
  };

  Validator.json = function (options) {
    options = _.defaults({}, options, {
      error: 'invalid json'
    });
    return function (val, attr) {
      if (!_.isString(val)) {
        return options.error;
      }
      try {
        JSON.parse(val);
        return;
      } catch (err) {
        return options.error;
      }
    };
  };

  Backbone.Model.Validator = Validator;
  return Validator;
}));
<commit_msg>Fix a missing dependency<commit_after>//
// Copyright (c) 2014 Masayuki Nagamachi <masayuki.nagamachi@gmail.com>
// Distributed under MIT license
//

(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    define(['underscore', 'backbone'], factory);
  } else if (typeof exports === 'object') {
    module.exports = factory(require('underscore'), require('backbone'));
  } else {
    factory(root._, root.Backbone);
  }
}(this, function (_, Backbone) {
  'use strict';

  function Validator(validation) {
    if (_.isEmpty(validation)) {
      throw new Error('validation must be an empty object');
    }
    return function (attributes, options) {
      var errors = {};
      for (var attr in validation) {
        var validators = validation[attr];
        for (var i = 0; i < validators.length; i++) {
          var validator = validators[i];
          var err = validator(attributes[attr], attr, this);
          if (err) {
            errors[attr] = err;
            break;
          }
        }
        if (_.has(errors, attr)) {
          this.trigger('invalid:' + attr, this, err);
        } else {
          this.trigger('valid:' + attr, this);
        }
      }
      if (_.isEmpty(errors)) {
        return;
      }
      return errors;
    };
  }

  Validator.valid = function(options) {
    return function(val, attr) {};
  };

  Validator.invalid = function(options) {
    options = _.defaults({}, options, {
      error: 'invalid'
    });
    return function(val, attr) { return options.error; };
  };

  Validator.required = function(options) {
    options = _.defaults({}, options, {
      error: 'required'
    });
    return function(val, attr) {
      if (_.isUndefined(val) || _.isNull(val) || _.isNaN(val)) {
        return options.error;
      }
      if (_.isObject(val) && _.isEmpty(val)) {
        return options.error;
      }
      if (_.isString(val) && _.isEmpty(val)) {
        return options.error;
      }
      return;
    };
  };

  Validator.json = function (options) {
    options = _.defaults({}, options, {
      error: 'invalid json'
    });
    return function (val, attr) {
      if (!_.isString(val)) {
        return options.error;
      }
      try {
        JSON.parse(val);
        return;
      } catch (err) {
        return options.error;
      }
    };
  };

  Backbone.Model.Validator = Validator;
  return Validator;
}));
<|endoftext|>


<commit_before>/*
Copyright (c) 2013 Gabriel Corona <gabriel.corona@enst-bretagne.fr>

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*/

"use strict";

// ***** Polyfill/compatibility

if(!window.console) {
  (function() {
    var noop = function() {}
    window.console = noop;
  })();
 }

if(!navigator.getUserMedia) {
  navigator.getUserMedia =
    navigator.webkitGetUserMedia ||
    navigator.mozGetUserMedia ||
    navigator.msGetUserMedia;
 }

// ***** Helpers

var Helpers = {};
Helpers.dropzone = function(element, callback) {
  element.ondragenter = element.ondragover = function (e) {
    e.preventDefault();
    e.dataTransfer.dropEffect = 'copy';
    return false;
  }
  element.ondrop = function(e) {
    e.preventDefault();
    callback(e.dataTransfer);
  }
};

Helpers.parseUriList = function(list) {
  var lines = list.split(/[\r\n]/);

  return lines.
  map(function(x){ return x.trim(); }).
  filter(function(x) { return x.length!=0 && !x[0]!="#"; })
};

Helpers.fitImage = function(img) {
  var wc = img.parentElement.clientWidth;
  var hc = img.parentElement.clientHeight;

  var w = img.naturalWidth;
  var h = img.naturalHeight;

  if(h*wc/w > hc) {
    img.height = hc;
    img.width  = w*hc/h;
  } else {
    img.height = h*wc/w;
    img.width  = wc;
  }
};

// conditions is string or regexp
Helpers.getItem = function(items,condition) {
  if(!items)
    return null;

  for (var i = 0 ; i < items.length ; i++) {
    var item = items[i];
    if(condition.test ? condition.test(item.type) : condition==item.type) {
      return item;
    }
  }
};

Helpers.swap = function(xs,i,j) {
  var temp = xs[i];
  xs[i] = xs[j];
  xs[j] = temp;
};

// ***** Media

var media = {};

function Media() {

}
Media.prototype=media;

Media.fromFile = function(file) {
  var media = new Media();
  var url = (window.URL || window.webkitURL).createObjectURL(file);
  media.url = url;
  media.type = "temporary";
  return media;
};
Media.fromUri = function(uri) {
  var media = new Media;
  media.url = uri;
  return media;
};

// *****

function Cell(media) {
  this.media = media;
  this.found = 0;
}
Cell.prototype = {};

// *****

function MemoryControler($scope, $location) {

  // ***** Feature detection

  $scope.userMediaEnabled = (navigator.getUserMedia!=null);

  // ***** Watch location

 $scope.$location = $location;
 $scope.hash = $location.hash();

 $scope.$watch('location.path()',function() {
     $scope.hash = $location.hash();
   });

 // ***** List of media

 $scope.media = [];

 $scope.clear = function() {
   $scope.media = [];
 };

 $scope.removeMedia = function(m) {
   var i = $scope.media.indexOf(m);
   if(i>=0) {
     $scope.media.splice(i, 1);
   }
 };

 $scope.addUri = function(uri) {
   $scope.media.push(Media.fromUri(uri));
 };

 $scope.addUriList = function(uriList) {
   var uris = Helpers.parseUriList(uriList);
   var n = uris.length;
   for(var i=0; i!=n; ++i) {
     $scope.addUri(uris[i]);
   }
 };

 $scope.addFile = function(file) {
   if(file.type=="text/uri-list" || (file.type=="" && /\.uris?$/.test(file.name))) {
     var reader = new FileReader();
     reader.readAsText(file);
     var handleText = function(event) {
       $scope.$apply(function() {
	   var uris = Helpers.parseUriList(event.target.result);
	   uris.forEach(function(uri) {
	       $scope.addUri(uri);
	     });
	 });
     };
     reader.onload = handleText;
   } else {
     $scope.media.push(Media.fromFile(file));
   }
 };

 $scope.addFiles = function(files) {
   var n = files.length;
   console.log("Add files: "+files.length);
   for(var i=0; i!=n; ++i) {
     $scope.addFile(files[i]);
   }
 };

 // Parse a HTML snippet:
 $scope.parseHtml = function(html) {
   // Poor man's HTMl parsing:
   var f = document.createElement("div");
   f.innerHTML = html;

   var imgs = f.querySelectorAll("img");
   var n = imgs.length;
   for(var j=0; j!=n; ++j) {
     $scope.addUri(imgs[j].src);
   }
 };

 $scope.addDataTransfer = function(dataTransfer) {

   // ***** Debugging

   if(dataTransfer.files) {
     var n = dataTransfer.files.length;
     console.log("#files:"+n);
     for (var i = 0 ; i < n ; i++) {
       console.log("item #"+i+": "+dataTransfer.files[i].type);
     }
   }
   if(dataTransfer.items) {
     var n = dataTransfer.items.length;
     console.log("#items:"+n);
     for (var i = 0 ; i < n ; i++) {
       console.log("item #"+i+": "+dataTransfer.items[i].type);
     }
   }

   // ***** Process

   if(dataTransfer.files && dataTransfer.files.length!=0) {
     console.log("Adding files");
     $scope.addFiles(dataTransfer.files);
     return;
   }

   if(dataTransfer.items && dataTransfer.items.length>0) {

     var imageItem = Helpers.getItem(dataTransfer.items, /^image\//);
     if(imageItem && imageItem.getAsFile) {
       console.log("Adding item image/*");
       var file = imageItem.getAsFile();
       $scope.addFile(file);
       return;
     }

     var htmlItem = Helpers.getItem(dataTransfer.items, "text/html");
     if(htmlItem && htmlItem.getAsFile) {
       console.log("Adding item text/html");
       htmlItem.getAsString(function(html) {
	   $scope.$apply(function() {
	       $scope.parseHtml(html);
	     });
	 });
       return;
     }

     var uriListItem = Helpers.getItem(dataTransfer.items,"text/uri-list");
     if(uriListItem && uriListItem.getAsString) {
       console.log("Adding item text/uri-list");
       item.getAsString(function(uriList) {
	   $scope.$apply(function() {
	       $scope.addUriList(uriList);
	     });
	 });
       return;
     }

   }

   // Why only one? Is it possible to get more?
   var uri = dataTransfer.getData("text/uri-list");
   if(uri) {
     console.log("Adding uri");
     $scope.addUri(uri);
   }
 };

 // ***** Actions

 $scope.url = "";

 // TODO, change this name (too close to addUri)
 $scope.addUrl = function() {
   if($scope.url && $scope.url!="") {
     $scope.media.push(Media.fromUri($scope.url));
   }
   $scope.url = "";
 };

 // ***** Video

 $scope.stream = null;

 $scope.canShoot = function() {
   if($scope.stream) {
     return true;
   } else {
     return false;
   }
 };

 $scope.videoIcon = function() {
   if($scope.stream) {
     return "icon-stop";
   } else {
     return "icon-play";
   }
 };

 $scope.videoLabel = function() {
   if($scope.stream) {
     return "Stop";
   } else {
     return "Start";
   }
 };

 $scope.toggleStream = function() {
   if($scope.stream) {
     $scope.stream.stop();
     $scope.stream = null;
     console.log("video off");
     return;
   }
   var video = document.querySelector("video");

   console.log("video on");
   navigator.getUserMedia({"video": true, "audio": false},
			  function(stream) {
			    $scope.$apply(function() {
				$scope.stream = stream;
				video.src = (window.URL || window.webkitURL).createObjectURL(stream);
			      });
			  },
			  function(e) {
			    // TODO, handle error
			    alert(e);
			  }
			  );
 };

 $scope.takePicture = function() {
   var canvas = document.querySelector("canvas");
   var video  = document.querySelector("video");
   canvas.width = video.videoWidth;
   canvas.height = video.videoHeight;
   canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
   if(canvas.toBlob) {
     var handler = function(blob) {
       $scope.media.push(Media.fromFile(blob));
     };
     canvas.toBlob(handler,"image/png");
   } else {
     var url = canvas.toDataURL('image/png');
     $scope.addUri(url);
   }
 };

 // ***** Launch play

  $scope.sizes = [4,5,6,7,8];

  $scope.rows = 4;
  $scope.columns = 4;

  $scope.neededTiles = function() {
    return Math.floor($scope.rows*$scope.columns/2);
  };

  $scope.playReady = function() {
    return $scope.media.length >= $scope.neededTiles();
  };

  $scope.playStyle = function() {
    if($scope.playReady) {
      return "text-success";
    } else {
      return "text-error;"
    }
  };

  // ***** Play model

  $scope.playing = false;

  // How many cells still to find?
  $scope.remaining = 0;

  //
  $scope.selectedCell = null;

  $scope.grid = [];

  $scope.play = function() {
    $scope.selectedCell = null;
    $scope.remaining = $scope.neededTiles();

    // 1) Choose n elements

    var selection = [];

    for(var i=0; i!=$scope.media.length; ++i) {
      selection.push($scope.media[i]);
    }

    if($scope.remaining<$scope.media.length) {
      var preselection = selection;
      selection = [];
      for(var i=0; i!=$scope.remaining; ++i) {
	var index = Math.floor(Math.random() * preselection.length);
	selection.push(preselection[index]);
	selection.push(preselection[index]);
	Helpers.swap(preselection, index, preselection.length-1);
	preselection.pop();
      }
    }

    // 2) Setup grid

    $scope.grid = [];
    for(var i=0; i!=$scope.rows; ++i) {
      var row = [];
      for(var j=0; j!=$scope.columns; ++j) {
	if(selection.length>0) {
	  var index = Math.floor(Math.random() * selection.length);
	  row.push(new Cell(selection[index]));
	  Helpers.swap(selection, index, selection.length-1);
	  selection.pop();
	}
      }
      $scope.grid.push(row);
    }

    $scope.playing = true;
  };

}

// **** All of this should by angular-ified

/* input file */
window.addEventListener("load", function() {
    var fileInput = document.querySelector("input[type='file']");
    fileInput.addEventListener("change", function() {
	var $scope = angular.element(fileInput).scope();
	$scope.$apply(function() {
	    $scope.addFiles(fileInput.files);
	    fileInput.value = null;
	  });
      });
  });

/* Drag & Drop */
window.addEventListener("load", function() {

    var element = document.body;
      Helpers.dropzone(element, function(dataTransfer) {
	  var $scope = angular.element(element).scope();
	  $scope.$apply(function() {
	      $scope.addDataTransfer(dataTransfer);
	    });
	});

  });

/* Paste */
window.addEventListener("load", function() {
    document.querySelector(".MemoryBuilder").
      addEventListener("paste", function(e) {
	  if(e.target.tagName=="INPUT" || e.target.tagName=="TEXTAREA")
	    return;

	  if(!e.clipboardData)
	    return;

	  e.preventDefault();

	  var filedrop = document.querySelector(".filedrop");
	  var $scope = angular.element(filedrop).scope();

	  $scope.$apply(function() {
	    $scope.addDataTransfer(e.clipboardData);
	    });

	});
  });

<commit_msg>Max 80 columns<commit_after>/*
Copyright (c) 2013 Gabriel Corona <gabriel.corona@enst-bretagne.fr>

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*/

"use strict";

// ***** Polyfill/compatibility

if(!window.console) {
  (function() {
    var noop = function() {}
    window.console = noop;
  })();
 }

if(!navigator.getUserMedia) {
  navigator.getUserMedia =
    navigator.webkitGetUserMedia ||
    navigator.mozGetUserMedia ||
    navigator.msGetUserMedia;
 }

// ***** Helpers

var Helpers = {};
Helpers.dropzone = function(element, callback) {
  element.ondragenter = element.ondragover = function (e) {
    e.preventDefault();
    e.dataTransfer.dropEffect = 'copy';
    return false;
  }
  element.ondrop = function(e) {
    e.preventDefault();
    callback(e.dataTransfer);
  }
};

Helpers.parseUriList = function(list) {
  var lines = list.split(/[\r\n]/);

  return lines.
  map(function(x){ return x.trim(); }).
  filter(function(x) { return x.length!=0 && !x[0]!="#"; })
};

Helpers.fitImage = function(img) {
  var wc = img.parentElement.clientWidth;
  var hc = img.parentElement.clientHeight;

  var w = img.naturalWidth;
  var h = img.naturalHeight;

  if(h*wc/w > hc) {
    img.height = hc;
    img.width  = w*hc/h;
  } else {
    img.height = h*wc/w;
    img.width  = wc;
  }
};

// conditions is string or regexp
Helpers.getItem = function(items,condition) {
  if(!items)
    return null;

  for (var i = 0 ; i < items.length ; i++) {
    var item = items[i];
    if(condition.test ? condition.test(item.type)
       : condition==item.type) {
      return item;
    }
  }
};

Helpers.swap = function(xs,i,j) {
  var temp = xs[i];
  xs[i] = xs[j];
  xs[j] = temp;
};

// ***** Media

var media = {};

function Media() {

}
Media.prototype=media;

Media.fromFile = function(file) {
  var media = new Media();
  var url = (window.URL || window.webkitURL).createObjectURL(file);
  media.url = url;
  media.type = "temporary";
  return media;
};
Media.fromUri = function(uri) {
  var media = new Media;
  media.url = uri;
  return media;
};

// *****

function Cell(media) {
  this.media = media;
  this.found = 0;
}
Cell.prototype = {};

// *****

function MemoryControler($scope, $location) {

  // ***** Feature detection

  $scope.userMediaEnabled = (navigator.getUserMedia!=null);

  // ***** Watch location

 $scope.$location = $location;
 $scope.hash = $location.hash();

 $scope.$watch('location.path()',function() {
     $scope.hash = $location.hash();
   });

 // ***** List of media

 $scope.media = [];

 $scope.clear = function() {
   $scope.media = [];
 };

 $scope.removeMedia = function(m) {
   var i = $scope.media.indexOf(m);
   if(i>=0) {
     $scope.media.splice(i, 1);
   }
 };

 $scope.addUri = function(uri) {
   $scope.media.push(Media.fromUri(uri));
 };

 $scope.addUriList = function(uriList) {
   var uris = Helpers.parseUriList(uriList);
   var n = uris.length;
   for(var i=0; i!=n; ++i) {
     $scope.addUri(uris[i]);
   }
 };

 $scope.addFile = function(file) {
   if(file.type=="text/uri-list" ||
      (file.type=="" && /\.uris?$/.test(file.name))) {
     var reader = new FileReader();
     reader.readAsText(file);
     var handleText = function(event) {
       $scope.$apply(function() {
	   var uris = Helpers.parseUriList(event.target.result);
	   uris.forEach(function(uri) {
	       $scope.addUri(uri);
	     });
	 });
     };
     reader.onload = handleText;
   } else {
     $scope.media.push(Media.fromFile(file));
   }
 };

 $scope.addFiles = function(files) {
   var n = files.length;
   console.log("Add files: "+files.length);
   for(var i=0; i!=n; ++i) {
     $scope.addFile(files[i]);
   }
 };

 // Parse a HTML snippet:
 $scope.parseHtml = function(html) {
   // Poor man's HTMl parsing:
   var f = document.createElement("div");
   f.innerHTML = html;

   var imgs = f.querySelectorAll("img");
   var n = imgs.length;
   for(var j=0; j!=n; ++j) {
     $scope.addUri(imgs[j].src);
   }
 };

 $scope.addDataTransfer = function(dataTransfer) {

   // ***** Debugging

   if(dataTransfer.files) {
     var n = dataTransfer.files.length;
     console.log("#files:"+n);
     for (var i = 0 ; i < n ; i++) {
       console.log("item #"+i+": "+dataTransfer.files[i].type);
     }
   }
   if(dataTransfer.items) {
     var n = dataTransfer.items.length;
     console.log("#items:"+n);
     for (var i = 0 ; i < n ; i++) {
       console.log("item #"+i+": "+dataTransfer.items[i].type);
     }
   }

   // ***** Process

   if(dataTransfer.files && dataTransfer.files.length!=0) {
     console.log("Adding files");
     $scope.addFiles(dataTransfer.files);
     return;
   }

   if(dataTransfer.items && dataTransfer.items.length>0) {

     var imageItem = Helpers.getItem(dataTransfer.items, /^image\//);
     if(imageItem && imageItem.getAsFile) {
       console.log("Adding item image/*");
       var file = imageItem.getAsFile();
       $scope.addFile(file);
       return;
     }

     var htmlItem = Helpers.getItem(dataTransfer.items, "text/html");
     if(htmlItem && htmlItem.getAsFile) {
       console.log("Adding item text/html");
       htmlItem.getAsString(function(html) {
	   $scope.$apply(function() {
	       $scope.parseHtml(html);
	     });
	 });
       return;
     }

     var uriListItem = Helpers.getItem(dataTransfer.items,"text/uri-list");
     if(uriListItem && uriListItem.getAsString) {
       console.log("Adding item text/uri-list");
       item.getAsString(function(uriList) {
	   $scope.$apply(function() {
	       $scope.addUriList(uriList);
	     });
	 });
       return;
     }

   }

   // Why only one? Is it possible to get more?
   var uri = dataTransfer.getData("text/uri-list");
   if(uri) {
     console.log("Adding uri");
     $scope.addUri(uri);
   }
 };

 // ***** Actions

 $scope.url = "";

 // TODO, change this name (too close to addUri)
 $scope.addUrl = function() {
   if($scope.url && $scope.url!="") {
     $scope.media.push(Media.fromUri($scope.url));
   }
   $scope.url = "";
 };

 // ***** Video

 $scope.stream = null;

 $scope.canShoot = function() {
   if($scope.stream) {
     return true;
   } else {
     return false;
   }
 };

 $scope.videoIcon = function() {
   if($scope.stream) {
     return "icon-stop";
   } else {
     return "icon-play";
   }
 };

 $scope.videoLabel = function() {
   if($scope.stream) {
     return "Stop";
   } else {
     return "Start";
   }
 };

 $scope.toggleStream = function() {
   if($scope.stream) {
     $scope.stream.stop();
     $scope.stream = null;
     console.log("video off");
     return;
   }
   var video = document.querySelector("video");

   console.log("video on");

   var callback = function(stream) {
     $scope.$apply(function() {
	 $scope.stream = stream;
	 video.src = (window.URL || window.webkitURL).createObjectURL(stream);
       });
   };

   var errback = function(e) {
     alert(e);
   }

   navigator.getUserMedia({"video": true, "audio": false},
			  callback, errback);
 };

 $scope.takePicture = function() {
   var canvas = document.querySelector("canvas");
   var video  = document.querySelector("video");
   canvas.width = video.videoWidth;
   canvas.height = video.videoHeight;
   canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
   if(canvas.toBlob) {
     var handler = function(blob) {
       $scope.media.push(Media.fromFile(blob));
     };
     canvas.toBlob(handler,"image/png");
   } else {
     var url = canvas.toDataURL('image/png');
     $scope.addUri(url);
   }
 };

 // ***** Launch play

  $scope.sizes = [4,5,6,7,8];

  $scope.rows = 4;
  $scope.columns = 4;

  $scope.neededTiles = function() {
    return Math.floor($scope.rows*$scope.columns/2);
  };

  $scope.playReady = function() {
    return $scope.media.length >= $scope.neededTiles();
  };

  $scope.playStyle = function() {
    if($scope.playReady) {
      return "text-success";
    } else {
      return "text-error;"
    }
  };

  // ***** Play model

  $scope.playing = false;

  // How many cells still to find?
  $scope.remaining = 0;

  //
  $scope.selectedCell = null;

  $scope.grid = [];

  $scope.play = function() {
    $scope.selectedCell = null;
    $scope.remaining = $scope.neededTiles();

    // 1) Choose n elements

    var selection = [];

    for(var i=0; i!=$scope.media.length; ++i) {
      selection.push($scope.media[i]);
    }

    if($scope.remaining<$scope.media.length) {
      var preselection = selection;
      selection = [];
      for(var i=0; i!=$scope.remaining; ++i) {
	var index = Math.floor(Math.random() * preselection.length);
	selection.push(preselection[index]);
	selection.push(preselection[index]);
	Helpers.swap(preselection, index, preselection.length-1);
	preselection.pop();
      }
    }

    // 2) Setup grid

    $scope.grid = [];
    for(var i=0; i!=$scope.rows; ++i) {
      var row = [];
      for(var j=0; j!=$scope.columns; ++j) {
	if(selection.length>0) {
	  var index = Math.floor(Math.random() * selection.length);
	  row.push(new Cell(selection[index]));
	  Helpers.swap(selection, index, selection.length-1);
	  selection.pop();
	}
      }
      $scope.grid.push(row);
    }

    $scope.playing = true;
  };

}

// **** All of this should by angular-ified

/* input file */
window.addEventListener("load", function() {
    var fileInput = document.querySelector("input[type='file']");
    fileInput.addEventListener("change", function() {
	var $scope = angular.element(fileInput).scope();
	$scope.$apply(function() {
	    $scope.addFiles(fileInput.files);
	    fileInput.value = null;
	  });
      });
  });

/* Drag & Drop */
window.addEventListener("load", function() {

    var element = document.body;
      Helpers.dropzone(element, function(dataTransfer) {
	  var $scope = angular.element(element).scope();
	  $scope.$apply(function() {
	      $scope.addDataTransfer(dataTransfer);
	    });
	});

  });

/* Paste */
window.addEventListener("load", function() {
    document.querySelector(".MemoryBuilder").
      addEventListener("paste", function(e) {
	  if(e.target.tagName=="INPUT" || e.target.tagName=="TEXTAREA")
	    return;

	  if(!e.clipboardData)
	    return;

	  e.preventDefault();

	  var filedrop = document.querySelector(".filedrop");
	  var $scope = angular.element(filedrop).scope();

	  $scope.$apply(function() {
	    $scope.addDataTransfer(e.clipboardData);
	    });

	});
  });

<|endoftext|>


<commit_before>var EXPORTED_SYMBOLS = ['parse'];

Components.utils.import('resource://greasemonkey/extractMeta.js');
Components.utils.import('resource://greasemonkey/script.js');
Components.utils.import('resource://greasemonkey/scriptIcon.js');
Components.utils.import('resource://greasemonkey/scriptRequire.js');
Components.utils.import('resource://greasemonkey/scriptResource.js');
Components.utils.import('resource://greasemonkey/third-party/MatchPattern.js');
Components.utils.import('resource://greasemonkey/util.js');

var gIoService = Components.classes["@mozilla.org/network/io-service;1"]
    .getService(Components.interfaces.nsIIOService);
var gStringBundle = Components
    .classes["@mozilla.org/intl/stringbundle;1"]
    .getService(Components.interfaces.nsIStringBundleService)
    .createBundle("chrome://greasemonkey/locale/greasemonkey.properties");

/** Parse the source of a script; produce Script object. */
function parse(aSource, aUri, aFailWhenMissing, aNoMetaOk) {
  var meta = extractMeta(aSource).match(/.+/g);
  if (aFailWhenMissing && !meta && !aNoMetaOk) return null;

  var script = new Script();

  if (aUri) script.downloadURL = aUri.spec;
  if (aUri && aUri.spec) {
    var name = aUri.spec;
    name = name.substring(0, name.indexOf(".user.js"));
    name = name.substring(name.lastIndexOf("/") + 1);
    script._name = name;
  }
  if (aUri) script._namespace = aUri.host;

  if (!meta && aNoMetaOk) {
    setDefaults(script);
    return script;
  }

  var resourceNames = {};
  for (var i = 0, metaLine = ''; metaLine = meta[i]; i++) {
    try {
      var data = GM_util.parseMetaLine(metaLine.replace(/\s+$/, ''));
    } catch (e) {
      // Ignore invalid/unsupported meta lines.
      continue;
    }

    switch (data.keyword) {
    case 'noframes':
      script._noframes = true;
      break;

    case 'description':
    case 'name':
      var locale = data.locale;

      if (locale) {
        if (!script._locales[locale]) {
          script._locales[locale] = {};
        }
        script._locales[locale][data.keyword] = data.value;
      }
      else {
        if ((data.keyword == 'description')
            && (script['_' + data.keyword] == ''))
            script['_' + data.keyword] = data.value;
        if ((data.keyword == 'name')
            && ((script['_' + data.keyword] == 'user-script')
            || (script['_' + data.keyword] == name)))
            script['_' + data.keyword] = data.value;
      }

      break;

    case 'resource':
      var name = data.value1;
      var url = data.value2;

      if (name in resourceNames) {
        script.parseErrors.push(
            gStringBundle.GetStringFromName('parse.resource-duplicate')
                .replace('%1', resName));
        break;
      }
      resourceNames[name] = true;

      try {
        var resUri = GM_util.uriFromUrl(url, aUri);
        var scriptResource = new ScriptResource(script);
        scriptResource._name = name;
        scriptResource._downloadURL = resUri.spec;
        script._resources.push(scriptResource);
        script._rawMeta += data.keyword + '\0'
            + name + '\0'
            + resUri.spec + '\0';
      } catch (e) {
        script.parseErrors.push(
            gStringBundle.GetStringFromName('parse.resource-failed')
                .replace('%1', name).replace('%2', url)
            );
      }

      break;

    case 'namespace':
    case 'version':
      script['_' + data.keyword] = data.value;
      break;
    case 'exclude':
      script._excludes.push(data.value);
      break;
    case 'grant':
      script._grants.push(data.value);
      break;
    case 'include':
      script._includes.push(data.value);
      break;
    case 'run-at':
      script._runAt = data.value;
      break;

    case 'installURL':
      data.keyword = 'downloadURL';
    case 'downloadURL':
    case 'updateURL':
      try {
        var uri = GM_util.uriFromUrl(data.value, aUri);
        script[data.keyword] = uri.spec;
      } catch (e) {
        dump('Failed to parse ' + data.keyword
            + ' "' + data.value + '":\n' + e + '\n');
      }
      break;

    case 'icon':
      try {
        script.icon.setMetaVal(data.value);
        script._rawMeta += data.keyword + '\0' + data.value + '\0';
      } catch (e) {
        script.parseErrors.push(e.message);
      }
      break;

    case 'match':
      try {
        var match = new MatchPattern(data.value);
        script._matches.push(match);
      } catch (e) {
        script.parseErrors.push(
            gStringBundle.GetStringFromName('parse.ignoring-match')
                .replace('%1', data.value).replace('%2', e)
            );
      }
      break;

    case 'require':
      try {
        var reqUri = GM_util.uriFromUrl(data.value, aUri);
        var scriptRequire = new ScriptRequire(script);
        scriptRequire._downloadURL = reqUri.spec;
        script._requires.push(scriptRequire);
        script._rawMeta += data.keyword + '\0' + data.value + '\0';
      } catch (e) {
        dump('require err:'+e+'\n');
        script.parseErrors.push(
            gStringBundle.GetStringFromName('parse.require-failed')
                .replace('%1', data.value)
            );
      }
      break;
    }
  }

  setDefaults(script);
  return script;
}

function setDefaults(script) {
  if (!script.updateURL && script.downloadURL) {
    script.updateURL = script.downloadURL;
  }
  if ('document-start' != script._runAt && 'document-end' != script._runAt) {
    script._runAt = 'document-end';
  }
  if (script._includes.length == 0 && script._matches.length == 0) {
    script._includes.push('*');
  }
}
<commit_msg>Always initialize name.<commit_after>var EXPORTED_SYMBOLS = ['parse'];

Components.utils.import('resource://greasemonkey/extractMeta.js');
Components.utils.import('resource://greasemonkey/script.js');
Components.utils.import('resource://greasemonkey/scriptIcon.js');
Components.utils.import('resource://greasemonkey/scriptRequire.js');
Components.utils.import('resource://greasemonkey/scriptResource.js');
Components.utils.import('resource://greasemonkey/third-party/MatchPattern.js');
Components.utils.import('resource://greasemonkey/util.js');

var gIoService = Components.classes["@mozilla.org/network/io-service;1"]
    .getService(Components.interfaces.nsIIOService);
var gStringBundle = Components
    .classes["@mozilla.org/intl/stringbundle;1"]
    .getService(Components.interfaces.nsIStringBundleService)
    .createBundle("chrome://greasemonkey/locale/greasemonkey.properties");

/** Parse the source of a script; produce Script object. */
function parse(aSource, aUri, aFailWhenMissing, aNoMetaOk) {
  var meta = extractMeta(aSource).match(/.+/g);
  if (aFailWhenMissing && !meta && !aNoMetaOk) return null;

  var script = new Script();

  var name = null;
  if (aUri) script.downloadURL = aUri.spec;
  if (aUri && aUri.spec) {
    name = aUri.spec;
    name = name.substring(0, name.indexOf(".user.js"));
    name = name.substring(name.lastIndexOf("/") + 1);
    script._name = name;
  }
  if (aUri) script._namespace = aUri.host;

  if (!meta && aNoMetaOk) {
    setDefaults(script);
    return script;
  }

  var resourceNames = {};
  for (var i = 0, metaLine = ''; metaLine = meta[i]; i++) {
    try {
      var data = GM_util.parseMetaLine(metaLine.replace(/\s+$/, ''));
    } catch (e) {
      // Ignore invalid/unsupported meta lines.
      continue;
    }

    switch (data.keyword) {
    case 'noframes':
      script._noframes = true;
      break;

    case 'description':
    case 'name':
      var locale = data.locale;

      if (locale) {
        if (!script._locales[locale]) {
          script._locales[locale] = {};
        }
        script._locales[locale][data.keyword] = data.value;
      }
      else {
        if ((data.keyword == 'description')
            && (script['_' + data.keyword] == ''))
            script['_' + data.keyword] = data.value;
        if ((data.keyword == 'name')
            && ((script['_' + data.keyword] == 'user-script')
            || (script['_' + data.keyword] == name)))
            script['_' + data.keyword] = data.value;
      }

      break;

    case 'resource':
      var name = data.value1;
      var url = data.value2;

      if (name in resourceNames) {
        script.parseErrors.push(
            gStringBundle.GetStringFromName('parse.resource-duplicate')
                .replace('%1', resName));
        break;
      }
      resourceNames[name] = true;

      try {
        var resUri = GM_util.uriFromUrl(url, aUri);
        var scriptResource = new ScriptResource(script);
        scriptResource._name = name;
        scriptResource._downloadURL = resUri.spec;
        script._resources.push(scriptResource);
        script._rawMeta += data.keyword + '\0'
            + name + '\0'
            + resUri.spec + '\0';
      } catch (e) {
        script.parseErrors.push(
            gStringBundle.GetStringFromName('parse.resource-failed')
                .replace('%1', name).replace('%2', url)
            );
      }

      break;

    case 'namespace':
    case 'version':
      script['_' + data.keyword] = data.value;
      break;
    case 'exclude':
      script._excludes.push(data.value);
      break;
    case 'grant':
      script._grants.push(data.value);
      break;
    case 'include':
      script._includes.push(data.value);
      break;
    case 'run-at':
      script._runAt = data.value;
      break;

    case 'installURL':
      data.keyword = 'downloadURL';
    case 'downloadURL':
    case 'updateURL':
      try {
        var uri = GM_util.uriFromUrl(data.value, aUri);
        script[data.keyword] = uri.spec;
      } catch (e) {
        dump('Failed to parse ' + data.keyword
            + ' "' + data.value + '":\n' + e + '\n');
      }
      break;

    case 'icon':
      try {
        script.icon.setMetaVal(data.value);
        script._rawMeta += data.keyword + '\0' + data.value + '\0';
      } catch (e) {
        script.parseErrors.push(e.message);
      }
      break;

    case 'match':
      try {
        var match = new MatchPattern(data.value);
        script._matches.push(match);
      } catch (e) {
        script.parseErrors.push(
            gStringBundle.GetStringFromName('parse.ignoring-match')
                .replace('%1', data.value).replace('%2', e)
            );
      }
      break;

    case 'require':
      try {
        var reqUri = GM_util.uriFromUrl(data.value, aUri);
        var scriptRequire = new ScriptRequire(script);
        scriptRequire._downloadURL = reqUri.spec;
        script._requires.push(scriptRequire);
        script._rawMeta += data.keyword + '\0' + data.value + '\0';
      } catch (e) {
        dump('require err:'+e+'\n');
        script.parseErrors.push(
            gStringBundle.GetStringFromName('parse.require-failed')
                .replace('%1', data.value)
            );
      }
      break;
    }
  }

  setDefaults(script);
  return script;
}

function setDefaults(script) {
  if (!script.updateURL && script.downloadURL) {
    script.updateURL = script.downloadURL;
  }
  if ('document-start' != script._runAt && 'document-end' != script._runAt) {
    script._runAt = 'document-end';
  }
  if (script._includes.length == 0 && script._matches.length == 0) {
    script._includes.push('*');
  }
}
<|endoftext|>


<commit_before>import { picker } from 'picker'
import { userFacingText } from 'user-facing-text'

export default () ->
  describe 'picker', ->
    it 'should have user facing text defined', ->
      # XXX: this feels not quite right - we are doing side affecting stuff just
      # by requiring a file...
      userFacingText('picker', 'chooseValue').should.equal('Choose a value...')
<commit_msg>Port the picker tests<commit_after>
import { picker } from 'picker'
import { userFacingText } from 'user-facing-text'

export default () => {
  describe('picker', () => {
    it('should have user facing text defined', () => {
      userFacingText('picker', 'chooseValue').should.equal('Choose a value...')
    })
  })
}
<|endoftext|>