<commit_before>const commander = require("commander");
const process = require("process");
const { spawn } = require("cross-spawn");
const fs = require("fs-extra");
const path = require("path");
const lazyMethodRequire = require("./lib/LazyMethodRequire").default(__dirname);

const newApp = lazyMethodRequire("./commands/new");
const startClient = lazyMethodRequire("./commands/start-client");
const startServer = lazyMethodRequire("./commands/start-server");
const startTest = lazyMethodRequire("./commands/test");
const generate = lazyMethodRequire("./commands/generate");
const destroy = lazyMethodRequire("./commands/destroy");
const dockerize = lazyMethodRequire("./commands/dockerize");

const updateLastVersionUsed = require("./lib/updateVersion");
const getVersion = require("./lib/getVersion");

const logger = require("./lib/cliLogger");
const cliColorScheme = require("./lib/cliColorScheme");
const { highlight } = cliColorScheme;

const utils = require("./lib/utils");
const { quitUnlessGluestickProject } = utils;

const autoUpgrade = require("./autoUpgrade");

const isProduction = process.env.NODE_ENV === "production";

const IS_WINDOWS = process.platform === "win32";

const currentGluestickVersion = getVersion();

const debugServerOption = ["-D, --debug-server", "debug server side rendering with node-inspector"];
const debugTestOption = ["-B, --debug-test", "debug tests with node-inspector"];
const karmaTestOption = ["-k, --karma", "run tests in Karma"];
const mochaReporterOption = ["-r, --reporter [type]", "run tests in Node.js"];
const firefoxOption = ["-F, --firefox", "Use Firefox with test runner"];
const singleRunOption = ["-S, --single", "Run test suite only once"];
const skipBuildOption = ["-P, --skip-build", "skip build when running in production mode"];

commander
  .version(currentGluestickVersion);

commander
  .command("touch")
  .description("update project version")
  .action(checkGluestickProject)
  .action(() => updateLastVersionUsed(currentGluestickVersion));

commander
  .command("new")
  .description("generate a new application")
  .arguments("<app_name>")
  .action((app_name) => {
    if(newApp(app_name)) {
      updateLastVersionUsed(currentGluestickVersion, false);
    }
  });

commander
  .command("generate <container|component|reducer>")
  .description("generate a new container")
  .arguments("<name>")
  .action(checkGluestickProject)
  .action((type, name) => generate(type, name, (err) => {
    if (err) { logger.error(err); }
  }))
  .action(() => updateLastVersionUsed(currentGluestickVersion));

commander
  .command("destroy <container|component|reducer>")
  .description("destroy a generated container")
  .arguments("<name>")
  .action(checkGluestickProject)
  .action(destroy)
  .action(() => updateLastVersionUsed(currentGluestickVersion));

commander
  .command("start")
  .alias("s")
  .description("start everything")
  .option("-T, --skip-tests", "ignore test hook")
  .option("-L, --log-level <level>", "logging level", /^(fatal|error|warn|info|debug|trace|silent)$/, null)
  .option(...debugServerOption)
  .option(...debugTestOption)
  .option(...mochaReporterOption)
  .option(...karmaTestOption)
  .option(...skipBuildOption)
  .action(checkGluestickProject)
  .action(() => notifyUpdates())
  .action(startAll)
  .action(() => updateLastVersionUsed(currentGluestickVersion));

commander
  .command("build")
  .description("create production asset build")
  .action(checkGluestickProject)
  .action(() => startClient(true))
  .action(() => updateLastVersionUsed(currentGluestickVersion));

commander
  .command("dockerize")
  .description("create docker image")
  .arguments("<name>")
  .action(checkGluestickProject)
  .action(upgradeAndDockerize)
  .action(() => updateLastVersionUsed(currentGluestickVersion));

commander
  .command("start-client", null, {noHelp: true})
  .description("start client")
  .action(checkGluestickProject)
  .action(() => startClient(false))
  .action(() => updateLastVersionUsed(currentGluestickVersion));

commander
  .command("start-server", null, {noHelp: true})
  .description("start server")
  .option(...debugServerOption)
  .option(...debugTestOption)
  .option(...mochaReporterOption)
  .action(checkGluestickProject)
  .action((options) => startServer(options.debugServer))
  .action(() => updateLastVersionUsed(currentGluestickVersion));

commander
  .command("start-test", null, {noHelp: true})
  .option(...firefoxOption)
  .option(...singleRunOption)
  .option(...karmaTestOption)
  .option(...debugTestOption)
  .option(...mochaReporterOption)
  .description("start test")
  .action(checkGluestickProject)
  .action(startTest)
  .action(() => updateLastVersionUsed(currentGluestickVersion));

commander
  .command("test")
  .option(...firefoxOption)
  .option(...singleRunOption)
  .option(...karmaTestOption)
  .option(...debugTestOption)
  .option(...mochaReporterOption)
  .description("start tests")
  .action(checkGluestickProject)
  .action(() => updateLastVersionUsed(currentGluestickVersion))
  .action(() => {
    const proc = spawnProcess("test", commander.rawArgs.slice(3));
    proc.on("close", code => { process.exit(code); });
  });

// This is a catch all command. DO NOT PLACE ANY COMMANDS BELOW THIS
commander
  .command("*", null, {noHelp: true})
  .action(function(cmd){
    logger.error(`Command '${highlight(cmd)}' not recognized`);
    commander.help();
  });

commander.parse(process.argv);

function checkGluestickProject () {
  quitUnlessGluestickProject(commander.rawArgs[2]);
}

function notifyUpdates () {
  const indexFilePath = path.join(process.cwd(), "Index.js");
  const data = fs.readFileSync(indexFilePath);
  if (data.indexOf("Helmet.rewind()") === -1) {
    logger.info(`
##########################################################################
Upgrade Notice: Newer versions of Index.js now include react-helmet
for allowing dynamic changes to document header data. You will need to
manually update your Index.js file to receive this change.
For a simple example see:
https://github.com/TrueCar/gluestick/blob/develop/templates/new/Index.js
##########################################################################
    `);
  }
}

function spawnProcess (type, args=[]) {
  let childProcess;
  const postFix = IS_WINDOWS ? ".cmd" : "";
  switch (type) {
    case "client":
      childProcess = spawn("gluestick" + postFix, ["start-client", ...args], {stdio: "inherit", env: Object.assign({}, process.env)});
      break;
    case "server":
      childProcess = spawn("gluestick" + postFix, ["start-server", ...args], {stdio: "inherit", env: Object.assign({}, process.env, {NODE_ENV: isProduction ? "production": "development-server"})});
      break;
    case "test":
      childProcess = spawn("gluestick" + postFix, ["start-test", ...args], {stdio: "inherit", env: Object.assign({}, process.env, {NODE_ENV: isProduction ? "production": "development-test"})});
      break;
    default:
      break;
  }
  childProcess.on("error", function () { logger.error(JSON.stringify(arguments)); });
  return childProcess;
}

/*
 * Start server and (optionally) tests in different processes.
 *
 * @param {object} options        Command-line options object directly from Commander
 */
async function startAll(options) {
  try {
    await autoUpgrade(options.logLevel);
  }
  catch (e) {
    logger.error(`During auto upgrade: ${e}`);
    process.exit();
  }

  // Set parsed command line args so that spawned processes can refer to them
  const parsedOptions = getCommandOptions(options);
  process.env.GS_COMMAND_OPTIONS = JSON.stringify(parsedOptions);

  // in production spawning the client really just creates a build. Our docker
  // images pre-build and therefor they start with the skip build option as
  // true.  We only want to start the client in development mode or if
  // skipBuild is not specified
  if (!(isProduction && options.skipBuild)) {
    spawnProcess("client");
  }

  spawnProcess("server", (options.debugServer ? ["--debug-server"] : []));

  // Start tests unless they asked us not to or we are in production mode
  if (!isProduction && !options.skipTests) {
    spawnProcess("test", commander.rawArgs.slice(3));
  }
}

async function upgradeAndDockerize (name) {
  await autoUpgrade();
  dockerize(name);
}

function getCommandOptions(options) {
  const parsedOptionEntries = Object.entries(options).filter(entry => !(entry[0].match(/^_.+/)));
  const parsedOptions = parsedOptionEntries.reduce((obj, [key, value]) => {
    // remove keys that are internal to commander
    if (["parent", "options", "commands"].includes(key)) {
      return obj;
    }
    return {...obj, [key]: value};
  }, {});
  return parsedOptions;
}
<commit_msg>Check the version before npm updates<commit_after>const commander = require("commander");
const process = require("process");
const { spawn } = require("cross-spawn");
const fs = require("fs-extra");
const path = require("path");
const lazyMethodRequire = require("./lib/LazyMethodRequire").default(__dirname);

const newApp = lazyMethodRequire("./commands/new");
const startClient = lazyMethodRequire("./commands/start-client");
const startServer = lazyMethodRequire("./commands/start-server");
const startTest = lazyMethodRequire("./commands/test");
const generate = lazyMethodRequire("./commands/generate");
const destroy = lazyMethodRequire("./commands/destroy");
const dockerize = lazyMethodRequire("./commands/dockerize");

const updateLastVersionUsed = require("./lib/updateVersion");
const getVersion = require("./lib/getVersion");

const logger = require("./lib/cliLogger");
const cliColorScheme = require("./lib/cliColorScheme");
const { highlight } = cliColorScheme;

const utils = require("./lib/utils");
const { quitUnlessGluestickProject } = utils;

const autoUpgrade = require("./autoUpgrade");

const isProduction = process.env.NODE_ENV === "production";

const IS_WINDOWS = process.platform === "win32";

const currentGluestickVersion = getVersion();

const debugServerOption = ["-D, --debug-server", "debug server side rendering with node-inspector"];
const debugTestOption = ["-B, --debug-test", "debug tests with node-inspector"];
const karmaTestOption = ["-k, --karma", "run tests in Karma"];
const mochaReporterOption = ["-r, --reporter [type]", "run tests in Node.js"];
const firefoxOption = ["-F, --firefox", "Use Firefox with test runner"];
const singleRunOption = ["-S, --single", "Run test suite only once"];
const skipBuildOption = ["-P, --skip-build", "skip build when running in production mode"];

commander
  .version(currentGluestickVersion);

commander
  .command("touch")
  .description("update project version")
  .action(checkGluestickProject)
  .action(() => updateLastVersionUsed(currentGluestickVersion));

commander
  .command("new")
  .description("generate a new application")
  .arguments("<app_name>")
  .action((app_name) => {
    if(newApp(app_name)) {
      updateLastVersionUsed(currentGluestickVersion, false);
    }
  });

commander
  .command("generate <container|component|reducer>")
  .description("generate a new container")
  .arguments("<name>")
  .action(checkGluestickProject)
  .action((type, name) => generate(type, name, (err) => {
    if (err) { logger.error(err); }
  }))
  .action(() => updateLastVersionUsed(currentGluestickVersion));

commander
  .command("destroy <container|component|reducer>")
  .description("destroy a generated container")
  .arguments("<name>")
  .action(checkGluestickProject)
  .action(destroy)
  .action(() => updateLastVersionUsed(currentGluestickVersion));

commander
  .command("start")
  .alias("s")
  .description("start everything")
  .option("-T, --skip-tests", "ignore test hook")
  .option("-L, --log-level <level>", "logging level", /^(fatal|error|warn|info|debug|trace|silent)$/, null)
  .option(...debugServerOption)
  .option(...debugTestOption)
  .option(...mochaReporterOption)
  .option(...karmaTestOption)
  .option(...skipBuildOption)
  .action(checkGluestickProject)
  .action(() => updateLastVersionUsed(currentGluestickVersion))
  .action(() => notifyUpdates())
  .action(startAll);

commander
  .command("build")
  .description("create production asset build")
  .action(checkGluestickProject)
  .action(() => startClient(true))
  .action(() => updateLastVersionUsed(currentGluestickVersion));

commander
  .command("dockerize")
  .description("create docker image")
  .arguments("<name>")
  .action(checkGluestickProject)
  .action(() => updateLastVersionUsed(currentGluestickVersion))
  .action(upgradeAndDockerize);

commander
  .command("start-client", null, {noHelp: true})
  .description("start client")
  .action(checkGluestickProject)
  .action(() => startClient(false))
  .action(() => updateLastVersionUsed(currentGluestickVersion));

commander
  .command("start-server", null, {noHelp: true})
  .description("start server")
  .option(...debugServerOption)
  .option(...debugTestOption)
  .option(...mochaReporterOption)
  .action(checkGluestickProject)
  .action((options) => startServer(options.debugServer))
  .action(() => updateLastVersionUsed(currentGluestickVersion));

commander
  .command("start-test", null, {noHelp: true})
  .option(...firefoxOption)
  .option(...singleRunOption)
  .option(...karmaTestOption)
  .option(...debugTestOption)
  .option(...mochaReporterOption)
  .description("start test")
  .action(checkGluestickProject)
  .action(startTest)
  .action(() => updateLastVersionUsed(currentGluestickVersion));

commander
  .command("test")
  .option(...firefoxOption)
  .option(...singleRunOption)
  .option(...karmaTestOption)
  .option(...debugTestOption)
  .option(...mochaReporterOption)
  .description("start tests")
  .action(checkGluestickProject)
  .action(() => updateLastVersionUsed(currentGluestickVersion))
  .action(() => {
    const proc = spawnProcess("test", commander.rawArgs.slice(3));
    proc.on("close", code => { process.exit(code); });
  });

// This is a catch all command. DO NOT PLACE ANY COMMANDS BELOW THIS
commander
  .command("*", null, {noHelp: true})
  .action(function(cmd){
    logger.error(`Command '${highlight(cmd)}' not recognized`);
    commander.help();
  });

commander.parse(process.argv);

function checkGluestickProject () {
  quitUnlessGluestickProject(commander.rawArgs[2]);
}

function notifyUpdates () {
  const indexFilePath = path.join(process.cwd(), "Index.js");
  const data = fs.readFileSync(indexFilePath);
  if (data.indexOf("Helmet.rewind()") === -1) {
    logger.info(`
##########################################################################
Upgrade Notice: Newer versions of Index.js now include react-helmet
for allowing dynamic changes to document header data. You will need to
manually update your Index.js file to receive this change.
For a simple example see:
https://github.com/TrueCar/gluestick/blob/develop/templates/new/Index.js
##########################################################################
    `);
  }
}

function spawnProcess (type, args=[]) {
  let childProcess;
  const postFix = IS_WINDOWS ? ".cmd" : "";
  switch (type) {
    case "client":
      childProcess = spawn("gluestick" + postFix, ["start-client", ...args], {stdio: "inherit", env: Object.assign({}, process.env)});
      break;
    case "server":
      childProcess = spawn("gluestick" + postFix, ["start-server", ...args], {stdio: "inherit", env: Object.assign({}, process.env, {NODE_ENV: isProduction ? "production": "development-server"})});
      break;
    case "test":
      childProcess = spawn("gluestick" + postFix, ["start-test", ...args], {stdio: "inherit", env: Object.assign({}, process.env, {NODE_ENV: isProduction ? "production": "development-test"})});
      break;
    default:
      break;
  }
  childProcess.on("error", function () { logger.error(JSON.stringify(arguments)); });
  return childProcess;
}

/*
 * Start server and (optionally) tests in different processes.
 *
 * @param {object} options        Command-line options object directly from Commander
 */
async function startAll(options) {
  try {
    await autoUpgrade(options.logLevel);
  }
  catch (e) {
    logger.error(`During auto upgrade: ${e}`);
    process.exit();
  }

  // Set parsed command line args so that spawned processes can refer to them
  const parsedOptions = getCommandOptions(options);
  process.env.GS_COMMAND_OPTIONS = JSON.stringify(parsedOptions);

  // in production spawning the client really just creates a build. Our docker
  // images pre-build and therefor they start with the skip build option as
  // true.  We only want to start the client in development mode or if
  // skipBuild is not specified
  if (!(isProduction && options.skipBuild)) {
    spawnProcess("client");
  }

  spawnProcess("server", (options.debugServer ? ["--debug-server"] : []));

  // Start tests unless they asked us not to or we are in production mode
  if (!isProduction && !options.skipTests) {
    spawnProcess("test", commander.rawArgs.slice(3));
  }
}

async function upgradeAndDockerize (name) {
  await autoUpgrade();
  dockerize(name);
}

function getCommandOptions(options) {
  const parsedOptionEntries = Object.entries(options).filter(entry => !(entry[0].match(/^_.+/)));
  const parsedOptions = parsedOptionEntries.reduce((obj, [key, value]) => {
    // remove keys that are internal to commander
    if (["parent", "options", "commands"].includes(key)) {
      return obj;
    }
    return {...obj, [key]: value};
  }, {});
  return parsedOptions;
}
<|endoftext|>


<commit_before>/**
 * File: ModelAdmin.js
 */

/**
 * Javascript handlers for generic model admin.
 *
 * Most of the work being done here is intercepting clicks on form submits,
 * and managing the loading and sequencing of data between the different panels of
 * the CMS interface.
 *
 * @todo add live query to manage application of events to DOM refreshes
 * @todo alias the $ function instead of literal jQuery
 */
(function($) {
	$.entwine('ss', function($){

		//////////////////////////////////////////////////////////////////
		// Search form
		//////////////////////////////////////////////////////////////////

		/**
		 * Class: #ModelClassSelector select
		 *
		 * If a dropdown is used to choose between the classes, it is handled by this code
		 */
		$('#ModelClassSelector select').entwine({
			onmatch: function() {
				// Initialise the form by calling this onchange event straight away
				this.change();

				this._super();
			},

			/**
			 * Function: onchange
			 *
			 * Set up an onchange function to show the applicable form and hide all others
			 */
			onchange: function(e) {
				this.find('option').each(function() {
					var $form = $('#'+this.val());
					if(this.val() == this.val()) $form.show();
					else $form.hide();
				});
			}
		});

		/**
		 * Class: #SearchForm_holder form
		 *
		 * Submits a search filter query and attaches event handlers
		 * to the response table, excluding the import form because
		 * file ($_FILES) submission doesn't work using AJAX
		 *
		 * Note: This is used for Form_CreateForm and all Form_SearchForm_* variations
		 */
		$('#SearchForm_holder form').entwine({

			/**
			 * Function: onsubmit
			 */
			onsubmit: function(e) {
				// Import forms are processed without ajax
				if(this.is('#Form_ImportForm')) return true;

				this.trigger('beforeSubmit');

				var btn = $(this[0].clickedButton);
				btn.addClass('loading');

				$('#Form_EditForm').loadForm(
					this.attr('action'),
					function() {
						btn.removeClass('loading');
					},
					{data: this.serialize()}
				);

				return false;
			}
		});

		/**
		 * Class: a.form_frontend_function.toggle_result_assembly
		 *
		 * Column selection in search form
		  */
		$('a.form_frontend_function.toggle_result_assembly').entwine({

			/**
			 * Function: onclick
			 */
			onclick: function(e) {
				var toggleElement = $(this).next();
				toggleElement.toggle();
				return false;
			}
		});

		/**
		 * Class: a.form_frontend_function.tick_all_result_assembly
		 */
		$('a.form_frontend_function.tick_all_result_assembly').entwine({
			/**
			 * Function: onclick
			 */
			onclick: function(e) {
				var resultAssembly = $(this).prevAll('div#ResultAssembly').find('ul li input');
				resultAssembly.attr('checked', 'checked');
				return false;
			}
		});

		$('a.form_frontend_function.untick_all_result_assembly').entwine({
			onclick: function(e) {
				var resultAssembly = $(this).prevAll('div#ResultAssembly').find('ul li input');
				resultAssembly.removeAttr('checked');
				return false;
			}
		});

		/**
		 * Class: .resultsTable tbody td
		 *
		 * Table record handler for search result record
		 */
		$('.resultsTable tbody td').entwine({
			/**
			 * Function: onclick
			 */
			onclick: function(e) {
				var firstLink = this.find('a[href]');
				if(!firstLink) return;
				$('#Form_EditForm').loadForm(firstLink.attr('href'));
				return false;
			}
		});

		/**
		 * Class: #Form_ManagedModelsSelect
		 *
		 * Add object button
		 */
		$('#Form_ManagedModelsSelect').entwine({
			/**
			 * Function: onsubmit
			 */
			onsubmit: function(e) {
				className = $('select option:selected', this).val();
				requestPath = this.attr('action').replace('ManagedModelsSelect', className + '/add');
				var $button = $(':submit', this);
				$('#Form_EditForm').loadForm(
					requestPath,
					function() {
						$button.removeClass('loading');
						$button = null;
					}
				);

				return false;
			}
		});

		/**
		 * Class: #Form_EditForm input[name=action_doDelete]
		 *
		 * RHS panel Delete button
		 */
		$('#Form_EditForm input[name=action_doDelete]').entwine({
			// Function: onclick
			onclick: function(e) {
				if(!confirm(ss.i18n._t('ModelAdmin.REALLYDELETE', 'Really delete?'))) {
					this.removeClass('loading');
					return false;
				}
			}
		});

		/**
		 * Class: .importSpec
		 *
		 * Toggle import specifications
		 */
		$('.importSpec').entwine({
			onmatch: function() {
				this.hide();
				this.find('a.detailsLink').click(function() {
					$('#' + $(this).attr('href').replace(/.*#/,'')).toggle();
					return false;
				});

				this._super();
			}
		});

	});
})(jQuery);<commit_msg>BUGFIX: Closing TinyMCE image, link or flash panel when loading form (ticket #4907) (from r98690)<commit_after>/**
 * File: ModelAdmin.js
 */

/**
 * Javascript handlers for generic model admin.
 *
 * Most of the work being done here is intercepting clicks on form submits,
 * and managing the loading and sequencing of data between the different panels of
 * the CMS interface.
 *
 * @todo add live query to manage application of events to DOM refreshes
 * @todo alias the $ function instead of literal jQuery
 */
(function($) {
	$.entwine('ss', function($){

		//////////////////////////////////////////////////////////////////
		// Search form
		//////////////////////////////////////////////////////////////////

		/**
		 * Class: #ModelClassSelector select
		 *
		 * If a dropdown is used to choose between the classes, it is handled by this code
		 */
		$('#ModelClassSelector select').entwine({
			onmatch: function() {
				// Initialise the form by calling this onchange event straight away
				this.change();

				this._super();
			},

			/**
			 * Function: onchange
			 *
			 * Set up an onchange function to show the applicable form and hide all others
			 */
			onchange: function(e) {
				this.find('option').each(function() {
					var $form = $('#'+this.val());
					if(this.val() == this.val()) $form.show();
					else $form.hide();
				});
			}
		});

		/**
		 * Class: #SearchForm_holder form
		 *
		 * Submits a search filter query and attaches event handlers
		 * to the response table, excluding the import form because
		 * file ($_FILES) submission doesn't work using AJAX
		 *
		 * Note: This is used for Form_CreateForm and all Form_SearchForm_* variations
		 */
		$('#SearchForm_holder form').entwine({

			/**
			 * Function: onsubmit
			 */
			onsubmit: function(e) {
				// Import forms are processed without ajax
				if(this.is('#Form_ImportForm')) return true;

				$('#contentPanel').closeRightPanel();

				this.trigger('beforeSubmit');

				var btn = $(this[0].clickedButton);
				btn.addClass('loading');

				$('#Form_EditForm').loadForm(
					this.attr('action'),
					function() {
						btn.removeClass('loading');
					},
					{data: this.serialize()}
				);

				return false;
			}
		});

		/**
		 * Class: a.form_frontend_function.toggle_result_assembly
		 *
		 * Column selection in search form
		  */
		$('a.form_frontend_function.toggle_result_assembly').entwine({

			/**
			 * Function: onclick
			 */
			onclick: function(e) {
				var toggleElement = $(this).next();
				toggleElement.toggle();
				return false;
			}
		});

		/**
		 * Class: a.form_frontend_function.tick_all_result_assembly
		 */
		$('a.form_frontend_function.tick_all_result_assembly').entwine({
			/**
			 * Function: onclick
			 */
			onclick: function(e) {
				var resultAssembly = $(this).prevAll('div#ResultAssembly').find('ul li input');
				resultAssembly.attr('checked', 'checked');
				return false;
			}
		});

		$('a.form_frontend_function.untick_all_result_assembly').entwine({
			onclick: function(e) {
				var resultAssembly = $(this).prevAll('div#ResultAssembly').find('ul li input');
				resultAssembly.removeAttr('checked');
				return false;
			}
		});

		/**
		 * Class: .resultsTable tbody td
		 *
		 * Table record handler for search result record
		 */
		$('.resultsTable tbody td').entwine({
			/**
			 * Function: onclick
			 */
			onclick: function(e) {
				var firstLink = this.find('a[href]');
				if(!firstLink) return;
				$('#Form_EditForm').loadForm(firstLink.attr('href'));
				return false;
			}
		});

		/**
		 * Class: #Form_ManagedModelsSelect
		 *
		 * Add object button
		 */
		$('#Form_ManagedModelsSelect').entwine({
			/**
			 * Function: onsubmit
			 */
			onsubmit: function(e) {
				className = $('select option:selected', this).val();
				requestPath = this.attr('action').replace('ManagedModelsSelect', className + '/add');
				var $button = $(':submit', this);
				$('#Form_EditForm').loadForm(
					requestPath,
					function() {
						$button.removeClass('loading');
						$button = null;
					}
				);

				return false;
			}
		});

		/**
		 * Class: #Form_EditForm input[name=action_doDelete]
		 *
		 * RHS panel Delete button
		 */
		$('#Form_EditForm input[name=action_doDelete]').entwine({
			// Function: onclick
			onclick: function(e) {
				if(!confirm(ss.i18n._t('ModelAdmin.REALLYDELETE', 'Really delete?'))) {
					this.removeClass('loading');
					return false;
				}
			}
		});

		/**
		 * Class: .importSpec
		 *
		 * Toggle import specifications
		 */
		$('.importSpec').entwine({
			onmatch: function() {
				this.hide();
				this.find('a.detailsLink').click(function() {
					$('#' + $(this).attr('href').replace(/.*#/,'')).toggle();
					return false;
				});

				this._super();
			}
		});

		$('#contentPanel').entwine({
			/**
			* Close TinyMCE image, link or flash panel.
			* this function is called everytime a new search, back or add new DataObject are clicked
			**/
			closeRightPanel: function(){
				if($('#contentPanel').is(':visible')) {
					$('#contentPanel').hide();
					$('#Form_EditorToolbarImageForm').hide();
					$('#Form_EditorToolbarFlashForm').hide();
					$('#Form_EditorToolbarLinkForm').hide();
				}
			}

		});
	});
})(jQuery);<|endoftext|>


<commit_before>/*
 * Provides L.Map with convenient shortcuts for using browser geolocation features.
 */

L.Map.include({
	_defaultLocateOptions: {
		watch: false,
		setView: false,
		maxZoom: Infinity,
		timeout: 10000,
		maximumAge: 0,
		enableHighAccuracy: false
	},

	locate: function (/*Object*/ options) {

		options = this._locationOptions = L.extend(this._defaultLocateOptions, options);

		if (!navigator.geolocation) {
			this._handleGeolocationError({
				code: 0,
				message: "Geolocation not supported."
			});
			return this;
		}

		var onResponse = L.bind(this._handleGeolocationResponse, this),
			onError = L.bind(this._handleGeolocationError, this);

		if (options.watch) {
			this._locationWatchId =
			        navigator.geolocation.watchPosition(onResponse, onError, options);
		} else {
			navigator.geolocation.getCurrentPosition(onResponse, onError, options);
		}
		return this;
	},

	stopLocate: function () {
		if (navigator.geolocation) {
			navigator.geolocation.clearWatch(this._locationWatchId);
		}
		return this;
	},

	_handleGeolocationError: function (error) {
		var c = error.code,
		    message = error.message ||
		            (c === 1 ? "permission denied" :
		            (c === 2 ? "position unavailable" : "timeout"));

		if (this._locationOptions.setView && !this._loaded) {
			this.fitWorld();
		}

		this.fire('locationerror', {
			code: c,
			message: "Geolocation error: " + message + "."
		});
	},

	_handleGeolocationResponse: function (pos) {
		var latAccuracy = 180 * pos.coords.accuracy / 4e7,
		    lngAccuracy = latAccuracy * 2,

		    lat = pos.coords.latitude,
		    lng = pos.coords.longitude,
		    latlng = new L.LatLng(lat, lng),

		    sw = new L.LatLng(lat - latAccuracy, lng - lngAccuracy),
		    ne = new L.LatLng(lat + latAccuracy, lng + lngAccuracy),
		    bounds = new L.LatLngBounds(sw, ne),

		    options = this._locationOptions;

		if (options.setView) {
			var zoom = Math.min(this.getBoundsZoom(bounds), options.maxZoom);
			this.setView(latlng, zoom);
		}

		this.fire('locationfound', {
			latlng: latlng,
			bounds: bounds,
			accuracy: pos.coords.accuracy
		});
	}
});
<commit_msg>fix geolocation accuracy bounds, add more event data, close #984, related #584, #987, #1028<commit_after>/*
 * Provides L.Map with convenient shortcuts for using browser geolocation features.
 */

L.Map.include({
	_defaultLocateOptions: {
		watch: false,
		setView: false,
		maxZoom: Infinity,
		timeout: 10000,
		maximumAge: 0,
		enableHighAccuracy: false
	},

	locate: function (/*Object*/ options) {

		options = this._locationOptions = L.extend(this._defaultLocateOptions, options);

		if (!navigator.geolocation) {
			this._handleGeolocationError({
				code: 0,
				message: "Geolocation not supported."
			});
			return this;
		}

		var onResponse = L.bind(this._handleGeolocationResponse, this),
			onError = L.bind(this._handleGeolocationError, this);

		if (options.watch) {
			this._locationWatchId =
			        navigator.geolocation.watchPosition(onResponse, onError, options);
		} else {
			navigator.geolocation.getCurrentPosition(onResponse, onError, options);
		}
		return this;
	},

	stopLocate: function () {
		if (navigator.geolocation) {
			navigator.geolocation.clearWatch(this._locationWatchId);
		}
		return this;
	},

	_handleGeolocationError: function (error) {
		var c = error.code,
		    message = error.message ||
		            (c === 1 ? "permission denied" :
		            (c === 2 ? "position unavailable" : "timeout"));

		if (this._locationOptions.setView && !this._loaded) {
			this.fitWorld();
		}

		this.fire('locationerror', {
			code: c,
			message: "Geolocation error: " + message + "."
		});
	},

	_handleGeolocationResponse: function (pos) {
		var lat = pos.coords.latitude,
		    lng = pos.coords.longitude,
		    latlng = new L.LatLng(lat, lng),

		    latAccuracy = 180 * pos.coords.accuracy / 40075017,
		    lngAccuracy = latAccuracy / Math.cos(L.LatLng.DEG_TO_RAD * lat),

		    sw = new L.LatLng(lat - latAccuracy, lng - lngAccuracy),
		    ne = new L.LatLng(lat + latAccuracy, lng + lngAccuracy),
		    bounds = new L.LatLngBounds(sw, ne),

		    options = this._locationOptions;

		if (options.setView) {
			var zoom = Math.min(this.getBoundsZoom(bounds), options.maxZoom);
			this.setView(latlng, zoom);
		}

		var event = L.extend({
			latlng: latlng,
			bounds: bounds
		}, pos.coords);

		this.fire('locationfound', event);
	}
});
<|endoftext|>


<commit_before>/**
 * Javascript handlers for generic model admin.
 *
 * Most of the work being done here is intercepting clicks on form submits,
 * and managing the loading and sequencing of data between the different panels of
 * the CMS interface.
 *
 * @todo add live query to manage application of events to DOM refreshes
 * @todo alias the $ function instead of literal jQuery
 */
(function($) {
$(document).ready(function() {
	/**
	 * Generic ajax error handler
	 */
	$('form').livequery('ajaxError', function (XMLHttpRequest, textStatus, errorThrown) {
			$('input', this).removeClass('loading');
			statusMessage(ss.i18n._t('ModelAdmin.ERROR', 'Error'), 'bad');
	});

	/**
	 * Add class ajaxActions class to the parent of Add button of AddForm
	 * so it float to the right
	 */
	$('#Form_AddForm_action_doCreate').livequery(function(){
		$(this).parent().addClass('ajaxActions');
	});

    /*
     * Highlight buttons on click
     */
	$('input[type=submit]').livequery('click', function() {
	    $(this).addClass('loading');
	});

	$("#right").scroll( function () {
		positionActionArea();
	});

	$(window).resize( function() {
		positionActionArea();
	});

	/*
	 * Make the status message and ajax action button fixed
	 */
	function positionActionArea() {
		if ( $.browser.msie && $.browser.version.indexOf("6.", 0)==0 ) {
			newTopValue = $("#right").scrollTop()+$(window).height()-139;
			$('.ajaxActions').css('top', newTopValue);
			$('#statusMessage').css('top', newTopValue);
		}
	}

	//////////////////////////////////////////////////////////////////
	// Search form
	//////////////////////////////////////////////////////////////////

	/**
	 * If a dropdown is used to choose between the classes, it is handled by this code
	 */
    $('#ModelClassSelector select')
        // Set up an onchange function to show the applicable form and hide all others
        .change(function() {
            var $selector = $(this);
            $('option', this).each(function() {
                var $form = $('#'+$(this).val());
                if($selector.val() == $(this).val()) $form.show();
                else $form.hide();
            });
        })
        // Initialise the form by calling this onchange event straight away
        .change();

	/**
	 * Stores a jQuery reference to the last submitted search form.
	 */
	__lastSearch = null;

	/**
	 * Submits a search filter query and attaches event handlers
	 * to the response table, excluding the import form because
	 * file ($_FILES) submission doesn't work using AJAX
	 *
	 * Note: This is used for Form_CreateForm too
	 *
	 * @todo use livequery to manage ResultTable click handlers
	 */
	$('#SearchForm_holder .tab form:not(#Form_ImportForm)').submit(function () {
	    var $form = $(this);

		// @todo TinyMCE coupling
		tinymce_removeAll();

		$('#ModelAdminPanel').fn('startHistory', $(this).attr('action'), $(this).formToArray());
	    $('#ModelAdminPanel').load($(this).attr('action'), $(this).formToArray(), standardStatusHandler(function(result) {
			if(!this.future || !this.future.length) {
			    $('#Form_EditForm_action_goForward, #Form_ResultsForm_action_goForward').hide();
		    }
			if(!this.history || this.history.length <= 1) {
			    $('#Form_EditForm_action_goBack, #Form_ResultsForm_action_goBack').hide();
		    }

    		$('#form_actions_right').remove();
    		Behaviour.apply();

			if(window.onresize) window.onresize();
    		// Remove the loading indicators from the buttons
    		$('input[type=submit]', $form).removeClass('loading');
	    },
	    // Failure handler - we should still remove loading indicator
	    function () {
    		$('input[type=submit]', $form).removeClass('loading');
	    }));
	    return false;
	});

	/**
	 * Clear search button
	 */
	$('#SearchForm_holder button[name=action_clearsearch]').click(function(e) {
		$(this.form).resetForm();
		return false;
	});

	/**
	 * Column selection in search form
	  */
	$('a.form_frontend_function.toggle_result_assembly').click(function(){
		var toggleElement = $(this).next();
		toggleElement.toggle();
		return false;
	});

	$('a.form_frontend_function.tick_all_result_assembly').click(function(){
		var resultAssembly = $(this).prevAll('div#ResultAssembly').find('ul li input');
		resultAssembly.attr('checked', 'checked');
		return false;
	});

	$('a.form_frontend_function.untick_all_result_assembly').click(function(){
		var resultAssembly = $(this).prevAll('div#ResultAssembly').find('ul li input');
		resultAssembly.removeAttr('checked');
		return false;
	});

	//////////////////////////////////////////////////////////////////
	// Results list
	//////////////////////////////////////////////////////////////////

	/**
	 * Table record handler for search result record
	 * @todo: Shouldn't this be part of TableListField?
	 */
	$('#right #Form_ResultsForm tbody td a:not(.deletelink,.downloadlink)')
	    .livequery('click', function(){
	        $(this).parent().parent().addClass('loading');
    		var el = $(this);
    		$('#ModelAdminPanel').fn('addHistory', el.attr('href'));
    		$('#ModelAdminPanel').fn('loadForm', el.attr('href'));
    		return false;
    	});
    	/* this isn't being used currently; the real hover code is part of TableListField
    	.hover(
    	    function(){
        		$(this).addClass('over').siblings().addClass('over')
        	},
        	function(){
        		$(this).removeClass('over').siblings().removeClass('over')
        	}
        );
        */

	//////////////////////////////////////////////////////////////////
	// RHS detail form
	//////////////////////////////////////////////////////////////////

    /**
     * RHS panel Back button
     */
	$('#Form_EditForm_action_goBack, #Form_ResultsForm_action_goBack').livequery('click', function() {
	    $('#ModelAdminPanel').fn('goBack');
		return false;
	});

    /**
     * RHS panel Back button
     */
	$('#Form_ResultsForm_action_goForward').livequery('click', function() {
	    $('#ModelAdminPanel').fn('goForward');
		return false;
	});

	/**
	 * RHS panel Save button
	 */
	$('#right input[name=action_doSave],#right input[name=action_doCreate]').livequery('click', function(){
		var form = $('#right form');
		var formAction = form.attr('action') + '?' + $(this).fieldSerialize();

		// @todo TinyMCE coupling
		if(typeof tinyMCE != 'undefined') tinyMCE.triggerSave();

		// Post the data to save
		$.post(formAction, form.formToArray(), function(result){
			// @todo TinyMCE coupling
			tinymce_removeAll();

			$('#right #ModelAdminPanel').html(result);

			if($('#right #ModelAdminPanel form').hasClass('validationerror')) {
				statusMessage(ss.i18n._t('ModelAdmin.VALIDATIONERROR', 'Validation Error'), 'bad');
			} else {
				statusMessage(ss.i18n._t('ModelAdmin.SAVED', 'Saved'), 'good');
			}

			// TODO/SAM: It seems a bit of a hack to have to list all the little updaters here.
			// Is livequery a solution?
			Behaviour.apply(); // refreshes ComplexTableField
			if(window.onresize) window.onresize();
		}, 'html');

		return false;
	});

	/**
	 * RHS panel Delete button
	 */
	$('#right input[name=action_doDelete]').livequery('click', function(){
		var confirmed = confirm(ss.i18n._t('ModelAdmin.REALLYDELETE', 'Really delete?'));
		if(!confirmed) {
			$(this).removeClass('loading')
			return false;
		}

		var form = $('#right form');
		var formAction = form.attr('action') + '?' + $(this).fieldSerialize();

        // The POST actually handles the delete
		$.post(formAction, form.formToArray(), function(result){
		    // On success, the panel is refreshed and a status message shown.
			$('#right #ModelAdminPanel').html(result);

			statusMessage(ss.i18n._t('ModelAdmin.DELETED', 'Successfully deleted'));
    		$('#form_actions_right').remove();

            // To do - convert everything to jQuery so that this isn't needed
			Behaviour.apply(); // refreshes ComplexTableField
		});

		return false;
	});

	//////////////////////////////////////////////////////////////////
	// Import/Add form
	//////////////////////////////////////////////////////////////////

	/**
	 * Add object button
	 */
	$('#Form_ManagedModelsSelect').submit(function(){
		className = $('select option:selected', this).val();
		requestPath = $(this).attr('action').replace('ManagedModelsSelect', className + '/add');
		var $button = $(':submit', this);
		$('#ModelAdminPanel').fn(
			'loadForm',
			requestPath,
			function() {
				$button.removeClass('loading');
				$button = null;
			}
		);
		$('#form_actions_right').remove();
		return false;
	});

	/**
	 * Toggle import specifications
	 */
	$('.importSpec .details').hide();
	$('.importSpec a.detailsLink').click(function() {
		$('#' + $(this).attr('href').replace(/.*#/,'')).toggle();
		return false;
	});

	//////////////////////////////////////////////////////////////////
	// Helper functions
	//////////////////////////////////////////////////////////////////

    $('#ModelAdminPanel').fn({
        /**
         * Load a detail editing form into the main edit panel
         * @todo Convert everything to jQuery so that the built-in load method can be used with this instead
         */
        loadForm: function(url, successCallback) {
			// @todo TinyMCE coupling
			tinymce_removeAll();

    	    $('#right #ModelAdminPanel').load(url, standardStatusHandler(function(result) {
				if(typeof(successCallback) == 'function') successCallback.apply();
				if(!this.future || !this.future.length) {
				    $('#Form_EditForm_action_goForward, #Form_ResultsForm_action_goForward').hide();
			    }
				if(!this.history || this.history.length <= 1) {
				    $('#Form_EditForm_action_goBack, #Form_ResultsForm_action_goBack').hide();
			    }

    			Behaviour.apply(); // refreshes ComplexTableField
				if(window.onresize) window.onresize();
    		}));
    	},

    	startHistory: function(url, data) {
    	    this.history = [];
    	    $(this).fn('addHistory', url, data);
    	},

    	/**
    	 * Add an item to the history, to be accessed by goBack and goForward
    	 */
    	addHistory: function(url, data) {
    	    // Combine data into URL
    	    if(data) {
    	        if(url.indexOf('?') == -1) url += '?' + $.param(data);
    	        else url += '&' + $.param(data);
	        }

	        // Add to history
    	    if(this.history == null) this.history = [];
    	    this.history.push(url);

    	    // Reset future
    	    this.future = [];
    	},

    	goBack: function() {
    	    if(this.history && this.history.length) {
        	    if(this.future == null) this.future = [];

        	    var currentPage = this.history.pop();
        	    var previousPage = this.history[this.history.length-1];

        	    this.future.push(currentPage);
        	    $(this).fn('loadForm', previousPage);
    	    }
    	},

    	goForward: function() {
    	    if(this.future && this.future.length) {
        	    if(this.future == null) this.future = [];

        	    var nextPage = this.future.pop();

        	    this.history.push(nextPage);
        	    $(this).fn('loadForm', nextPage);
    	    }
    	}

    });

	/**
	 * Standard SilverStripe status handler for ajax responses
	 * It will generate a status message out of the response, and only call the callback for successful responses
	 *
	 * To use:
	 *    Instead of passing your callback function as:
	 *       function(response) { ... }
	 *
	 *    Pass it as this:
	 *       standardStatusHandler(function(response) { ... })
	 */
	function standardStatusHandler(callback, failureCallback) {
	    return function(response, status, xhr) {
	        // If the response is takne from $.ajax's complete handler, then swap the variables around
	        if(response.status) {
	            xhr = response;
	            response = xhr.responseText;
	        }

	        if(status == 'success') {
	            statusMessage(xhr.statusText, "good");
	            $(this).each(callback, [response, status, xhr]);
			} else {
	            errorMessage(xhr.statusText);
	            if(failureCallback) $(this).each(failureCallback, [response, status, xhr]);
			}
	    }
	}

})
})(jQuery);

/**
 * @todo Terrible HACK, but thats the cms UI...
 */
function fixHeight_left() {
	//fitToParent('LeftPane');
	fitToParent('Search_holder',12);
	fitToParent('ResultTable_holder',12);
}

function prepareAjaxActions(actions, formName, tabName) {
	// @todo HACK Overwrites LeftAndMain.js version of this method to avoid double form actions
	// (by new jQuery and legacy prototype)
	return false;
}<commit_msg>BUGFIX: Closing TinyMCE image, link or flash panel when loading form (ticket #4907)<commit_after>/**
 * Javascript handlers for generic model admin.
 *
 * Most of the work being done here is intercepting clicks on form submits,
 * and managing the loading and sequencing of data between the different panels of
 * the CMS interface.
 *
 * @todo add live query to manage application of events to DOM refreshes
 * @todo alias the $ function instead of literal jQuery
 */
(function($) {
$(document).ready(function() {
	/**
	 * Generic ajax error handler
	 */
	$('form').livequery('ajaxError', function (XMLHttpRequest, textStatus, errorThrown) {
			$('input', this).removeClass('loading');
			statusMessage(ss.i18n._t('ModelAdmin.ERROR', 'Error'), 'bad');
	});

	/**
	 * Add class ajaxActions class to the parent of Add button of AddForm
	 * so it float to the right
	 */
	$('#Form_AddForm_action_doCreate').livequery(function(){
		$(this).parent().addClass('ajaxActions');
	});

    /*
     * Highlight buttons on click
     */
	$('input[type=submit]').livequery('click', function() {
	    $(this).addClass('loading');
	});

	$('input[name=action_search]').livequery('click', function() {
		$('#contentPanel').fn('closeRightPanel');
	});

	$("#right").scroll( function () {
		positionActionArea();
	});

	$(window).resize( function() {
		positionActionArea();
	});

	/*
	 * Make the status message and ajax action button fixed
	 */
	function positionActionArea() {
		if ( $.browser.msie && $.browser.version.indexOf("6.", 0)==0 ) {
			newTopValue = $("#right").scrollTop()+$(window).height()-139;
			$('.ajaxActions').css('top', newTopValue);
			$('#statusMessage').css('top', newTopValue);
		}
	}

	//////////////////////////////////////////////////////////////////
	// Search form
	//////////////////////////////////////////////////////////////////

	/**
	 * If a dropdown is used to choose between the classes, it is handled by this code
	 */
    $('#ModelClassSelector select')
        // Set up an onchange function to show the applicable form and hide all others
        .change(function() {
            var $selector = $(this);
            $('option', this).each(function() {
                var $form = $('#'+$(this).val());
                if($selector.val() == $(this).val()) $form.show();
                else $form.hide();
            });
        })
        // Initialise the form by calling this onchange event straight away
        .change();

	/**
	 * Stores a jQuery reference to the last submitted search form.
	 */
	__lastSearch = null;

	/**
	 * Submits a search filter query and attaches event handlers
	 * to the response table, excluding the import form because
	 * file ($_FILES) submission doesn't work using AJAX
	 *
	 * Note: This is used for Form_CreateForm too
	 *
	 * @todo use livequery to manage ResultTable click handlers
	 */
	$('#SearchForm_holder .tab form:not(#Form_ImportForm)').submit(function () {
	    var $form = $(this);
		$('#contentPanel').fn('closeRightPanel');
		// @todo TinyMCE coupling
		tinymce_removeAll();

		$('#ModelAdminPanel').fn('startHistory', $(this).attr('action'), $(this).formToArray());
	    $('#ModelAdminPanel').load($(this).attr('action'), $(this).formToArray(), standardStatusHandler(function(result) {
			if(!this.future || !this.future.length) {
			    $('#Form_EditForm_action_goForward, #Form_ResultsForm_action_goForward').hide();
		    }
			if(!this.history || this.history.length <= 1) {
			    $('#Form_EditForm_action_goBack, #Form_ResultsForm_action_goBack').hide();
		    }

    		$('#form_actions_right').remove();
    		Behaviour.apply();

			if(window.onresize) window.onresize();
    		// Remove the loading indicators from the buttons
    		$('input[type=submit]', $form).removeClass('loading');
	    },
	    // Failure handler - we should still remove loading indicator
	    function () {
    		$('input[type=submit]', $form).removeClass('loading');
	    }));
	    return false;
	});

	/**
	 * Clear search button
	 */
	$('#SearchForm_holder button[name=action_clearsearch]').click(function(e) {
		$(this.form).resetForm();
		return false;
	});

	/**
	 * Column selection in search form
	  */
	$('a.form_frontend_function.toggle_result_assembly').click(function(){
		var toggleElement = $(this).next();
		toggleElement.toggle();
		return false;
	});

	$('a.form_frontend_function.tick_all_result_assembly').click(function(){
		var resultAssembly = $(this).prevAll('div#ResultAssembly').find('ul li input');
		resultAssembly.attr('checked', 'checked');
		return false;
	});

	$('a.form_frontend_function.untick_all_result_assembly').click(function(){
		var resultAssembly = $(this).prevAll('div#ResultAssembly').find('ul li input');
		resultAssembly.removeAttr('checked');
		return false;
	});

	//////////////////////////////////////////////////////////////////
	// Results list
	//////////////////////////////////////////////////////////////////

	/**
	 * Table record handler for search result record
	 * @todo: Shouldn't this be part of TableListField?
	 */
	$('#right #Form_ResultsForm tbody td a:not(.deletelink,.downloadlink)')
	    .livequery('click', function(){
	        $(this).parent().parent().addClass('loading');
    		var el = $(this);
    		$('#ModelAdminPanel').fn('addHistory', el.attr('href'));
    		$('#ModelAdminPanel').fn('loadForm', el.attr('href'));
    		return false;
    	});
    	/* this isn't being used currently; the real hover code is part of TableListField
    	.hover(
    	    function(){
        		$(this).addClass('over').siblings().addClass('over')
        	},
        	function(){
        		$(this).removeClass('over').siblings().removeClass('over')
        	}
        );
        */

	//////////////////////////////////////////////////////////////////
	// RHS detail form
	//////////////////////////////////////////////////////////////////

    /**
     * RHS panel Back button
     */
	$('#Form_EditForm_action_goBack, #Form_ResultsForm_action_goBack').livequery('click', function() {
	    $('#ModelAdminPanel').fn('goBack');
		return false;
	});

    /**
     * RHS panel Back button
     */
	$('#Form_ResultsForm_action_goForward').livequery('click', function() {
	    $('#ModelAdminPanel').fn('goForward');
		return false;
	});

	/**
	 * RHS panel Save button
	 */
	$('#right input[name=action_doSave],#right input[name=action_doCreate]').livequery('click', function(){
		var form = $('#right form');
		var formAction = form.attr('action') + '?' + $(this).fieldSerialize();

		// @todo TinyMCE coupling
		if(typeof tinyMCE != 'undefined') tinyMCE.triggerSave();

		// Post the data to save
		$.post(formAction, form.formToArray(), function(result){
			// @todo TinyMCE coupling
			tinymce_removeAll();

			$('#right #ModelAdminPanel').html(result);

			if($('#right #ModelAdminPanel form').hasClass('validationerror')) {
				statusMessage(ss.i18n._t('ModelAdmin.VALIDATIONERROR', 'Validation Error'), 'bad');
			} else {
				statusMessage(ss.i18n._t('ModelAdmin.SAVED', 'Saved'), 'good');
			}

			// TODO/SAM: It seems a bit of a hack to have to list all the little updaters here.
			// Is livequery a solution?
			Behaviour.apply(); // refreshes ComplexTableField
			if(window.onresize) window.onresize();
		}, 'html');

		return false;
	});

	/**
	 * RHS panel Delete button
	 */
	$('#right input[name=action_doDelete]').livequery('click', function(){
		var confirmed = confirm(ss.i18n._t('ModelAdmin.REALLYDELETE', 'Really delete?'));
		if(!confirmed) {
			$(this).removeClass('loading')
			return false;
		}

		var form = $('#right form');
		var formAction = form.attr('action') + '?' + $(this).fieldSerialize();

        // The POST actually handles the delete
		$.post(formAction, form.formToArray(), function(result){
		    // On success, the panel is refreshed and a status message shown.
			$('#right #ModelAdminPanel').html(result);

			statusMessage(ss.i18n._t('ModelAdmin.DELETED', 'Successfully deleted'));
    		$('#form_actions_right').remove();

            // To do - convert everything to jQuery so that this isn't needed
			Behaviour.apply(); // refreshes ComplexTableField
		});

		return false;
	});

	//////////////////////////////////////////////////////////////////
	// Import/Add form
	//////////////////////////////////////////////////////////////////

	/**
	 * Add object button
	 */
	$('#Form_ManagedModelsSelect').submit(function(){
		className = $('select option:selected', this).val();
		requestPath = $(this).attr('action').replace('ManagedModelsSelect', className + '/add');
		var $button = $(':submit', this);
		$('#ModelAdminPanel').fn(
			'loadForm',
			requestPath,
			function() {
				$button.removeClass('loading');
				$button = null;
			}
		);
		$('#form_actions_right').remove();
		return false;
	});

	/**
	 * Toggle import specifications
	 */
	$('.importSpec .details').hide();
	$('.importSpec a.detailsLink').click(function() {
		$('#' + $(this).attr('href').replace(/.*#/,'')).toggle();
		return false;
	});

	//////////////////////////////////////////////////////////////////
	// Helper functions
	//////////////////////////////////////////////////////////////////

	$('#contentPanel').fn({
		/**
		* Close TinyMCE image, link or flash panel.
		* this function is called everytime a new search, back or add new DataObject are clicked
		**/
		closeRightPanel: function(){
			if($('#contentPanel').is(':visible')) {
				$('#contentPanel').hide();
				$('#Form_EditorToolbarImageForm').hide();
				$('#Form_EditorToolbarFlashForm').hide();
				$('#Form_EditorToolbarLinkForm').hide();
			}
		}

	})

    $('#ModelAdminPanel').fn({
        /**
         * Load a detail editing form into the main edit panel
         * @todo Convert everything to jQuery so that the built-in load method can be used with this instead
         */
        loadForm: function(url, successCallback) {
			// @todo TinyMCE coupling
			tinymce_removeAll();
			$('#contentPanel').fn('closeRightPanel');
    	    $('#right #ModelAdminPanel').load(url, standardStatusHandler(function(result) {
				if(typeof(successCallback) == 'function') successCallback.apply();
				if(!this.future || !this.future.length) {
				    $('#Form_EditForm_action_goForward, #Form_ResultsForm_action_goForward').hide();
			    }
				if(!this.history || this.history.length <= 1) {
				    $('#Form_EditForm_action_goBack, #Form_ResultsForm_action_goBack').hide();
			    }

    			Behaviour.apply(); // refreshes ComplexTableField
				if(window.onresize) window.onresize();
    		}));
    	},

    	startHistory: function(url, data) {
    	    this.history = [];
    	    $(this).fn('addHistory', url, data);
    	},

    	/**
    	 * Add an item to the history, to be accessed by goBack and goForward
    	 */
    	addHistory: function(url, data) {
    	    // Combine data into URL
    	    if(data) {
    	        if(url.indexOf('?') == -1) url += '?' + $.param(data);
    	        else url += '&' + $.param(data);
	        }

	        // Add to history
    	    if(this.history == null) this.history = [];
    	    this.history.push(url);

    	    // Reset future
    	    this.future = [];
    	},

    	goBack: function() {
    	    if(this.history && this.history.length) {
        	    if(this.future == null) this.future = [];

        	    var currentPage = this.history.pop();
        	    var previousPage = this.history[this.history.length-1];

        	    this.future.push(currentPage);
        	    $(this).fn('loadForm', previousPage);
    	    }
    	},

    	goForward: function() {
    	    if(this.future && this.future.length) {
        	    if(this.future == null) this.future = [];

        	    var nextPage = this.future.pop();

        	    this.history.push(nextPage);
        	    $(this).fn('loadForm', nextPage);
    	    }
    	}

    });

	/**
	 * Standard SilverStripe status handler for ajax responses
	 * It will generate a status message out of the response, and only call the callback for successful responses
	 *
	 * To use:
	 *    Instead of passing your callback function as:
	 *       function(response) { ... }
	 *
	 *    Pass it as this:
	 *       standardStatusHandler(function(response) { ... })
	 */
	function standardStatusHandler(callback, failureCallback) {
	    return function(response, status, xhr) {
	        // If the response is takne from $.ajax's complete handler, then swap the variables around
	        if(response.status) {
	            xhr = response;
	            response = xhr.responseText;
	        }

	        if(status == 'success') {
	            statusMessage(xhr.statusText, "good");
	            $(this).each(callback, [response, status, xhr]);
			} else {
	            errorMessage(xhr.statusText);
	            if(failureCallback) $(this).each(failureCallback, [response, status, xhr]);
			}
	    }
	}

})
})(jQuery);

/**
 * @todo Terrible HACK, but thats the cms UI...
 */
function fixHeight_left() {
	//fitToParent('LeftPane');
	fitToParent('Search_holder',12);
	fitToParent('ResultTable_holder',12);
}

function prepareAjaxActions(actions, formName, tabName) {
	// @todo HACK Overwrites LeftAndMain.js version of this method to avoid double form actions
	// (by new jQuery and legacy prototype)
	return false;
}<|endoftext|>


<commit_before>/*
Router.configure({
   layoutTemplate: 'layout',
   loadingTemplate: 'loading',
   waitOn: function() { return Meteor.subscribe('posts'); }
});
*/
// Router.map(function() {
//   this.route('postsPage', {path: '/'})
//   this.route('about');
//   this.route('profPage', {
// 	  path: '/profile/:_id',
// 	  data: function() { return Meteor.users.findOne(this.params._id); }
// 	});
// });

Router.configure({
  layout: 'layout',
  loadingTemplate: 'loading',
  notFoundTemplate: 'notFound',
  waitOn: function() {
  	return Meteor.subscribe('posts') && Meteor.subscribe('comments');
  }
});

Router.map(function () {
  this.route('femFeed',{
    path:'/feed',
    waitOn: function () {
      return Meteor.subscribe('posts');
    }
  });

  this.route('login',{
    path:'/login',
    // action: function () {
		// Router.go('/sign-in');
    // }
  });

  this.route('signup', {
    path: '/signup',
  });

  this.route('termsOfService', {
	  path: '/tos',
  });

  this.route('split',{
    path:'/split'
  });

  this.route('splash',{
    path:'/',
    action: function () {
      Router.go('femFeed');
    }
  });

  this.route('lgoin',{
    path:'/profile',
    action: function () {
      Router.go('femFeed');
    }
  });
  this.route('home',{
    path:'/home',
    action: function () {
      Router.go('femFeed');
    }
  });

  this.route('composePost', {
    path: '/post/new',
    template: 'composePost'
  });

  this.route('postPage',{
    path:'/post/:_id',
    waitOn: function() {
		    return Meteor.subscribe('comments', this.params._id);
    },
    data: function () {
      return Posts.findOne({_id:this.params._id});
    }
  });

  this.route('likesPost',{
    path:'/post/:_id/likes',
    data: function () {
      return Posts.findOne({_id:this.params._id});
    }
  });

  this.route('profileId',{
    path:'/profile/:_id',
    template: "profile",
    data: function () {
      return Meteor.users.findOne({_id:this.params._id});
    }
  });

  this.route('profile',{
    path:'/profile/:ownedBy',
    data: function () {
      return Meteor.users.findOne({_id:this.params.ownedBy});
    }
  });
  this.route('editProfile',{
    path:'/profile/:_id/edit',
    data: function () {
      return Meteor.users.findOne({_id:this.params._id});
    }
  });
  this.route('browser',{
    action: function () {
      var generalURL = "https://feminologywiki.com/";
      showWebViewBrowser(generalURL);
    }
  });
});

showWebViewBrowserCurrent = function () {
  showWebViewBrowser("https://feminologywiki.com/index.php?title=Current_Events");
}

showWebViewBrowser = function (url) {
    if (!url) {
      url = "https://feminologywiki.com/";
    };
    var ref = window.open(url,"_system");
    // var ref = window.open("http://www.facebook.com","_blank","location=yes");
}
<commit_msg>Linted Router js<commit_after>'use strict';
Router.configure({
	layout: 'layout',
	loadingTemplate: 'loading',
	notFoundTemplate: 'notFound',
	waitOn: function() {
		return Meteor.subscribe('posts') && Meteor.subscribe('comments');
	}
});

Router.map(function () {
	this.route('femFeed',{
		path:'/feed',
		waitOn: function () {
			return Meteor.subscribe('posts');
		}
	});

	this.route('login',{
		path:'/login',
	});

	this.route('signup', {
		path: '/signup',
	});

	this.route('termsOfService', {
		path: '/tos',
	});

	this.route('split',{
		path:'/split'
	});

	this.route('splash',{
		path:'/',
		action: function () {
			Router.go('femFeed');
		}
	});

	this.route('lgoin',{
		path:'/profile',
		action: function () {
			Router.go('femFeed');
		}
	});

	this.route('home',{
		path:'/home',
		action: function () {
			Router.go('femFeed');
		}
	});

	this.route('composePost', {
		path: '/post/new',
		template: 'composePost'
	});

	this.route('postPage',{
		path:'/post/:_id',
		waitOn: function() {
			return Meteor.subscribe('comments', this.params._id);
		},
		data: function () {
			return Posts.findOne({_id:this.params._id});
		}
	});

	this.route('likesPost',{
		path:'/post/:_id/likes',
		data: function () {
			return Posts.findOne({_id:this.params._id});
		}
	});

	this.route('profileId',{
		path:'/profile/:_id',
		template: 'profile',
		data: function () {
			return Meteor.users.findOne({_id:this.params._id});
		}
	});

	this.route('profile',{
		path:'/profile/:ownedBy',
		data: function () {
			return Meteor.users.findOne({_id:this.params.ownedBy});
		}
	});

	this.route('editProfile',{
		path:'/profile/:_id/edit',
		data: function () {
			return Meteor.users.findOne({_id:this.params._id});
		}
	});
});
<|endoftext|>


<commit_before>// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

frappe.search = {
	setup: function() {
		var opts = {
			autoFocus: true,
			minLength: 0,
			source: function(request, response) {
				var txt = strip(request.term);
				frappe.search.options = [];
				if(txt) {
					var lower = strip(txt.toLowerCase());
					$.each(frappe.search.verbs, function(i, action) {
						action(lower);
					});
				}

				// sort options
				frappe.search.options.sort(function(a, b) {
					return (a.match || "").length - (b.match || "").length; });

				frappe.search.add_recent(txt || "");
				frappe.search.add_help();

				// de-duplicate
				var out = [], routes = [];
				frappe.search.options.forEach(function(option) {
					if(option.route) {
						var str_route = (typeof option.route==='string') ?
							 option.route : option.route.join('/');
						if(routes.indexOf(str_route)===-1) {
							out.push(option);
							routes.push(str_route);
						}
					} else {
						out.push(option);
					}
 				});

				response(out);
			},
			open: function(event, ui) {
				frappe.search.autocomplete_open = event.target;
			},
			close: function(event, ui) {
				frappe.search.autocomplete_open = false;
			},
			select: function(event, ui) {
				if(ui.item.route_options) {
					frappe.route_options = ui.item.route_options;
				}

				if(ui.item.onclick) {
					ui.item.onclick(ui.item.match);
				} else {
					var previous_hash = window.location.hash;
					frappe.set_route(ui.item.route);

					// hashchange didn't fire!
					if (window.location.hash == previous_hash) {
						frappe.route();
					}
				}
				$(this).val('');
				return false;
			}
		};

		var open_recent = function() {
			if (!frappe.search.autocomplete_open) {
				$(this).autocomplete("search", "");
			}
		}

		$("#navbar-search")
			.on("focus", open_recent)
			.autocomplete(opts).data('ui-autocomplete')._renderItem =
				frappe.search.render_item;

		$("#modal-search")
			.on("focus", open_recent)
			.autocomplete(opts).data('ui-autocomplete')._renderItem =
				frappe.search.render_item;

		frappe.search.make_page_title_map();
		frappe.search.setup_recent();
	},
	render_item: function(ul, d) {
		var html = "" + __(d.label || d.value) + "";
		if(d.description && d.value!==d.description) {
			html += '
' + __(d.description) + '';
		}
		return $('

')
			.data('item.autocomplete', d)
			.html('
' + html + '
')
			.appendTo(ul);
	},
	add_help: function() {
		frappe.search.options.push({
			label: __("Help on Search"),
			onclick: function() {
				var txt = '
\

'+__("Make a new record")+'	'+
						__("new type of document")+'
\

'+__("List a document type")+'	'+
						__("document type..., e.g. customer")+'
\

'+__("Search in a document type")+'	'+
						__("text in document type")+'
\

'+__("Open a module or tool")+'	'+
						__("module name...")+'
\

'+__("Calculate")+'	'+
						__("e.g. (55 + 434) / 4 or =Math.sin(Math.PI/2)...")+'
\

'
				msgprint(txt, "Search Help");
			}
		});
	},
	add_recent: function(txt) {
		var doctypes = frappe.utils.unique(keys(locals).concat(keys(frappe.search.recent)));
		for(var i in doctypes) {
			var doctype = doctypes[i];
			if(doctype[0]!==":" && !frappe.model.is_table(doctype)
				&& !in_list(frappe.boot.single_types, doctype)
				&& !in_list(["DocType", "DocField", "DocPerm", "Page", "Country",
					"Currency", "Page Role", "Print Format", "Report"], doctype)) {

				var values = frappe.utils.remove_nulls(frappe.utils.unique(
					keys(locals[doctype]).concat(frappe.search.recent[doctype] || [])
				));

				var ret = frappe.search.find(values, txt, function(match) {
					return {
						label: __(doctype) + " " + match.bold(),
						value: __(doctype) + " " + match,
						route: ["Form", doctype, match]
					}
				}, true);
			}
		}
	},
	make_page_title_map: function() {
		frappe.search.pages = {};
		$.each(frappe.boot.page_info, function(name, p) {
			frappe.search.pages[p.title] = p;
			p.name = name;
		});
	},
	setup_recent: function() {
		var recent = JSON.parse(frappe.boot.user.recent || "[]") || [];
		frappe.search.recent = {};
		for (var i=0, l=recent.length; i < l; i++) {
			var d = recent[i];
			if (!(d[0] && d[1])) continue;

			if (!frappe.search.recent[d[0]]) {
				frappe.search.recent[d[0]] = [];
			}
			frappe.search.recent[d[0]].push(d[1]);
		}
	},
	find: function(list, txt, process, prepend) {
		$.each(list, function(i, item) {
			_item = __(item).toLowerCase().replace(/-/g, " ");
			if(txt===_item || _item.indexOf(txt) !== -1) {
				var option = process(item);

				if(option) {
					option.match = item;

					if(prepend) {
						frappe.search.options = [option].concat(frappe.search.options);
					} else {
						frappe.search.options.push(option);
					}
				}
			}
		});
	}
}

frappe.search.verbs = [
	// search in list if current
	function(txt) {
		var route = frappe.get_route();
		if(route[0]==="List" && txt.indexOf(" in") === -1) {
			// search in title field
			var meta = frappe.get_meta(frappe.container.page.doclistview.doctype);
			var search_field = meta.title_field || "name";
			var options = {};
			options[search_field] = ["like", "%" + txt + "%"];
			frappe.search.options.push({
				label: __('Find {0} in {1}', [txt.bold(), route[1].bold()]),
				value: __('Find {0} in {1}', [txt, route[1]]),
				route_options: options,
				onclick: function() {
					cur_list.refresh();
				},
				match: txt
			});
		}
	},

	// new doc
	function(txt) {
		var ret = false;
		if(txt.split(" ")[0]==="new") {
			frappe.search.find(frappe.boot.user.can_create, txt.substr(4), function(match) {
				return {
					label: __("New {0}", [match.bold()]),
					value: __("New {0}", [match]),
					onclick: function() { frappe.new_doc(match, true); }
				}
			});
		}
	},

	// doctype list
	function(txt) {
		if (txt.toLowerCase().indexOf(" list")) {
			// remove list keyword
			txt = txt.replace(/ list/ig, "").trim();
		}

		frappe.search.find(frappe.boot.user.can_read, txt, function(match) {
			if(in_list(frappe.boot.single_types, match)) {
				return {
					label: __("{0}", [__(match).bold()]),
					value: __(match),
					route:["Form", match, match]
				}
			} else if(in_list(frappe.boot.treeviews, match)) {
				return {
					label: __("{0} Tree", [__(match).bold()]),
					value: __(match),
					route:["Tree", match]
				}
			} else {
				return {
					label: __("{0} List", [__(match).bold()]),
					value: __("{0} List", [__(match)]),
					route:["List", match]
				}
			}
		});
	},

	// reports
	function(txt) {
		frappe.search.find(keys(frappe.boot.user.all_reports), txt, function(match) {
			var report = frappe.boot.user.all_reports[match];
			var route = [];
			if(report.report_type == "Report Builder")
				route = ["Report", report.ref_doctype, match];
			else
				route = ["query-report",  match];

			return {
				label: __("Report {0}", [__(match).bold()]),
				value: __("Report {0}", [__(match)]),
				route: route
			}
		});
	},

	// pages
	function(txt) {
		frappe.search.find(keys(frappe.search.pages), txt, function(match) {
			return {
				label: __("Open {0}", [__(match).bold()]),
				value: __("Open {0}", [__(match)]),
				route: [frappe.search.pages[match].route || frappe.search.pages[match].name]
			}
		});
	},

	// modules
	function(txt) {
		frappe.search.find(keys(frappe.modules), txt, function(match) {
			var module = frappe.modules[match];

			if(module._doctype) return;

			ret = {
				label: __("Open {0}", [__(match).bold()]),
				value: __("Open {0}", [__(match)]),
			}
			if(module.link) {
				ret.route = [module.link];
			} else {
				ret.route = ["Module", match];
			}
			return ret;
		});
	},

	// in
	function(txt) {
		if(in_list(txt.split(" "), "in")) {
			parts = txt.split(" in ");
			frappe.search.find(frappe.boot.user.can_read, parts[1], function(match) {
				return {
					label: __('Find {0} in {1}', [__(parts[0]).bold(), __(match).bold()]),
					value: __('Find {0} in {1}', [__(parts[0]), __(match)]),
					route_options: {"name": ["like", "%" + parts[0] + "%"]},
					route: ["List", match]
				}
			});
		}
	},

	// calculator
	function(txt) {
		var first = txt.substr(0,1);
		if(first==parseInt(first) || first==="(" || first==="=") {
			if(first==="=") {
				txt = txt.substr(1);
			}

			try {
				var val = eval(txt);
				var formatted_value = __('{0} = {1}', [txt, (val + '').bold()]);
				frappe.search.options.push({
					label: formatted_value,
					value: __('{0} = {1}', [txt, val]),
					match: val,
					onclick: function(match) {
						msgprint(formatted_value, "Result");
					}
				});
			} catch(e) {
				// pass
			}

		};
	}
];
<commit_msg>[minor] add calendar, gantt views in awesome bar fixes frappe/erpnext#4479<commit_after>// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

frappe.search = {
	setup: function() {
		var opts = {
			autoFocus: true,
			minLength: 0,
			source: function(request, response) {
				var txt = strip(request.term);
				frappe.search.options = [];
				if(txt) {
					var lower = strip(txt.toLowerCase());
					$.each(frappe.search.verbs, function(i, action) {
						action(lower);
					});
				}

				// sort options
				frappe.search.options.sort(function(a, b) {
					return (a.match || "").length - (b.match || "").length; });

				frappe.search.add_recent(txt || "");
				frappe.search.add_help();

				// de-duplicate
				var out = [], routes = [];
				frappe.search.options.forEach(function(option) {
					if(option.route) {
						var str_route = (typeof option.route==='string') ?
							 option.route : option.route.join('/');
						if(routes.indexOf(str_route)===-1) {
							out.push(option);
							routes.push(str_route);
						}
					} else {
						out.push(option);
					}
 				});

				response(out);
			},
			open: function(event, ui) {
				frappe.search.autocomplete_open = event.target;
			},
			close: function(event, ui) {
				frappe.search.autocomplete_open = false;
			},
			select: function(event, ui) {
				if(ui.item.route_options) {
					frappe.route_options = ui.item.route_options;
				}

				if(ui.item.onclick) {
					ui.item.onclick(ui.item.match);
				} else {
					var previous_hash = window.location.hash;
					frappe.set_route(ui.item.route);

					// hashchange didn't fire!
					if (window.location.hash == previous_hash) {
						frappe.route();
					}
				}
				$(this).val('');
				return false;
			}
		};

		var open_recent = function() {
			if (!frappe.search.autocomplete_open) {
				$(this).autocomplete("search", "");
			}
		}

		$("#navbar-search")
			.on("focus", open_recent)
			.autocomplete(opts).data('ui-autocomplete')._renderItem =
				frappe.search.render_item;

		$("#modal-search")
			.on("focus", open_recent)
			.autocomplete(opts).data('ui-autocomplete')._renderItem =
				frappe.search.render_item;

		frappe.search.make_page_title_map();
		frappe.search.setup_recent();
	},
	render_item: function(ul, d) {
		var html = "" + __(d.label || d.value) + "";
		if(d.description && d.value!==d.description) {
			html += '
' + __(d.description) + '';
		}
		return $('

')
			.data('item.autocomplete', d)
			.html('
' + html + '
')
			.appendTo(ul);
	},
	add_help: function() {
		frappe.search.options.push({
			label: __("Help on Search"),
			onclick: function() {
				var txt = '
\

'+__("Make a new record")+'	'+
						__("new type of document")+'
\

'+__("List a document type")+'	'+
						__("document type..., e.g. customer")+'
\

'+__("Search in a document type")+'	'+
						__("text in document type")+'
\

'+__("Open a module or tool")+'	'+
						__("module name...")+'
\

'+__("Calculate")+'	'+
						__("e.g. (55 + 434) / 4 or =Math.sin(Math.PI/2)...")+'
\

'
				msgprint(txt, "Search Help");
			}
		});
	},
	add_recent: function(txt) {
		var doctypes = frappe.utils.unique(keys(locals).concat(keys(frappe.search.recent)));
		for(var i in doctypes) {
			var doctype = doctypes[i];
			if(doctype[0]!==":" && !frappe.model.is_table(doctype)
				&& !in_list(frappe.boot.single_types, doctype)
				&& !in_list(["DocType", "DocField", "DocPerm", "Page", "Country",
					"Currency", "Page Role", "Print Format", "Report"], doctype)) {

				var values = frappe.utils.remove_nulls(frappe.utils.unique(
					keys(locals[doctype]).concat(frappe.search.recent[doctype] || [])
				));

				var ret = frappe.search.find(values, txt, function(match) {
					return {
						label: __(doctype) + " " + match.bold(),
						value: __(doctype) + " " + match,
						route: ["Form", doctype, match]
					}
				}, true);
			}
		}
	},
	make_page_title_map: function() {
		frappe.search.pages = {};
		$.each(frappe.boot.page_info, function(name, p) {
			frappe.search.pages[p.title] = p;
			p.name = name;
		});
	},
	setup_recent: function() {
		var recent = JSON.parse(frappe.boot.user.recent || "[]") || [];
		frappe.search.recent = {};
		for (var i=0, l=recent.length; i < l; i++) {
			var d = recent[i];
			if (!(d[0] && d[1])) continue;

			if (!frappe.search.recent[d[0]]) {
				frappe.search.recent[d[0]] = [];
			}
			frappe.search.recent[d[0]].push(d[1]);
		}
	},
	find: function(list, txt, process, prepend) {
		$.each(list, function(i, item) {
			_item = __(item).toLowerCase().replace(/-/g, " ");
			if(txt===_item || _item.indexOf(txt) !== -1) {
				var option = process(item);

				if(option) {
					if($.isPlainObject(option)) {
						option = [option];
					}

					option.forEach(function(o) { o.match = item; });

					if(prepend) {
						frappe.search.options = option.concat(frappe.search.options);
					} else {
						frappe.search.options = frappe.search.options.concat(option);
					}
				}
			}
		});
	}
}

frappe.search.verbs = [
	// search in list if current
	function(txt) {
		var route = frappe.get_route();
		if(route[0]==="List" && txt.indexOf(" in") === -1) {
			// search in title field
			var meta = frappe.get_meta(frappe.container.page.doclistview.doctype);
			var search_field = meta.title_field || "name";
			var options = {};
			options[search_field] = ["like", "%" + txt + "%"];
			frappe.search.options.push({
				label: __('Find {0} in {1}', [txt.bold(), route[1].bold()]),
				value: __('Find {0} in {1}', [txt, route[1]]),
				route_options: options,
				onclick: function() {
					cur_list.refresh();
				},
				match: txt
			});
		}
	},

	// new doc
	function(txt) {
		var ret = false;
		if(txt.split(" ")[0]==="new") {
			frappe.search.find(frappe.boot.user.can_create, txt.substr(4), function(match) {
				return {
					label: __("New {0}", [match.bold()]),
					value: __("New {0}", [match]),
					onclick: function() { frappe.new_doc(match, true); }
				}
			});
		}
	},

	// doctype list
	function(txt) {
		if (txt.toLowerCase().indexOf(" list")) {
			// remove list keyword
			txt = txt.replace(/ list/ig, "").trim();
		}

		frappe.search.find(frappe.boot.user.can_read, txt, function(match) {
			if(in_list(frappe.boot.single_types, match)) {
				return {
					label: __("{0}", [__(match).bold()]),
					value: __(match),
					route:["Form", match, match]
				}
			} else if(in_list(frappe.boot.treeviews, match)) {
				return {
					label: __("{0} Tree", [__(match).bold()]),
					value: __(match),
					route:["Tree", match]
				}
			} else {
				out = [
					{
						label: __("{0} List", [__(match).bold()]),
						value: __("{0} List", [__(match)]),
						route:["List", match]
					},
				];
				if(frappe.model.can_get_report(match)) {
					out.push({
						label: __("{0} Report", [__(match).bold()]),
						value: __("{0} Report", [__(match)]),
						route:["Report", match]
					});
				}
				if(frappe.boot.calendars.indexOf(match) !== -1) {
					out.push({
						label: __("{0} Calendar", [__(match).bold()]),
						value: __("{0} Calendar", [__(match)]),
						route:["Calendar", match]
					});

					out.push({
						label: __("{0} Gantt", [__(match).bold()]),
						value: __("{0} Gantt", [__(match)]),
						route:["List", match, "Gantt"]
					});
				}
				return out;
			}
		});
	},

	// reports
	function(txt) {
		frappe.search.find(keys(frappe.boot.user.all_reports), txt, function(match) {
			var report = frappe.boot.user.all_reports[match];
			var route = [];
			if(report.report_type == "Report Builder")
				route = ["Report", report.ref_doctype, match];
			else
				route = ["query-report",  match];

			return {
				label: __("Report {0}", [__(match).bold()]),
				value: __("Report {0}", [__(match)]),
				route: route
			}
		});
	},

	// pages
	function(txt) {
		frappe.search.find(keys(frappe.search.pages), txt, function(match) {
			return {
				label: __("Open {0}", [__(match).bold()]),
				value: __("Open {0}", [__(match)]),
				route: [frappe.search.pages[match].route || frappe.search.pages[match].name]
			}
		});

		// calendar
		var match = 'Calendar';
		if(__('calendar').indexOf(txt.toLowerCase()) === 0) {
			frappe.search.options.push({
				label: __("Open {0}", [__(match).bold()]),
				value: __("Open {0}", [__(match)]),
				route: [match, 'Event'],
				match: match
			});
		}
	},

	// modules
	function(txt) {
		frappe.search.find(keys(frappe.modules), txt, function(match) {
			var module = frappe.modules[match];

			if(module._doctype) return;

			ret = {
				label: __("Open {0}", [__(match).bold()]),
				value: __("Open {0}", [__(match)]),
			}
			if(module.link) {
				ret.route = [module.link];
			} else {
				ret.route = ["Module", match];
			}
			return ret;
		});
	},

	// in
	function(txt) {
		if(in_list(txt.split(" "), "in")) {
			parts = txt.split(" in ");
			frappe.search.find(frappe.boot.user.can_read, parts[1], function(match) {
				return {
					label: __('Find {0} in {1}', [__(parts[0]).bold(), __(match).bold()]),
					value: __('Find {0} in {1}', [__(parts[0]), __(match)]),
					route_options: {"name": ["like", "%" + parts[0] + "%"]},
					route: ["List", match]
				}
			});
		}
	},

	// calculator
	function(txt) {
		var first = txt.substr(0,1);
		if(first==parseInt(first) || first==="(" || first==="=") {
			if(first==="=") {
				txt = txt.substr(1);
			}

			try {
				var val = eval(txt);
				var formatted_value = __('{0} = {1}', [txt, (val + '').bold()]);
				frappe.search.options.push({
					label: formatted_value,
					value: __('{0} = {1}', [txt, val]),
					match: val,
					onclick: function(match) {
						msgprint(formatted_value, "Result");
					}
				});
			} catch(e) {
				// pass
			}

		};
	}
];
<|endoftext|>


<commit_before>
export default function Observer (data, callback, path) {
	defineProperties(data, callback, path, true);
	return data;
}

function defineProperties (data, callback, path, redefine) {
	path = path ? path + '.' : '';
	for (var key in data) defineProperty(data, key, data[key], callback, path, redefine);
	if (data.constructor === Object) overrideObjectMethods(data, callback, path);
	else if (data.constructor === Array) overrideArrayMethods(data, callback, path);
}

function defineProperty (data, key, value, callback, path, redefine) {
	var property = Object.getOwnPropertyDescriptor(data, key);

	if (property && property.configurable === false) return;

	var getter = property && property.get;
	var setter = property && property.set;

	// recursive observe child properties
	if (value && typeof value === 'object') defineProperties(value, callback, path + key, redefine);

	// set the property value if getter setter previously defined and redefine is false
	if (getter && setter && !redefine) return setter.call(data, value);
	// if (getter && setter && redefine === false) return setter.call(data, value);

	Object.defineProperty(data, key, {
		enumerable: true,
		configurable: true,
		get: function () {
			return getter ? getter.call(data) : value;
		},
		set: function (newValue) {

			var oldValue = getter ? getter.call(data) : value;

			// set the value with the same value not updated
			if (newValue === oldValue) return;

			if (setter) setter.call(data, newValue);
			else value = newValue;

			//	adds attributes to new valued property getter setter
			if (newValue && typeof newValue === 'object') defineProperties(newValue, callback, path + key, redefine);

			if (callback) callback(newValue, path + key, key, data);
		}
	});
}

function overrideObjectMethods (data, callback, path) {
	Object.defineProperties(data, {
		$set: {
			configurable: true,
			value: function (key, value) {
				if (typeof key !== 'string' || value === undefined) return;
				var isNew = !(key in data);
				defineProperty(data, key, value, callback, path);
				if (isNew && callback) callback(data[key], path + key, key, data);
				return data;
			}
		},
		$remove: {
			configurable: true,
			value: function (key) {
				if (typeof key !== 'string') return;
				delete data[key];
				if (callback) callback(undefined, path + key, key, data);
			}
		}
	});
}

function overrideArrayMethods (data, callback, path) {
	Object.defineProperties(data, {
		push: {
			configurable: true,
			value: function () {
				if (!arguments.length) return data.length;

				for (var i = 0, l = arguments.length; i < l; i++) {
					defineProperty(data, data.length, arguments[i], callback, path);

					if (callback) {
						callback(data.length, path.slice(0, -1), 'length', data);
						// callback(data.length, path + 'length', 'length', data);
						callback(data[data.length-1], path + (data.length-1), data.length-1, data);
					}

				}

				return data.length;
			}
		},
		unshift: {
			configurable: true,
			value: function () {
				if (!arguments.length) return data.length;

				var i, l, result = [];

				for (i = 0, l = arguments.length; i < l; i++) {
					result.push(arguments[i]);
				}

				for (i = 0, l = data.length; i < l; i++) {
					result.push(data[i]);
				}

				for (i = 0, l = data.length; i < l; i++) {
					data[i] = result[i];
				}

				for (i = 0, l = result.length; i < l; i++) {
				// for (i, l = result.length; i < l; i++) {
					defineProperty(data, data.length, result[i], callback, path);
					if (callback) {
						callback(data.length, path.slice(0, -1), 'length', data);
						// callback(data.length, path + 'length', 'length', data);
						callback(data[data.length-1], path + (data.length-1), data.length-1, data);
					}
				}

				return data.length;
			}
		},
		pop: {
			configurable: true,
			value: function () {
				if (!data.length) return;

				var value = data[data.length-1];

				data.length--;

				if (callback) {
					callback(data.length, path.slice(0, -1), 'length', data);
					// callback(data.length, path + 'length', 'length', data);
					callback(undefined, path + data.length, data.length, data);
				}

				return value;
			}
		},
		shift: {
			configurable: true,
			value: function () {
				if (!data.length) return;

				var value = data[0];

				for (var i = 0, l = data.length-1; i < l; i++) {
					data[i] = data[i+1];
				}

				data.length--;

				if (callback) {
					callback(data.length, path.slice(0, -1), 'length', data);
					// callback(data.length, path + 'length', 'length', data);
					callback(undefined, path + data.length, data.length, data);
				}

				return value;
			}
		},
		splice: {
			configurable: true,
			value: function (startIndex, deleteCount) {
				if (!data.length || (typeof startIndex !== 'number' && typeof deleteCount !== 'number')) return [];
				if (typeof startIndex !== 'number') startIndex = 0;
				if (typeof deleteCount !== 'number') deleteCount = data.length;

				var removed = [];
				var result = [];
				var index, i, l;

				// follow spec more or less
				// startIndex = parseInt(startIndex, 10);
				// deleteCount = parseInt(deleteCount, 10);

				// handle negative startIndex
				if (startIndex < 0) {
					startIndex = data.length + startIndex;
					startIndex = startIndex > 0 ? startIndex : 0;
				} else {
					startIndex = startIndex < data.length ? startIndex : data.length;
				}

				// handle negative deleteCount
				if (deleteCount < 0) {
					deleteCount = 0;
				} else if (deleteCount > (data.length - startIndex)) {
					deleteCount = data.length - startIndex;
				}

				// copy items up to startIndex
				for (i = 0; i < startIndex; i++) {
					result[i] = data[i];
				}

				// add new items from arguments
				for (i = 2, l = arguments.length; i < l; i++) {
					result.push(arguments[i]);
				}

				// copy removed items
				for (i = startIndex, l = startIndex + deleteCount; i < l; i++) {
					removed.push(data[i]);
				}

				// add the items after startIndex + deleteCount
				for (i = startIndex + deleteCount, l = data.length; i < l; i++) {
					result.push(data[i]);
				}

				index = 0;
				i = result.length - data.length;
				i = result.length - (i < 0 ? 0 : i);

				// update all observed items
				while (i--) {
					data[index] = result[index];
					index++;
				}

				i = result.length - data.length;

				// add and observe or remove items
				if (i > 0) {
					while (i--) {
						defineProperty(data, data.length, result[index++], callback, path);
						if (callback) {
							callback(data.length, path.slice(0, -1), 'length', data);
							// callback(data.length, path + 'length', 'length', data);
							callback(data[data.length-1], path + (data.length-1), data.length-1, data);
						}
					}
				} else if (i < 0) {
					while (i++) {
						data.length--;
						if (callback) {
							callback(data.length, path.slice(0, -1), 'length', data);
							// callback(data.length, path + 'length', 'length', data);
							callback(undefined, path + data.length, data.length, data);
						}
					}
				}

				return removed;
			}
		}
	});
}
<commit_msg>updated formatting<commit_after>
export default function Observer (data, callback, path) {
	defineProperties(data, callback, path, true);
	return data;
}

function defineProperties (data, callback, path, redefine) {
	path = path ? path + '.' : '';

	for (var key in data) {
		defineProperty(data, key, data[key], callback, path, redefine);
	}

	if (data.constructor === Object) {
		overrideObjectMethods(data, callback, path);
	} else if (data.constructor === Array) {
		overrideArrayMethods(data, callback, path);
	}
}

function defineProperty (data, key, value, callback, path, redefine) {
	var property = Object.getOwnPropertyDescriptor(data, key);

	if (property && property.configurable === false) {
		return;
	}

	var getter = property && property.get;
	var setter = property && property.set;

	// recursive observe child properties
	if (value && typeof value === 'object') {
		defineProperties(value, callback, path + key, redefine);
	}

	// set the property value if getter setter previously defined and redefine is false
	if (getter && setter && !redefine) {
		return setter.call(data, value);
	}

	Object.defineProperty(data, key, {
		enumerable: true,
		configurable: true,
		get: function () {
			return getter ? getter.call(data) : value;
		},
		set: function (newValue) {

			var oldValue = getter ? getter.call(data) : value;

			// set the value with the same value not updated
			if (newValue === oldValue) {
				return;
			}

			if (setter) {
				setter.call(data, newValue);
			} else {
				value = newValue;
			}

			//	adds attributes to new valued property getter setter
			if (newValue && typeof newValue === 'object') {
				defineProperties(newValue, callback, path + key, redefine);
			}

			if (callback) {
				callback(newValue, path + key, key, data);
			}

		}
	});
}

function overrideObjectMethods (data, callback, path) {
	Object.defineProperties(data, {
		$set: {
			configurable: true,
			value: function (key, value) {

				if (typeof key !== 'string' || value === undefined) {
					return;
				}

				defineProperty(data, key, value, callback, path);

				if (!(key in data) && callback) {
					callback(data[key], path + key, key, data);
				}

				return data;
			}
		},
		$remove: {
			configurable: true,
			value: function (key) {

				if (typeof key !== 'string') {
					return;
				}

				delete data[key];

				if (callback) {
					callback(undefined, path + key, key, data);
				}

			}
		}
	});
}

function overrideArrayMethods (data, callback, path) {
	Object.defineProperties(data, {
		push: {
			configurable: true,
			value: function () {
				if (!arguments.length) return data.length;

				for (var i = 0, l = arguments.length; i < l; i++) {
					defineProperty(data, data.length, arguments[i], callback, path);

					if (callback) {
						callback(data.length, path.slice(0, -1), 'length', data);
						callback(data[data.length-1], path + (data.length-1), data.length-1, data);
					}

				}

				return data.length;
			}
		},
		unshift: {
			configurable: true,
			value: function () {
				if (!arguments.length) return data.length;

				var i, l, result = [];

				for (i = 0, l = arguments.length; i < l; i++) {
					result.push(arguments[i]);
				}

				for (i = 0, l = data.length; i < l; i++) {
					result.push(data[i]);
				}

				for (i = 0, l = data.length; i < l; i++) {
					data[i] = result[i];
				}

				for (i = 0, l = result.length; i < l; i++) {
					defineProperty(data, data.length, result[i], callback, path);
					if (callback) {
						callback(data.length, path.slice(0, -1), 'length', data);
						callback(data[data.length-1], path + (data.length-1), data.length-1, data);
					}
				}

				return data.length;
			}
		},
		pop: {
			configurable: true,
			value: function () {
				if (!data.length) return;

				var value = data[data.length-1];

				data.length--;

				if (callback) {
					callback(data.length, path.slice(0, -1), 'length', data);
					callback(undefined, path + data.length, data.length, data);
				}

				return value;
			}
		},
		shift: {
			configurable: true,
			value: function () {
				if (!data.length) return;

				var value = data[0];

				for (var i = 0, l = data.length-1; i < l; i++) {
					data[i] = data[i+1];
				}

				data.length--;

				if (callback) {
					callback(data.length, path.slice(0, -1), 'length', data);
					callback(undefined, path + data.length, data.length, data);
				}

				return value;
			}
		},
		splice: {
			configurable: true,
			value: function (startIndex, deleteCount) {
				if (!data.length || (typeof startIndex !== 'number' && typeof deleteCount !== 'number')) return [];
				if (typeof startIndex !== 'number') startIndex = 0;
				if (typeof deleteCount !== 'number') deleteCount = data.length;

				var removed = [];
				var result = [];
				var index, i, l;

				// this would follow spec more or less
				// startIndex = parseInt(startIndex, 10);
				// deleteCount = parseInt(deleteCount, 10);

				// handle negative startIndex
				if (startIndex < 0) {
					startIndex = data.length + startIndex;
					startIndex = startIndex > 0 ? startIndex : 0;
				} else {
					startIndex = startIndex < data.length ? startIndex : data.length;
				}

				// handle negative deleteCount
				if (deleteCount < 0) {
					deleteCount = 0;
				} else if (deleteCount > (data.length - startIndex)) {
					deleteCount = data.length - startIndex;
				}

				// copy items up to startIndex
				for (i = 0; i < startIndex; i++) {
					result[i] = data[i];
				}

				// add new items from arguments
				for (i = 2, l = arguments.length; i < l; i++) {
					result.push(arguments[i]);
				}

				// copy removed items
				for (i = startIndex, l = startIndex + deleteCount; i < l; i++) {
					removed.push(data[i]);
				}

				// add the items after startIndex + deleteCount
				for (i = startIndex + deleteCount, l = data.length; i < l; i++) {
					result.push(data[i]);
				}

				index = 0;
				i = result.length - data.length;
				i = result.length - (i < 0 ? 0 : i);

				// update all observed items
				while (i--) {
					data[index] = result[index];
					index++;
				}

				i = result.length - data.length;

				// add and observe or remove items
				if (i > 0) {
					while (i--) {
						defineProperty(data, data.length, result[index++], callback, path);
						if (callback) {
							callback(data.length, path.slice(0, -1), 'length', data);
							callback(data[data.length-1], path + (data.length-1), data.length-1, data);
						}
					}
				} else if (i < 0) {
					while (i++) {
						data.length--;
						if (callback) {
							callback(data.length, path.slice(0, -1), 'length', data);
							callback(undefined, path + data.length, data.length, data);
						}
					}
				}

				return removed;
			}
		}
	});
}
<|endoftext|>