<commit_before>AUI().add(
	'sortable',
	function(A) {
		var Lang = A.Lang,

			getClassName = A.ClassNameManager.getClassName,

			NAME = 'sortable',

			DDM = A.DD.DDM,

			CSS_DRAGGING = getClassName(NAME, 'dragging'),
			CSS_HANDLE = getClassName(NAME, 'handle'),
			CSS_ITEM = getClassName(NAME, 'item'),
			CSS_NO_HANDLES = getClassName(NAME, 'no-handles'),
			CSS_PLACEHOLDER = getClassName(NAME, 'placeholder'),
			CSS_PROXY = getClassName(NAME, 'proxy'),
			CSS_SORTABLE = getClassName(NAME);

		var Sortable = function() {
			Sortable.superclass.constructor.apply(this, arguments);
		};

		Sortable.NAME = NAME;

		Sortable.ATTRS = {
			dd: {
				value: {}
			},

			constrain: {},

			container: {
				value: null
			},

			groups: {
				valueFn: function() {
					var instance = this;

					return [A.guid()];
				}
			},

			nodes: {
				value: null,
				setter: function(value) {
					var instance = this;

					var container = instance.get('container');

					if (!(value instanceof A.NodeList)) {
						if(Lang.isString(value)) {
							if (container) {
								value = container.queryAll(value);
							}
							else {
								value = A.all(value);
							}
						}
					}

					if (value instanceof A.NodeList && value.size()) {
						instance.set('container', value.item(0).get('parentNode'));
					}
					else {
						value = A.Attribute.INVALID_VALUE;
					}

					return value;
				}
			},

			placeholder: {},

			proxy: {}
		};

		A.extend(
			Sortable,
			A.Base,
			{
				initializer: function() {
					var instance = this;

					var nodes = instance.get('nodes');

					var constrain = instance.get('constrain');

					instance._useConstrain = !!constrain;

					if (Lang.isObject(constrain)) {
						instance._constrainConfig = constrain;
					}

					var proxy = instance.get('proxy');

					instance._useProxy = !!proxy;

					if (Lang.isObject(proxy)) {
						instance._proxyConfig = proxy;
					}

					var ddConfig = instance.get('dd');

					instance._ddConfig = ddConfig;

					instance._ddConfig = A.mix(
						instance._ddConfig,
						{
							bubbles: instance,
							groups: instance.get('groups'),
							placeholder: instance.get('placeholder'),
							constrain: instance.get('constrain'),
							proxy: instance.get('proxy')
						}
					);

					if (nodes) {
						nodes.each(instance.add, instance);
					}

					instance.after('drag:drag', instance._onDrag);
					instance.after('drag:end', instance._onDragEnd);
					instance.after('drag:over', instance._onDragOver);
					instance.after('drag:start', instance._onDragStart);
				},

				add: function(item) {
					var instance = this;

					if (item) {
						var ddConfig = instance._ddConfig;

						if ((item instanceof A.Node) || Lang.isString(item) || item.nodeName) {
							item = A.get(item);

							ddConfig.node = item;
						}
						else if (Lang.isObject(item)) {
							A.mix(item, ddConfig);

							ddConfig = item;
						}

						var sortableItem = instance.getSortableItem();

						var dd = new sortableItem(ddConfig);
					}
				},

				getSortableItem: function() {
					var instance = this;

					return SortableItem;
				},

				_onDrag: function(event) {
					var instance = this;

					var drag = event.target;

					var lastXY = drag.lastXY;
					var x = lastXY[0];
					var y = lastXY[1];

					var lastX = instance._lastX;
					var lastY = instance._lastY;

					var xToleranceMet = Math.abs(x - lastX);
					var yToleranceMet = Math.abs(y - lastY);

					instance._goingUp = ((x < lastX) && xToleranceMet) || ((y < lastY) && yToleranceMet);

					instance._lastX = x;
					instance._lastY = y;

					A.later(50, DDM, DDM.syncActiveShims);
				},

				_onDragEnd: function(event) {
					var instance = this;

					var drag = event.target;
					var node = drag.get('node');

					node.removeClass(CSS_DRAGGING);
				},

				_onDragOver: function(event) {
					var instance = this;

					var sortDirection = 0;
					var midpoint = 0;
					var lastX = instance._lastX;
					var lastY = instance._lastY;

					var DDM = A.DD.DDM;
					var drag = event.drag;
					var drop = event.drop;

					var dropNode = drop.get('node');

					var action = 'placeBefore';

					if (!instance._goingUp) {
						action = 'placeAfter';
					}

					dropNode[action](drag.get('placeholder'));

					drop.sizeShim();
				},

				_onDragStart: function(event) {
					var instance = this;

					var drag = event.target;

					var node = drag.get('node');

					if (instance._useProxy) {
						drag.get('dragNode').addClass(CSS_PROXY);
					}

					node.addClass(CSS_DRAGGING);
				},

				_lastX: 0,
				_lastY: 0
			}
		);

		var SortableItem = function(config) {
			SortableItem.superclass.constructor.call(this, config.dd || config);
		};

		SortableItem.NAME = 'sortableitem';

		SortableItem.ATTRS = {
			constrain: {
				value: null
			},

			placeholder: {
				getter: function() {
					var instance = this;

					return instance.get('node');
				}
			},

			proxy: {
				value: {
					borderStyle: 0,
					moveOnEnd: false
				}
			},

			target: {
				value: true
			},

			syncPlaceholderSize: {
				value: true
			}
		};

		A.extend(
			SortableItem,
			A.DD.Drag,
			{
				initializer: function() {
					var instance = this;

					var node = instance.get('node');

					node.dd = instance;

					node.addClass(CSS_ITEM);

					instance._initHandles();
					instance._initConstrain();
					instance._initProxy();
				},

				_initHandles: function() {
					var instance = this;

					var handles = instance.get('handles');
					var node = instance.get('node');

					if (handles) {
						for (var i = handles.length - 1; i >= 0; i--) {
							var handle = handles[i];

							node.queryAll(handle).addClass(CSS_HANDLE);
						}
					}
					else {
						node.addClass(CSS_NO_HANDLES);
					}

					instance.removeInvalid('a');
				},

				_initConstrain: function() {
					var instance = this;

					var constrain = instance.get('constrain');

					if (!!constrain) {
						if (!Lang.isObject(constrain)) {
							constrain = null;
						}

						instance.plug(A.Plugin.DDConstrained, constrain);
					}
				},

				_initProxy: function() {
					var instance = this;

					var proxy = instance.get('proxy');

					if (!!proxy) {
						if (!Lang.isObject(proxy)) {
							proxy = null;
						}

						instance.plug(A.Plugin.DDProxy, proxy);

						instance.get('dragNode').addClass(CSS_PROXY);
					}
				}
			}
		);

		A.Sortable = Sortable;
		A.SortableItem = SortableItem;
	},
	'@VERSION',
	{
		requires: [ 'aui-base', 'dd', 'sortable-css' ],
		use: []
	}
);<commit_msg>Adding class to proxy on DOM ready to guarantee the dragNode has been properly created<commit_after>AUI().add(
	'sortable',
	function(A) {
		var Lang = A.Lang,

			getClassName = A.ClassNameManager.getClassName,

			NAME = 'sortable',

			DDM = A.DD.DDM,

			CSS_DRAGGING = getClassName(NAME, 'dragging'),
			CSS_HANDLE = getClassName(NAME, 'handle'),
			CSS_ITEM = getClassName(NAME, 'item'),
			CSS_NO_HANDLES = getClassName(NAME, 'no-handles'),
			CSS_PLACEHOLDER = getClassName(NAME, 'placeholder'),
			CSS_PROXY = getClassName(NAME, 'proxy'),
			CSS_SORTABLE = getClassName(NAME);

		var Sortable = function() {
			Sortable.superclass.constructor.apply(this, arguments);
		};

		Sortable.NAME = NAME;

		Sortable.ATTRS = {
			dd: {
				value: {}
			},

			constrain: {},

			container: {
				value: null
			},

			groups: {
				valueFn: function() {
					var instance = this;

					return [A.guid()];
				}
			},

			nodes: {
				value: null,
				setter: function(value) {
					var instance = this;

					var container = instance.get('container');

					if (!(value instanceof A.NodeList)) {
						if(Lang.isString(value)) {
							if (container) {
								value = container.queryAll(value);
							}
							else {
								value = A.all(value);
							}
						}
					}

					if (value instanceof A.NodeList && value.size()) {
						instance.set('container', value.item(0).get('parentNode'));
					}
					else {
						value = A.Attribute.INVALID_VALUE;
					}

					return value;
				}
			},

			placeholder: {},

			proxy: {}
		};

		A.extend(
			Sortable,
			A.Base,
			{
				initializer: function() {
					var instance = this;

					var nodes = instance.get('nodes');

					var constrain = instance.get('constrain');

					instance._useConstrain = !!constrain;

					if (Lang.isObject(constrain)) {
						instance._constrainConfig = constrain;
					}

					var proxy = instance.get('proxy');

					instance._useProxy = !!proxy;

					if (Lang.isObject(proxy)) {
						instance._proxyConfig = proxy;
					}

					var ddConfig = instance.get('dd');

					instance._ddConfig = ddConfig;

					instance._ddConfig = A.mix(
						instance._ddConfig,
						{
							bubbles: instance,
							groups: instance.get('groups'),
							placeholder: instance.get('placeholder'),
							constrain: instance.get('constrain'),
							proxy: instance.get('proxy')
						}
					);

					if (nodes) {
						nodes.each(instance.add, instance);
					}

					instance.after('drag:drag', instance._onDrag);
					instance.after('drag:end', instance._onDragEnd);
					instance.after('drag:over', instance._onDragOver);
					instance.after('drag:start', instance._onDragStart);
				},

				add: function(item) {
					var instance = this;

					if (item) {
						var ddConfig = instance._ddConfig;

						if ((item instanceof A.Node) || Lang.isString(item) || item.nodeName) {
							item = A.get(item);

							ddConfig.node = item;
						}
						else if (Lang.isObject(item)) {
							A.mix(item, ddConfig);

							ddConfig = item;
						}

						var sortableItem = instance.getSortableItem();

						var dd = new sortableItem(ddConfig);
					}
				},

				getSortableItem: function() {
					var instance = this;

					return SortableItem;
				},

				_onDrag: function(event) {
					var instance = this;

					var drag = event.target;

					var lastXY = drag.lastXY;
					var x = lastXY[0];
					var y = lastXY[1];

					var lastX = instance._lastX;
					var lastY = instance._lastY;

					var xToleranceMet = Math.abs(x - lastX);
					var yToleranceMet = Math.abs(y - lastY);

					instance._goingUp = ((x < lastX) && xToleranceMet) || ((y < lastY) && yToleranceMet);

					instance._lastX = x;
					instance._lastY = y;

					A.later(50, DDM, DDM.syncActiveShims);
				},

				_onDragEnd: function(event) {
					var instance = this;

					var drag = event.target;
					var node = drag.get('node');

					node.removeClass(CSS_DRAGGING);
				},

				_onDragOver: function(event) {
					var instance = this;

					var sortDirection = 0;
					var midpoint = 0;
					var lastX = instance._lastX;
					var lastY = instance._lastY;

					var DDM = A.DD.DDM;
					var drag = event.drag;
					var drop = event.drop;

					var dropNode = drop.get('node');

					var action = 'placeBefore';

					if (!instance._goingUp) {
						action = 'placeAfter';
					}

					dropNode[action](drag.get('placeholder'));

					drop.sizeShim();
				},

				_onDragStart: function(event) {
					var instance = this;

					var drag = event.target;

					var node = drag.get('node');

					if (instance._useProxy) {
						drag.get('dragNode').addClass(CSS_PROXY);
					}

					node.addClass(CSS_DRAGGING);
				},

				_lastX: 0,
				_lastY: 0
			}
		);

		var SortableItem = function(config) {
			SortableItem.superclass.constructor.call(this, config.dd || config);
		};

		SortableItem.NAME = 'sortableitem';

		SortableItem.ATTRS = {
			constrain: {
				value: null
			},

			placeholder: {
				getter: function() {
					var instance = this;

					return instance.get('node');
				}
			},

			proxy: {
				value: {
					borderStyle: 0,
					moveOnEnd: false
				}
			},

			target: {
				value: true
			},

			syncPlaceholderSize: {
				value: true
			}
		};

		A.extend(
			SortableItem,
			A.DD.Drag,
			{
				initializer: function() {
					var instance = this;

					var node = instance.get('node');

					node.dd = instance;

					node.addClass(CSS_ITEM);

					instance._initHandles();
					instance._initConstrain();
					instance._initProxy();
				},

				_initHandles: function() {
					var instance = this;

					var handles = instance.get('handles');
					var node = instance.get('node');

					if (handles) {
						for (var i = handles.length - 1; i >= 0; i--) {
							var handle = handles[i];

							node.queryAll(handle).addClass(CSS_HANDLE);
						}
					}
					else {
						node.addClass(CSS_NO_HANDLES);
					}

					instance.removeInvalid('a');
				},

				_initConstrain: function() {
					var instance = this;

					var constrain = instance.get('constrain');

					if (!!constrain) {
						if (!Lang.isObject(constrain)) {
							constrain = null;
						}

						instance.plug(A.Plugin.DDConstrained, constrain);
					}
				},

				_initProxy: function() {
					var instance = this;

					var proxy = instance.get('proxy');

					if (!!proxy) {
						if (!Lang.isObject(proxy)) {
							proxy = null;
						}

						instance.plug(A.Plugin.DDProxy, proxy);

						A.on(
							'domready',
							function(A) {
								instance.get('dragNode').addClass(CSS_PROXY);
							}
						);
					}
				}
			}
		);

		A.Sortable = Sortable;
		A.SortableItem = SortableItem;
	},
	'@VERSION',
	{
		requires: [ 'aui-base', 'dd', 'sortable-css' ],
		use: []
	}
);<|endoftext|>


<commit_before>/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule RelayFileWriter
 * @flow
 * @format
 */

'use strict';

const ASTConvert = require('ASTConvert');
const CodegenDirectory = require('CodegenDirectory');
const RelayCompiler = require('RelayCompiler');
const RelayCompilerContext = require('RelayCompilerContext');
const RelayFlowGenerator = require('RelayFlowGenerator');
const RelayValidator = require('RelayValidator');

const invariant = require('invariant');
const path = require('path');
const printFlowTypes = require('printFlowTypes');
const writeLegacyFlowFile = require('./writeLegacyFlowFile');
const writeRelayGeneratedFile = require('./writeRelayGeneratedFile');

const {isOperationDefinitionAST} = require('GraphQLSchemaUtils');
const {generate} = require('RelayCodeGenerator');
const {Map: ImmutableMap} = require('immutable');

import type {RelayGeneratedNode} from 'RelayCodeGenerator';
import type {FileWriterInterface} from 'RelayCodegenTypes';
import type {CompiledNode, CompiledDocumentMap} from 'RelayCompiler';
import type {CompilerTransforms} from 'RelayCompiler';
import type {GeneratedNode} from 'RelayConcreteNode';
import type {ScalarTypeMapping} from 'RelayFlowGenerator';
import type {DocumentNode, GraphQLSchema} from 'graphql';
import type {FormatModule} from 'writeRelayGeneratedFile';

export type GenerateExtraFiles = (
  getOutputDirectory: (path?: string) => CodegenDirectory,
  compilerContext: RelayCompilerContext,
) => void;

export type WriterConfig = {
  baseDir: string,
  formatModule: FormatModule,
  compilerTransforms: CompilerTransforms,
  customScalars?: ScalarTypeMapping,
  generateExtraFiles?: GenerateExtraFiles,
  outputDir?: string,
  persistQuery?: (text: string) => Promise<string>,
  platform?: string,
  fragmentsWithLegacyFlowTypes?: Set<string>,
  schemaExtensions: Array<string>,
  relayRuntimeModule?: string,
  inputFieldWhiteListForFlow?: Array<string>,
};

/* eslint-disable no-console-disallow */

class RelayFileWriter implements FileWriterInterface {
  _onlyValidate: boolean;
  _config: WriterConfig;
  _baseSchema: GraphQLSchema;
  _baseDocuments: ImmutableMap<string, DocumentNode>;
  _documents: ImmutableMap<string, DocumentNode>;

  constructor(options: {
    config: WriterConfig,
    onlyValidate: boolean,
    baseDocuments: ImmutableMap<string, DocumentNode>,
    documents: ImmutableMap<string, DocumentNode>,
    schema: GraphQLSchema,
  }) {
    const {config, onlyValidate, baseDocuments, documents, schema} = options;
    this._baseDocuments = baseDocuments || ImmutableMap();
    this._baseSchema = schema;
    this._config = config;
    this._documents = documents;
    this._onlyValidate = onlyValidate;

    validateConfig(this._config);
  }

  async writeAll(): Promise<Map<string, CodegenDirectory>> {
    const tStart = Date.now();

    // Can't convert to IR unless the schema already has Relay-local extensions
    const transformedSchema = ASTConvert.transformASTSchema(
      this._baseSchema,
      this._config.schemaExtensions,
    );
    const extendedSchema = ASTConvert.extendASTSchema(
      transformedSchema,
      this._baseDocuments.merge(this._documents).valueSeq().toArray(),
    );

    // Build a context from all the documents
    const baseDefinitionNames = new Set();
    this._baseDocuments.forEach(doc => {
      doc.definitions.forEach(def => {
        if (isOperationDefinitionAST(def) && def.name) {
          baseDefinitionNames.add(def.name.value);
        }
      });
    });
    const definitionDirectories = new Map();
    const allOutputDirectories: Map<string, CodegenDirectory> = new Map();
    const addCodegenDir = dirPath => {
      const codegenDir = new CodegenDirectory(dirPath, {
        onlyValidate: this._onlyValidate,
      });
      allOutputDirectories.set(dirPath, codegenDir);
      return codegenDir;
    };

    let configOutputDirectory;
    if (this._config.outputDir) {
      configOutputDirectory = addCodegenDir(this._config.outputDir);
    } else {
      this._documents.forEach((doc, filePath) => {
        doc.definitions.forEach(def => {
          if (isOperationDefinitionAST(def) && def.name) {
            definitionDirectories.set(
              def.name.value,
              path.join(this._config.baseDir, path.dirname(filePath)),
            );
          }
        });
      });
    }

    const definitions = ASTConvert.convertASTDocumentsWithBase(
      extendedSchema,
      this._baseDocuments.valueSeq().toArray(),
      this._documents.valueSeq().toArray(),
      // Verify using local and global rules, can run global verifications here
      // because all files are processed together
      [...RelayValidator.LOCAL_RULES, ...RelayValidator.GLOBAL_RULES],
    );

    const compilerContext = new RelayCompilerContext(extendedSchema);
    const compiler = new RelayCompiler(
      this._baseSchema,
      compilerContext,
      this._config.compilerTransforms,
      generate,
    );

    const getGeneratedDirectory = definitionName => {
      if (configOutputDirectory) {
        return configOutputDirectory;
      }
      const definitionDir = definitionDirectories.get(definitionName);
      invariant(
        definitionDir,
        'RelayFileWriter: Could not determine source directory for definition: %s',
        definitionName,
      );
      const generatedPath = path.join(definitionDir, '__generated__');
      let cachedDir = allOutputDirectories.get(generatedPath);
      if (!cachedDir) {
        cachedDir = addCodegenDir(generatedPath);
      }
      return cachedDir;
    };

    compiler.addDefinitions(definitions);

    const transformedFlowContext = RelayFlowGenerator.flowTransforms.reduce(
      (ctx, transform) => transform(ctx, extendedSchema),
      compiler.context(),
    );
    const transformedQueryContext = compiler.transformedQueryContext();
    const compiledDocumentMap: CompiledDocumentMap<
      RelayGeneratedNode,
    > = compiler.compile();

    const tCompiled = Date.now();

    let tGenerated;
    try {
      await Promise.all(
        transformedFlowContext.documents().map(async node => {
          if (baseDefinitionNames.has(node.name)) {
            // don't add definitions that were part of base context
            return;
          }
          if (
            this._config.fragmentsWithLegacyFlowTypes &&
            this._config.fragmentsWithLegacyFlowTypes.has(node.name)
          ) {
            const legacyFlowTypes = printFlowTypes(node);
            if (legacyFlowTypes) {
              writeLegacyFlowFile(
                getGeneratedDirectory(node.name),
                node.name,
                legacyFlowTypes,
                this._config.platform,
              );
            }
          }

          const flowTypes = RelayFlowGenerator.generate(
            node,
            this._config.customScalars,
            this._config.inputFieldWhiteListForFlow,
          );

          const compiledNode = compiledDocumentMap.get(node.name);
          invariant(
            compiledNode,
            'RelayCompiler: did not compile definition: %s',
            node.name,
          );
          await writeRelayGeneratedFile(
            getGeneratedDirectory(compiledNode.name),
            getGeneratedNode(compiledNode),
            this._config.formatModule,
            flowTypes,
            this._config.persistQuery,
            this._config.platform,
            this._config.relayRuntimeModule || 'relay-runtime',
          );
        }),
      );
      tGenerated = Date.now();

      if (this._config.generateExtraFiles) {
        const configDirectory = this._config.outputDir;
        this._config.generateExtraFiles(dir => {
          const outputDirectory = dir || configDirectory;
          invariant(
            outputDirectory,
            'RelayFileWriter: cannot generate extra files without specifying ' +
              'an outputDir in the config or passing it in.',
          );
          let outputDir = allOutputDirectories.get(outputDirectory);
          if (!outputDir) {
            outputDir = addCodegenDir(outputDirectory);
          }
          return outputDir;
        }, transformedQueryContext);
      }

      // clean output directories
      allOutputDirectories.forEach(dir => {
        dir.deleteExtraFiles();
      });
    } catch (error) {
      tGenerated = Date.now();
      let details;
      try {
        details = JSON.parse(error.message);
      } catch (_) {}
      if (details && details.name === 'GraphQL2Exception' && details.message) {
        console.log('ERROR writing modules:\n' + details.message);
      } else {
        console.log('Error writing modules:\n' + error.toString());
      }
      return allOutputDirectories;
    }

    const tExtra = Date.now();
    console.log(
      'Writer time: %s [%s compiling, %s generating, %s extra]',
      toSeconds(tStart, tExtra),
      toSeconds(tStart, tCompiled),
      toSeconds(tCompiled, tGenerated),
      toSeconds(tGenerated, tExtra),
    );
    return allOutputDirectories;
  }
}

function getGeneratedNode(
  compiledNode: CompiledNode<RelayGeneratedNode>,
): GeneratedNode {
  invariant(
    typeof compiledNode === 'object' &&
      compiledNode !== null &&
      (compiledNode.kind === 'Fragment' || compiledNode.kind === 'Batch'),
    'getGeneratedNode: Expected a GeneratedNode, got `%s`.',
    JSON.stringify(compiledNode),
  );
  return (compiledNode: any);
}

function toSeconds(t0, t1) {
  return ((t1 - t0) / 1000).toFixed(2) + 's';
}

function validateConfig(config: Object): void {
  if (config.buildCommand) {
    process.stderr.write(
      'WARNING: RelayFileWriter: For RelayFileWriter to work you must ' +
        'replace config.buildCommand with config.formatModule.\n',
    );
  }
}

module.exports = RelayFileWriter;
<commit_msg>RelayFileWriter should not swallow exceptions<commit_after>/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule RelayFileWriter
 * @flow
 * @format
 */

'use strict';

const ASTConvert = require('ASTConvert');
const CodegenDirectory = require('CodegenDirectory');
const RelayCompiler = require('RelayCompiler');
const RelayCompilerContext = require('RelayCompilerContext');
const RelayFlowGenerator = require('RelayFlowGenerator');
const RelayValidator = require('RelayValidator');

const invariant = require('invariant');
const path = require('path');
const printFlowTypes = require('printFlowTypes');
const writeLegacyFlowFile = require('./writeLegacyFlowFile');
const writeRelayGeneratedFile = require('./writeRelayGeneratedFile');

const {isOperationDefinitionAST} = require('GraphQLSchemaUtils');
const {generate} = require('RelayCodeGenerator');
const {Map: ImmutableMap} = require('immutable');

import type {RelayGeneratedNode} from 'RelayCodeGenerator';
import type {FileWriterInterface} from 'RelayCodegenTypes';
import type {CompiledNode, CompiledDocumentMap} from 'RelayCompiler';
import type {CompilerTransforms} from 'RelayCompiler';
import type {GeneratedNode} from 'RelayConcreteNode';
import type {ScalarTypeMapping} from 'RelayFlowGenerator';
import type {DocumentNode, GraphQLSchema} from 'graphql';
import type {FormatModule} from 'writeRelayGeneratedFile';

export type GenerateExtraFiles = (
  getOutputDirectory: (path?: string) => CodegenDirectory,
  compilerContext: RelayCompilerContext,
) => void;

export type WriterConfig = {
  baseDir: string,
  formatModule: FormatModule,
  compilerTransforms: CompilerTransforms,
  customScalars?: ScalarTypeMapping,
  generateExtraFiles?: GenerateExtraFiles,
  outputDir?: string,
  persistQuery?: (text: string) => Promise<string>,
  platform?: string,
  fragmentsWithLegacyFlowTypes?: Set<string>,
  schemaExtensions: Array<string>,
  relayRuntimeModule?: string,
  inputFieldWhiteListForFlow?: Array<string>,
};

/* eslint-disable no-console-disallow */

class RelayFileWriter implements FileWriterInterface {
  _onlyValidate: boolean;
  _config: WriterConfig;
  _baseSchema: GraphQLSchema;
  _baseDocuments: ImmutableMap<string, DocumentNode>;
  _documents: ImmutableMap<string, DocumentNode>;

  constructor(options: {
    config: WriterConfig,
    onlyValidate: boolean,
    baseDocuments: ImmutableMap<string, DocumentNode>,
    documents: ImmutableMap<string, DocumentNode>,
    schema: GraphQLSchema,
  }) {
    const {config, onlyValidate, baseDocuments, documents, schema} = options;
    this._baseDocuments = baseDocuments || ImmutableMap();
    this._baseSchema = schema;
    this._config = config;
    this._documents = documents;
    this._onlyValidate = onlyValidate;

    validateConfig(this._config);
  }

  async writeAll(): Promise<Map<string, CodegenDirectory>> {
    const tStart = Date.now();

    // Can't convert to IR unless the schema already has Relay-local extensions
    const transformedSchema = ASTConvert.transformASTSchema(
      this._baseSchema,
      this._config.schemaExtensions,
    );
    const extendedSchema = ASTConvert.extendASTSchema(
      transformedSchema,
      this._baseDocuments.merge(this._documents).valueSeq().toArray(),
    );

    // Build a context from all the documents
    const baseDefinitionNames = new Set();
    this._baseDocuments.forEach(doc => {
      doc.definitions.forEach(def => {
        if (isOperationDefinitionAST(def) && def.name) {
          baseDefinitionNames.add(def.name.value);
        }
      });
    });
    const definitionDirectories = new Map();
    const allOutputDirectories: Map<string, CodegenDirectory> = new Map();
    const addCodegenDir = dirPath => {
      const codegenDir = new CodegenDirectory(dirPath, {
        onlyValidate: this._onlyValidate,
      });
      allOutputDirectories.set(dirPath, codegenDir);
      return codegenDir;
    };

    let configOutputDirectory;
    if (this._config.outputDir) {
      configOutputDirectory = addCodegenDir(this._config.outputDir);
    } else {
      this._documents.forEach((doc, filePath) => {
        doc.definitions.forEach(def => {
          if (isOperationDefinitionAST(def) && def.name) {
            definitionDirectories.set(
              def.name.value,
              path.join(this._config.baseDir, path.dirname(filePath)),
            );
          }
        });
      });
    }

    const definitions = ASTConvert.convertASTDocumentsWithBase(
      extendedSchema,
      this._baseDocuments.valueSeq().toArray(),
      this._documents.valueSeq().toArray(),
      // Verify using local and global rules, can run global verifications here
      // because all files are processed together
      [...RelayValidator.LOCAL_RULES, ...RelayValidator.GLOBAL_RULES],
    );

    const compilerContext = new RelayCompilerContext(extendedSchema);
    const compiler = new RelayCompiler(
      this._baseSchema,
      compilerContext,
      this._config.compilerTransforms,
      generate,
    );

    const getGeneratedDirectory = definitionName => {
      if (configOutputDirectory) {
        return configOutputDirectory;
      }
      const definitionDir = definitionDirectories.get(definitionName);
      invariant(
        definitionDir,
        'RelayFileWriter: Could not determine source directory for definition: %s',
        definitionName,
      );
      const generatedPath = path.join(definitionDir, '__generated__');
      let cachedDir = allOutputDirectories.get(generatedPath);
      if (!cachedDir) {
        cachedDir = addCodegenDir(generatedPath);
      }
      return cachedDir;
    };

    compiler.addDefinitions(definitions);

    const transformedFlowContext = RelayFlowGenerator.flowTransforms.reduce(
      (ctx, transform) => transform(ctx, extendedSchema),
      compiler.context(),
    );
    const transformedQueryContext = compiler.transformedQueryContext();
    const compiledDocumentMap: CompiledDocumentMap<
      RelayGeneratedNode,
    > = compiler.compile();

    const tCompiled = Date.now();

    let tGenerated;
    try {
      await Promise.all(
        transformedFlowContext.documents().map(async node => {
          if (baseDefinitionNames.has(node.name)) {
            // don't add definitions that were part of base context
            return;
          }
          if (
            this._config.fragmentsWithLegacyFlowTypes &&
            this._config.fragmentsWithLegacyFlowTypes.has(node.name)
          ) {
            const legacyFlowTypes = printFlowTypes(node);
            if (legacyFlowTypes) {
              writeLegacyFlowFile(
                getGeneratedDirectory(node.name),
                node.name,
                legacyFlowTypes,
                this._config.platform,
              );
            }
          }

          const flowTypes = RelayFlowGenerator.generate(
            node,
            this._config.customScalars,
            this._config.inputFieldWhiteListForFlow,
          );

          const compiledNode = compiledDocumentMap.get(node.name);
          invariant(
            compiledNode,
            'RelayCompiler: did not compile definition: %s',
            node.name,
          );
          await writeRelayGeneratedFile(
            getGeneratedDirectory(compiledNode.name),
            getGeneratedNode(compiledNode),
            this._config.formatModule,
            flowTypes,
            this._config.persistQuery,
            this._config.platform,
            this._config.relayRuntimeModule || 'relay-runtime',
          );
        }),
      );
      tGenerated = Date.now();

      if (this._config.generateExtraFiles) {
        const configDirectory = this._config.outputDir;
        this._config.generateExtraFiles(dir => {
          const outputDirectory = dir || configDirectory;
          invariant(
            outputDirectory,
            'RelayFileWriter: cannot generate extra files without specifying ' +
              'an outputDir in the config or passing it in.',
          );
          let outputDir = allOutputDirectories.get(outputDirectory);
          if (!outputDir) {
            outputDir = addCodegenDir(outputDirectory);
          }
          return outputDir;
        }, transformedQueryContext);
      }

      // clean output directories
      allOutputDirectories.forEach(dir => {
        dir.deleteExtraFiles();
      });
    } catch (error) {
      tGenerated = Date.now();
      let details;
      try {
        details = JSON.parse(error.message);
      } catch (_) {}
      if (details && details.name === 'GraphQL2Exception' && details.message) {
        throw new Error('GraphQL error writing modules:\n' + details.message);
      }
      throw new Error('Error writing modules:\n' + error.toString());
    }

    const tExtra = Date.now();
    console.log(
      'Writer time: %s [%s compiling, %s generating, %s extra]',
      toSeconds(tStart, tExtra),
      toSeconds(tStart, tCompiled),
      toSeconds(tCompiled, tGenerated),
      toSeconds(tGenerated, tExtra),
    );
    return allOutputDirectories;
  }
}

function getGeneratedNode(
  compiledNode: CompiledNode<RelayGeneratedNode>,
): GeneratedNode {
  invariant(
    typeof compiledNode === 'object' &&
      compiledNode !== null &&
      (compiledNode.kind === 'Fragment' || compiledNode.kind === 'Batch'),
    'getGeneratedNode: Expected a GeneratedNode, got `%s`.',
    JSON.stringify(compiledNode),
  );
  return (compiledNode: any);
}

function toSeconds(t0, t1) {
  return ((t1 - t0) / 1000).toFixed(2) + 's';
}

function validateConfig(config: Object): void {
  if (config.buildCommand) {
    process.stderr.write(
      'WARNING: RelayFileWriter: For RelayFileWriter to work you must ' +
        'replace config.buildCommand with config.formatModule.\n',
    );
  }
}

module.exports = RelayFileWriter;
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var $documents = [];

if ( window ) $documents.push(window.document);

// TODO: clean this up, hide $WID__ in closure
var $WID__ = 0;
function $addWindow(w) {
   w.window.$WID = $WID__++;
   $documents.push(w.document);
}
function $removeWindow(w) {
  for ( var i = $documents.length - 1 ; i >= 0 ; i-- ) {
    if ( ! $documents[i].defaultView || $documents[i].defaultView === w )
      $documents.splice(i,1);
  }
}

/** Replacement for getElementById **/
// TODO(kgr): remove this is deprecated, use X.$ instead()
var $ = function (id) {
  console.log('Deprecated use of GLOBAL.$.');
  for ( var i = 0 ; i < $documents.length ; i++ ) {
    if ( document.FOAM_OBJECTS && document.FOAM_OBJECTS[id] )
      return document.FOAM_OBJECTS[id];

    var ret = $documents[i].getElementById(id);

    if ( ret ) return ret;
  }
  return undefined;
};
/** Replacement for getElementByClassName **/
// TODO(kgr): remove this is deprecated, use X.$$ instead()
var $$ = function (cls) {
  console.log('Deprecated use of GLOBAL.$$.');
  for ( var i = 0 ; i < $documents.length ; i++ ) {
    var ret = $documents[i].getElementsByClassName(cls);

    if ( ret.length > 0 ) return ret;
  }
  return [];
};

var FOAM = function(map, opt_X, seq) {
   var obj = JSONUtil.mapToObj(opt_X || X, map, undefined, seq);
   return obj;
};

/**
 * Register a lazy factory for the specified name within a
 * specified context.
 * The first time the name is looked up, the factory will be invoked
 * and its value will be stored in the named slot and then returned.
 * Future lookups to the same slot will return the originally created
 * value.
 **/
FOAM.putFactory = function(ctx, name, factory) {
  ctx.__defineGetter__(name, function() {
    console.log('Bouncing Factory: ', name);
    delete ctx[name];
    return ctx[name] = factory();
  });
};

var   USED_MODELS = {};
var UNUSED_MODELS = {};
var NONMODEL_INSTANCES = {}; // for things such as interfaces

FOAM.browse = function(model, opt_dao, opt_X) {
   var Y = opt_X || X.sub(undefined, "FOAM BROWSER");

   if ( typeof model === 'string' ) model = Y[model];

   var dao = opt_dao || Y[model.name + 'DAO'] || Y[model.plural];

   if ( ! dao ) {
      Y[model.name + 'DAO'] = [].dao;
   }

   var ctrl = Y.foam.ui.DAOController.create({
     model:     model,
     dao:       dao,
     useSearchView: false
   });

  if ( ! Y.stack ) {
    var w = opt_X ? opt_X.window : window;
    Y.stack = Y.foam.ui.StackView.create();
    var win = Y.foam.ui.layout.Window.create({ window: w, data: Y.stack }, Y);
    document.body.insertAdjacentHTML('beforeend', win.toHTML());
    win.initHTML();
    Y.stack.setTopView(ctrl);
  } else {
    Y.stack.pushView(ctrl);
  }
};

var arequire = function(modelName, opt_X) {
  var X = opt_X || GLOBAL.X;
  var model = X.lookup(modelName);
  if ( ! model ) {
    if ( ! X.ModelDAO ) {
      // if ( modelName !== 'Template' ) console.warn('Unknown Model in arequire: ', modelName);
      return aconstant();
    }

    // check whether we have already hit the ModelDAO to load the model
    if ( ! X.arequire$ModelLoadsInProgress ) {
      X.set('arequire$ModelLoadsInProgress', {} );
    } else {
      if ( X.arequire$ModelLoadsInProgress[modelName] ) {
        return X.arequire$ModelLoadsInProgress[modelName];
      }
    }

    var future = afuture();
    X.arequire$ModelLoadsInProgress[modelName] = future.get;

    X.ModelDAO.find(modelName, {
      put: function(m) {
        // Contextualize the model for this context
        m.X = X;

        m.arequire()(function(m) {
          X.arequire$ModelLoadsInProgress[modelName] = false;
          X.registerModel(m);
          future.set(m);
        });
      },
      error: function() {
        var args = argsToArray(arguments);
        console.warn.apply(console, ['Could not load model: ', modelName].concat(args));
        X.arequire$ModelLoadsInProgress[modelName] = false;
        future.set(undefined);
      }
    });

    return future.get;
  }

  return model.arequire()
}

var FOAM_POWERED = '\
\
FOAM\
 POWERED';

/** Lookup a '.'-separated package path, creating sub-packages as required. **/
function packagePath(X, path) {
  function packagePath_(Y, path, i) {
    if ( i === path.length ) return Y;
    if ( ! Y[path[i]] ) {
      Y[path[i]] = {};
      // console.log('************ Creating sub-path: ', path[i]);
      if ( i == 0 ) GLOBAL[path[i]] = Y[path[i]];
    }
    return packagePath_(Y[path[i]], path, i+1);
  }
  return path ? packagePath_(X, path.split('.'), 0) : GLOBAL;
}

function registerModel(model, opt_name) {
  var root    = model.package ? this : GLOBAL;
  var name    = model.name;
  var package = model.package;

  if ( opt_name ) {
    var a = opt_name.split('.');
    name = a.pop();
    package = a.join('.');
  }

  var path = packagePath(root, package);
  Object.defineProperty(path, name, { value: model, configurable: true });

  // TODO: this is broken
  // update the cache if this model was already FOAM.lookup'd
  if ( root.lookupCache_ ) {
    var cache = root.lookupCache_;
    var modelRegName = (package ? package + '.' : '') + name;
//    if ( cache[modelRegName] ) {
      // console.log("registerModel: in lookupCache_, replaced model ", modelRegName );
      cache[modelRegName] = model;
//    }
  }

  this.onRegisterModel(model);
}

var CLASS = function(m) {

  /** Lazily create and register Model first time it's accessed. **/
  function registerModelLatch(path, m) {
    var id = m.package ? m.package + '.' + m.name : m.name;

    GLOBAL.lookupCache_[id] = undefined;
    UNUSED_MODELS[id] = true;

    // TODO(adamvy): Remove this once we no longer have code depending on models to being in the global scope.
    if ( ! m.package )
      Object.defineProperty(GLOBAL, m.name, { get: function() { return path[m.name]; }, configurable: true });

    //console.log("Model Getting defined: ", m.name, X.NAME);
    Object.defineProperty(path, m.name, {
      get: function () {
        // console.time('registerModel: ' + id);
        USED_MODELS[id] = true;
        UNUSED_MODELS[id] = undefined;
        Object.defineProperty(path, m.name, {value: null, configurable: true});

        var work = [];
        // console.time('buildModel: ' + id);
        var model = JSONUtil.mapToObj(X, m, Model, work);
        // console.timeEnd('buildModel: ' + id);

        if ( work.length > 0 ) {
          model.extra__ = aseq.apply(null, work);
        }

        X.registerModel(model);

        // console.timeEnd('registerModel: ' + id);
        return model;
      },
      configurable: true
    });
  }

  if ( document && document.currentScript ) m.sourcePath = document.currentScript.src;

  registerModelLatch(packagePath(X, m.package), m);
}

var MODEL = CLASS;

function INTERFACE(imodel) {
  // Unless in DEBUG mode, just discard interfaces as they're only used for type checking.
  // if ( ! DEBUG ) return;
  var i = JSONUtil.mapToObj(X, imodel, Interface);
  packagePath(X, i.package)[i.name] = i;

  var id = i.package ? i.package + '.' + i.name : i.name;
  NONMODEL_INSTANCES[id] = true;
}

/** Called when a Model is registered. **/
function onRegisterModel(m) {
  // NOP
}
<commit_msg>Small perf improvement.<commit_after>/**
 * @license
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var $documents = [];

if ( window ) $documents.push(window.document);

// TODO: clean this up, hide $WID__ in closure
var $WID__ = 0;
function $addWindow(w) {
   w.window.$WID = $WID__++;
   $documents.push(w.document);
}
function $removeWindow(w) {
  for ( var i = $documents.length - 1 ; i >= 0 ; i-- ) {
    if ( ! $documents[i].defaultView || $documents[i].defaultView === w )
      $documents.splice(i,1);
  }
}

/** Replacement for getElementById **/
// TODO(kgr): remove this is deprecated, use X.$ instead()
var $ = function (id) {
  console.log('Deprecated use of GLOBAL.$.');
  for ( var i = 0 ; i < $documents.length ; i++ ) {
    if ( document.FOAM_OBJECTS && document.FOAM_OBJECTS[id] )
      return document.FOAM_OBJECTS[id];

    var ret = $documents[i].getElementById(id);

    if ( ret ) return ret;
  }
  return undefined;
};
/** Replacement for getElementByClassName **/
// TODO(kgr): remove this is deprecated, use X.$$ instead()
var $$ = function (cls) {
  console.log('Deprecated use of GLOBAL.$$.');
  for ( var i = 0 ; i < $documents.length ; i++ ) {
    var ret = $documents[i].getElementsByClassName(cls);

    if ( ret.length > 0 ) return ret;
  }
  return [];
};

var FOAM = function(map, opt_X, seq) {
   var obj = JSONUtil.mapToObj(opt_X || X, map, undefined, seq);
   return obj;
};

/**
 * Register a lazy factory for the specified name within a
 * specified context.
 * The first time the name is looked up, the factory will be invoked
 * and its value will be stored in the named slot and then returned.
 * Future lookups to the same slot will return the originally created
 * value.
 **/
FOAM.putFactory = function(ctx, name, factory) {
  ctx.__defineGetter__(name, function() {
    console.log('Bouncing Factory: ', name);
    delete ctx[name];
    return ctx[name] = factory();
  });
};

var   USED_MODELS = {};
var UNUSED_MODELS = {};
var NONMODEL_INSTANCES = {}; // for things such as interfaces

FOAM.browse = function(model, opt_dao, opt_X) {
   var Y = opt_X || X.sub(undefined, "FOAM BROWSER");

   if ( typeof model === 'string' ) model = Y[model];

   var dao = opt_dao || Y[model.name + 'DAO'] || Y[model.plural];

   if ( ! dao ) {
      Y[model.name + 'DAO'] = [].dao;
   }

   var ctrl = Y.foam.ui.DAOController.create({
     model:     model,
     dao:       dao,
     useSearchView: false
   });

  if ( ! Y.stack ) {
    var w = opt_X ? opt_X.window : window;
    Y.stack = Y.foam.ui.StackView.create();
    var win = Y.foam.ui.layout.Window.create({ window: w, data: Y.stack }, Y);
    document.body.insertAdjacentHTML('beforeend', win.toHTML());
    win.initHTML();
    Y.stack.setTopView(ctrl);
  } else {
    Y.stack.pushView(ctrl);
  }
};

var arequire = function(modelName, opt_X) {
  var X = opt_X || GLOBAL.X;
  var model = X.lookup(modelName);
  if ( ! model ) {
    if ( ! X.ModelDAO ) {
      // if ( modelName !== 'Template' ) console.warn('Unknown Model in arequire: ', modelName);
      return aconstant();
    }

    // check whether we have already hit the ModelDAO to load the model
    if ( ! X.arequire$ModelLoadsInProgress ) {
      X.set('arequire$ModelLoadsInProgress', {} );
    } else {
      if ( X.arequire$ModelLoadsInProgress[modelName] ) {
        return X.arequire$ModelLoadsInProgress[modelName];
      }
    }

    var future = afuture();
    X.arequire$ModelLoadsInProgress[modelName] = future.get;

    X.ModelDAO.find(modelName, {
      put: function(m) {
        // Contextualize the model for this context
        m.X = X;

        m.arequire()(function(m) {
          X.arequire$ModelLoadsInProgress[modelName] = false;
          X.registerModel(m);
          future.set(m);
        });
      },
      error: function() {
        var args = argsToArray(arguments);
        console.warn.apply(console, ['Could not load model: ', modelName].concat(args));
        X.arequire$ModelLoadsInProgress[modelName] = false;
        future.set(undefined);
      }
    });

    return future.get;
  }

  return model.arequire()
}

var FOAM_POWERED = '\
\
FOAM\
 POWERED';

/** Lookup a '.'-separated package path, creating sub-packages as required. **/
function packagePath(X, path) {
  function packagePath_(Y, path, i) {
    if ( i === path.length ) return Y;
    if ( ! Y[path[i]] ) {
      Y[path[i]] = {};
      // console.log('************ Creating sub-path: ', path[i]);
      if ( i == 0 ) GLOBAL[path[i]] = Y[path[i]];
    }
    return packagePath_(Y[path[i]], path, i+1);
  }
  return path ? packagePath_(X, path.split('.'), 0) : GLOBAL;
}

function registerModel(model, opt_name) {
  var root    = model.package ? this : GLOBAL;
  var name    = model.name;
  var package = model.package;

  if ( opt_name ) {
    var a = opt_name.split('.');
    name = a.pop();
    package = a.join('.');
  }

  var path = packagePath(root, package);
  Object.defineProperty(path, name, { value: model, configurable: true });

  // TODO: this is broken
  // update the cache if this model was already FOAM.lookup'd
  if ( root.lookupCache_ ) {
    var cache = root.lookupCache_;
    var modelRegName = (package ? package + '.' : '') + name;
//    if ( cache[modelRegName] ) {
      // console.log("registerModel: in lookupCache_, replaced model ", modelRegName );
      cache[modelRegName] = model;
//    }
  }

  this.onRegisterModel(model);
}

var CLASS = function(m) {

  /** Lazily create and register Model first time it's accessed. **/
  function registerModelLatch(path, m) {
    var id = m.package ? m.package + '.' + m.name : m.name;

    GLOBAL.lookupCache_[id] = undefined;
    UNUSED_MODELS[id] = true;

    //console.log("Model Getting defined: ", m.name, X.NAME);
    Object.defineProperty(m.package ? path : GLOBAL, m.name, {
      get: function triggerModelLatch() {
        // console.time('registerModel: ' + id);
        USED_MODELS[id] = true;
        UNUSED_MODELS[id] = undefined;
        Object.defineProperty(path, m.name, {value: null, configurable: true});

        var work = [];
        // console.time('buildModel: ' + id);
        var model = JSONUtil.mapToObj(X, m, Model, work);
        // console.timeEnd('buildModel: ' + id);

        if ( work.length > 0 ) {
          model.extra__ = aseq.apply(null, work);
        }

        X.registerModel(model);

        // console.timeEnd('registerModel: ' + id);
        return model;
      },
      configurable: true
    });
  }

//  if ( document && document.currentScript ) m.sourcePath = document.currentScript.src;

  registerModelLatch(packagePath(X, m.package), m);
}

var MODEL = CLASS;

function INTERFACE(imodel) {
  // Unless in DEBUG mode, just discard interfaces as they're only used for type checking.
  // if ( ! DEBUG ) return;
  var i = JSONUtil.mapToObj(X, imodel, Interface);
  packagePath(X, i.package)[i.name] = i;

  var id = i.package ? i.package + '.' + i.name : i.name;
  NONMODEL_INSTANCES[id] = true;
}

/** Called when a Model is registered. **/
function onRegisterModel(m) {
  // NOP
}
<|endoftext|>


<commit_before>/*
 * jQuery simple-color plugin
 * @requires jQuery v1.4.2 or later
 *
 * See http://recursive-design.com/projects/jquery-simple-color/
 *
 * Licensed under the MIT license:
 *   http://www.opensource.org/licenses/mit-license.php
 *
 * Version: @VERSION (@DATE)
 */
 (function($) {
/**
 * simpleColor() provides a mechanism for displaying simple color-pickers.
 *
 * If an options Object is provided, the following attributes are supported:
 *
 *  defaultColor:       Default (initially selected) color.
 *                      Default value: '#FFF'
 *
 *  border:             CSS border properties.
 *                      Default value: '1px solid #000'
 *
 *  cellWidth:          Width of each individual color cell.
 *                      Default value: 10
 *
 *  cellHeight:         Height of each individual color cell.
 *                      Default value: 10
 *
 *  cellMargin:         Margin of each individual color cell.
 *                      Default value: 1
 *
 *  boxWidth:           Width of the color display box.
 *                      Default value: 115px
 *
 *  boxHeight:          Height of the color display box.
 *                      Default value: 20px
 *
 *  columns:            Number of columns to display. Color order may look strange if this is altered.
 *                      Default value: 16
 *
 *  insert:             The position to insert the color picker. 'before' or 'after'.
 *                      Default value: 'after'
 *
 *  colors:             An array of colors to display, if you want to customize the default color set.
 *                      Default value: default color set - see 'default_colors' below.
 *
 *  displayColorCode:   Display the color code (eg #333333) as text inside the button. true or false.
 *                      Default value: false
 *
 *  colorCodeAlign:     Text alignment used to display the color code inside the button. Only used if 'displayColorCode' is true. 'left', 'center' or 'right'
 *                      Default value: 'center'
 *
 *  colorCodeColor:     Text color of the color code inside the button. Only used if 'displayColorCode' is true.
 *                      Default value: '#FFF'
 *
 *  onSelect:           Callback function to call after a color has been chosen.
 *                      Default value: null
 *                      Returns:       hex value
 *
 *  onCellEnter:        Callback function that excecutes when the mouse enters a cell.
 *                      Default value: null
 *                      Returns:       hex value
 *
 *  onClose:            Callback function that executes when the chooser is closed.
 *                      Default value: null
 *
 *  livePreview:        The color display will change to show the color of the hovered color cell.
 *                      The display will revert if no color is selected.
 *                      Default value: false
 */
  $.fn.simpleColor = function(options) {

    var default_colors = [
      '990033', 'ff3366', 'cc0033', 'ff0033', 'ff9999', 'cc3366', 'ffccff', 'cc6699',
      '993366', '660033', 'cc3399', 'ff99cc', 'ff66cc', 'ff99ff', 'ff6699', 'cc0066',
      'ff0066', 'ff3399', 'ff0099', 'ff33cc', 'ff00cc', 'ff66ff', 'ff33ff', 'ff00ff',
      'cc0099', '990066', 'cc66cc', 'cc33cc', 'cc99ff', 'cc66ff', 'cc33ff', '993399',
      'cc00cc', 'cc00ff', '9900cc', '990099', 'cc99cc', '996699', '663366', '660099',
      '9933cc', '660066', '9900ff', '9933ff', '9966cc', '330033', '663399', '6633cc',
      '6600cc', '9966ff', '330066', '6600ff', '6633ff', 'ccccff', '9999ff', '9999cc',
      '6666cc', '6666ff', '666699', '333366', '333399', '330099', '3300cc', '3300ff',
      '3333ff', '3333cc', '0066ff', '0033ff', '3366ff', '3366cc', '000066', '000033',
      '0000ff', '000099', '0033cc', '0000cc', '336699', '0066cc', '99ccff', '6699ff',
      '003366', '6699cc', '006699', '3399cc', '0099cc', '66ccff', '3399ff', '003399',
      '0099ff', '33ccff', '00ccff', '99ffff', '66ffff', '33ffff', '00ffff', '00cccc',
      '009999', '669999', '99cccc', 'ccffff', '33cccc', '66cccc', '339999', '336666',
      '006666', '003333', '00ffcc', '33ffcc', '33cc99', '00cc99', '66ffcc', '99ffcc',
      '00ff99', '339966', '006633', '336633', '669966', '66cc66', '99ff99', '66ff66',
      '339933', '99cc99', '66ff99', '33ff99', '33cc66', '00cc66', '66cc99', '009966',
      '009933', '33ff66', '00ff66', 'ccffcc', 'ccff99', '99ff66', '99ff33', '00ff33',
      '33ff33', '00cc33', '33cc33', '66ff33', '00ff00', '66cc33', '006600', '003300',
      '009900', '33ff00', '66ff00', '99ff00', '66cc00', '00cc00', '33cc00', '339900',
      '99cc66', '669933', '99cc33', '336600', '669900', '99cc00', 'ccff66', 'ccff33',
      'ccff00', '999900', 'cccc00', 'cccc33', '333300', '666600', '999933', 'cccc66',
      '666633', '999966', 'cccc99', 'ffffcc', 'ffff99', 'ffff66', 'ffff33', 'ffff00',
      'ffcc00', 'ffcc66', 'ffcc33', 'cc9933', '996600', 'cc9900', 'ff9900', 'cc6600',
      '993300', 'cc6633', '663300', 'ff9966', 'ff6633', 'ff9933', 'ff6600', 'cc3300',
      '996633', '330000', '663333', '996666', 'cc9999', '993333', 'cc6666', 'ffcccc',
      'ff3333', 'cc3333', 'ff6666', '660000', '990000', 'cc0000', 'ff0000', 'ff3300',
      'cc9966', 'ffcc99', 'ffffff', 'cccccc', '999999', '666666', '333333', '000000',
      '000000', '000000', '000000', '000000', '000000', '000000', '000000', '000000'
    ];

    // Option defaults
    options = $.extend({
      defaultColor:     this.attr('defaultColor') || '#FFF',
      border:           this.attr('border') || '1px solid #000',
      cellWidth:        this.attr('cellWidth') || 10,
      cellHeight:       this.attr('cellHeight') || 10,
      cellMargin:       this.attr('cellMargin') || 1,
      boxWidth:         this.attr('boxWidth') || '115px',
      boxHeight:        this.attr('boxHeight') || '20px',
      columns:          this.attr('columns') || 16,
      insert:           this.attr('insert') || 'after',
      buttonClass:      this.attr('buttonClass') || '',
      colors:           this.attr('colors') || default_colors,
      displayColorCode: this.attr('displayColorCode') || false,
      colorCodeAlign:   this.attr('colorCodeAlign') || 'center',
      colorCodeColor:   this.attr('colorCodeColor') || '#FFF',
      onSelect:         null,
      onCellEnter:      null,
      onClose:          null,
      livePreview:      false
    }, options || {});

    // Hide the input
    this.hide();

    // Figure out the cell dimensions
    options.totalWidth = options.columns * (options.cellWidth + (2 * options.cellMargin));
    if ($.browser.msie) {
      options.totalWidth += 2;
    }

    options.totalHeight = Math.ceil(options.colors.length / options.columns) * (options.cellHeight + (2 * options.cellMargin));

    // Store these options so they'll be available to the other functions
    // TODO - must be a better way to do this, not sure what the 'official'
    // jQuery method is. Ideally i want to pass these as a parameter to the
    // each() function but i'm not sure how
    $.simpleColorOptions = options;

    function buildSelector(index) {
      options = $.simpleColorOptions;

      // Create a container to hold everything
      var container = $("
");

      // Absolutely positioned child elements now 'work'.
			container.css('position', 'relative');

      // Create the color display box
      var default_color = (this.value && this.value != '') ? this.value : options.defaultColor;

      var display_box = $("
");
      display_box.css({
        'backgroundColor': default_color,
        'border':          options.border,
        'width':           options.boxWidth,
        'height':          options.boxHeight,
        // Make sure that the code is vertically centered.
        'line-height':     options.boxHeight,
        'cursor':          'pointer'
      });
      container.append(display_box);

      // If 'displayColorCode' is turned on, display the currently selected color code as text inside the button.
      if (options.displayColorCode) {
        display_box.text(this.value);
        display_box.css({
          'color':     options.colorCodeColor,
          'textAlign': options.colorCodeAlign
        });
      }

      var select_callback = function (event) {

        // Bind and namespace the click listener only when the chooser is
        // displayed. Unbind when the chooser is closed.
        $('html').bind("click.simpleColorDisplay", function(e) {
          $('html').unbind("click.simpleColorDisplay");
          $('.simpleColorChooser').hide();

          // If the user has not selected a new color, then revert the display.
          // Makes sure the selected cell is within the current color selector.
          var target = $(e.target);
          if (target.is('.simpleColorCell') === false || $.contains( $(event.target).closest('.simpleColorContainer')[0], target[0]) === false) {
            display_box.css('backgroundColor', default_color);
            if (options.displayColorCode) {
              display_box.text(default_color);
            }
          }
          // Execute onClose callback whenever the color chooser is closed.
          if (options.onClose) {
            options.onClose();
          }
        });

        // Use an existing chooser if there is one
        if (event.data.container.chooser) {
          event.data.container.chooser.toggle();

        // Build the chooser.
        } else {

          // Make a chooser div to hold the cells
          var chooser = $("
");
          chooser.css({
            'border':   options.border,
            'margin':   '0 0 0 5px',
            'width':    options.totalWidth,
            'height':   options.totalHeight,
            'top':      0,
            'left':     options.boxWidth,
            'position': 'absolute'
          });

          event.data.container.chooser = chooser;
          event.data.container.append(chooser);

          // Create the cells
          for (var i=0; i<options.colors.length; i++) {
            var cell = $("
");
            cell.css({
              'width':           options.cellWidth + 'px',
              'height':          options.cellHeight + 'px',
              'margin':          options.cellMargin + 'px',
              'cursor':          'pointer',
              'lineHeight':      options.cellHeight + 'px',
              'fontSize':        '1px',
              'float':           'left',
              'backgroundColor': '#'+options.colors[i]
            });
            chooser.append(cell);
            if (options.onCellEnter || options.livePreview) {
              cell.bind('mouseenter', function(event) {
                if (options.onCellEnter) {
                  options.onCellEnter(this.id);
                }
                if (options.livePreview) {
                  display_box.css('backgroundColor', '#' + this.id);
                  if (options.displayColorCode) {
                    display_box.text('#' + this.id);
                  }
                }
              });
            }
            cell.bind('click', {
              input: event.data.input,
              chooser: chooser,
              display_box: display_box
            },
            function(event) {
              event.data.input.value = '#' + this.id;
              $(event.data.input).change();
              event.data.display_box.css('backgroundColor', '#' + this.id);
              event.data.chooser.hide();
              event.data.display_box.show();

              // If 'displayColorCode' is turned on, display the currently selected color code as text inside the button.
              if (options.displayColorCode) {
                event.data.display_box.text('#' + this.id);
              }
              // If an onSelect callback function is defined then excecute it.
              if (options.onSelect) {
                options.onSelect(this.id);
              }

            });
          }
        }
      };

      var callback_params = {
        container: container,
        input: this,
        display_box: display_box
      };

      // Also bind the display box button to display the chooser.
      display_box.bind('click', callback_params, select_callback);

      $(this).after(container);

    };

    this.each(buildSelector);

    $('.simpleColorDisplay').each(function() {
      $(this).click(function(e){
        e.stopPropagation();
      });
    });

    return this;
  };

  /*
   * Close the given color selectors
   */
  $.fn.closeSelector = function() {
    this.each( function(index) {
      var container = $(this).parent().find('div.simpleColorContainer');
      container.find('.simpleColorChooser').hide();
      container.find('.simpleColorDisplay').show();
    });

    return this;
  };

})(jQuery);
<commit_msg>Remove reliance on $.browser for jQuery 1.9.1<commit_after>/*
 * jQuery simple-color plugin
 * @requires jQuery v1.4.2 or later
 *
 * See http://recursive-design.com/projects/jquery-simple-color/
 *
 * Licensed under the MIT license:
 *   http://www.opensource.org/licenses/mit-license.php
 *
 * Version: @VERSION (@DATE)
 */
 (function($) {
/**
 * simpleColor() provides a mechanism for displaying simple color-pickers.
 *
 * If an options Object is provided, the following attributes are supported:
 *
 *  defaultColor:       Default (initially selected) color.
 *                      Default value: '#FFF'
 *
 *  border:             CSS border properties.
 *                      Default value: '1px solid #000'
 *
 *  cellWidth:          Width of each individual color cell.
 *                      Default value: 10
 *
 *  cellHeight:         Height of each individual color cell.
 *                      Default value: 10
 *
 *  cellMargin:         Margin of each individual color cell.
 *                      Default value: 1
 *
 *  boxWidth:           Width of the color display box.
 *                      Default value: 115px
 *
 *  boxHeight:          Height of the color display box.
 *                      Default value: 20px
 *
 *  columns:            Number of columns to display. Color order may look strange if this is altered.
 *                      Default value: 16
 *
 *  insert:             The position to insert the color picker. 'before' or 'after'.
 *                      Default value: 'after'
 *
 *  colors:             An array of colors to display, if you want to customize the default color set.
 *                      Default value: default color set - see 'default_colors' below.
 *
 *  displayColorCode:   Display the color code (eg #333333) as text inside the button. true or false.
 *                      Default value: false
 *
 *  colorCodeAlign:     Text alignment used to display the color code inside the button. Only used if 'displayColorCode' is true. 'left', 'center' or 'right'
 *                      Default value: 'center'
 *
 *  colorCodeColor:     Text color of the color code inside the button. Only used if 'displayColorCode' is true.
 *                      Default value: '#FFF'
 *
 *  onSelect:           Callback function to call after a color has been chosen.
 *                      Default value: null
 *                      Returns:       hex value
 *
 *  onCellEnter:        Callback function that excecutes when the mouse enters a cell.
 *                      Default value: null
 *                      Returns:       hex value
 *
 *  onClose:            Callback function that executes when the chooser is closed.
 *                      Default value: null
 *
 *  livePreview:        The color display will change to show the color of the hovered color cell.
 *                      The display will revert if no color is selected.
 *                      Default value: false
 */
  $.fn.simpleColor = function(options) {

    var default_colors = [
      '990033', 'ff3366', 'cc0033', 'ff0033', 'ff9999', 'cc3366', 'ffccff', 'cc6699',
      '993366', '660033', 'cc3399', 'ff99cc', 'ff66cc', 'ff99ff', 'ff6699', 'cc0066',
      'ff0066', 'ff3399', 'ff0099', 'ff33cc', 'ff00cc', 'ff66ff', 'ff33ff', 'ff00ff',
      'cc0099', '990066', 'cc66cc', 'cc33cc', 'cc99ff', 'cc66ff', 'cc33ff', '993399',
      'cc00cc', 'cc00ff', '9900cc', '990099', 'cc99cc', '996699', '663366', '660099',
      '9933cc', '660066', '9900ff', '9933ff', '9966cc', '330033', '663399', '6633cc',
      '6600cc', '9966ff', '330066', '6600ff', '6633ff', 'ccccff', '9999ff', '9999cc',
      '6666cc', '6666ff', '666699', '333366', '333399', '330099', '3300cc', '3300ff',
      '3333ff', '3333cc', '0066ff', '0033ff', '3366ff', '3366cc', '000066', '000033',
      '0000ff', '000099', '0033cc', '0000cc', '336699', '0066cc', '99ccff', '6699ff',
      '003366', '6699cc', '006699', '3399cc', '0099cc', '66ccff', '3399ff', '003399',
      '0099ff', '33ccff', '00ccff', '99ffff', '66ffff', '33ffff', '00ffff', '00cccc',
      '009999', '669999', '99cccc', 'ccffff', '33cccc', '66cccc', '339999', '336666',
      '006666', '003333', '00ffcc', '33ffcc', '33cc99', '00cc99', '66ffcc', '99ffcc',
      '00ff99', '339966', '006633', '336633', '669966', '66cc66', '99ff99', '66ff66',
      '339933', '99cc99', '66ff99', '33ff99', '33cc66', '00cc66', '66cc99', '009966',
      '009933', '33ff66', '00ff66', 'ccffcc', 'ccff99', '99ff66', '99ff33', '00ff33',
      '33ff33', '00cc33', '33cc33', '66ff33', '00ff00', '66cc33', '006600', '003300',
      '009900', '33ff00', '66ff00', '99ff00', '66cc00', '00cc00', '33cc00', '339900',
      '99cc66', '669933', '99cc33', '336600', '669900', '99cc00', 'ccff66', 'ccff33',
      'ccff00', '999900', 'cccc00', 'cccc33', '333300', '666600', '999933', 'cccc66',
      '666633', '999966', 'cccc99', 'ffffcc', 'ffff99', 'ffff66', 'ffff33', 'ffff00',
      'ffcc00', 'ffcc66', 'ffcc33', 'cc9933', '996600', 'cc9900', 'ff9900', 'cc6600',
      '993300', 'cc6633', '663300', 'ff9966', 'ff6633', 'ff9933', 'ff6600', 'cc3300',
      '996633', '330000', '663333', '996666', 'cc9999', '993333', 'cc6666', 'ffcccc',
      'ff3333', 'cc3333', 'ff6666', '660000', '990000', 'cc0000', 'ff0000', 'ff3300',
      'cc9966', 'ffcc99', 'ffffff', 'cccccc', '999999', '666666', '333333', '000000',
      '000000', '000000', '000000', '000000', '000000', '000000', '000000', '000000'
    ];

    // Option defaults
    options = $.extend({
      defaultColor:     this.attr('defaultColor') || '#FFF',
      border:           this.attr('border') || '1px solid #000',
      cellWidth:        this.attr('cellWidth') || 10,
      cellHeight:       this.attr('cellHeight') || 10,
      cellMargin:       this.attr('cellMargin') || 1,
      boxWidth:         this.attr('boxWidth') || '115px',
      boxHeight:        this.attr('boxHeight') || '20px',
      columns:          this.attr('columns') || 16,
      insert:           this.attr('insert') || 'after',
      buttonClass:      this.attr('buttonClass') || '',
      colors:           this.attr('colors') || default_colors,
      displayColorCode: this.attr('displayColorCode') || false,
      colorCodeAlign:   this.attr('colorCodeAlign') || 'center',
      colorCodeColor:   this.attr('colorCodeColor') || '#FFF',
      onSelect:         null,
      onCellEnter:      null,
      onClose:          null,
      livePreview:      false
    }, options || {});

    // Hide the input
    this.hide();

    // Figure out the cell dimensions
    options.totalWidth = options.columns * (options.cellWidth + (2 * options.cellMargin));

    // this should probably do feature detection - I don't know why we need +2 for IE
    // but this works for jQuery 1.9.1
    if (navigator.userAgent.indexOf("MSIE")!=-1){
      options.totalWidth += 2;
    }

    options.totalHeight = Math.ceil(options.colors.length / options.columns) * (options.cellHeight + (2 * options.cellMargin));

    // Store these options so they'll be available to the other functions
    // TODO - must be a better way to do this, not sure what the 'official'
    // jQuery method is. Ideally i want to pass these as a parameter to the
    // each() function but i'm not sure how
    $.simpleColorOptions = options;

    function buildSelector(index) {
      options = $.simpleColorOptions;

      // Create a container to hold everything
      var container = $("
");

      // Absolutely positioned child elements now 'work'.
			container.css('position', 'relative');

      // Create the color display box
      var default_color = (this.value && this.value != '') ? this.value : options.defaultColor;

      var display_box = $("
");
      display_box.css({
        'backgroundColor': default_color,
        'border':          options.border,
        'width':           options.boxWidth,
        'height':          options.boxHeight,
        // Make sure that the code is vertically centered.
        'line-height':     options.boxHeight,
        'cursor':          'pointer'
      });
      container.append(display_box);

      // If 'displayColorCode' is turned on, display the currently selected color code as text inside the button.
      if (options.displayColorCode) {
        display_box.text(this.value);
        display_box.css({
          'color':     options.colorCodeColor,
          'textAlign': options.colorCodeAlign
        });
      }

      var select_callback = function (event) {

        // Bind and namespace the click listener only when the chooser is
        // displayed. Unbind when the chooser is closed.
        $('html').bind("click.simpleColorDisplay", function(e) {
          $('html').unbind("click.simpleColorDisplay");
          $('.simpleColorChooser').hide();

          // If the user has not selected a new color, then revert the display.
          // Makes sure the selected cell is within the current color selector.
          var target = $(e.target);
          if (target.is('.simpleColorCell') === false || $.contains( $(event.target).closest('.simpleColorContainer')[0], target[0]) === false) {
            display_box.css('backgroundColor', default_color);
            if (options.displayColorCode) {
              display_box.text(default_color);
            }
          }
          // Execute onClose callback whenever the color chooser is closed.
          if (options.onClose) {
            options.onClose();
          }
        });

        // Use an existing chooser if there is one
        if (event.data.container.chooser) {
          event.data.container.chooser.toggle();

        // Build the chooser.
        } else {

          // Make a chooser div to hold the cells
          var chooser = $("
");
          chooser.css({
            'border':   options.border,
            'margin':   '0 0 0 5px',
            'width':    options.totalWidth,
            'height':   options.totalHeight,
            'top':      0,
            'left':     options.boxWidth,
            'position': 'absolute'
          });

          event.data.container.chooser = chooser;
          event.data.container.append(chooser);

          // Create the cells
          for (var i=0; i<options.colors.length; i++) {
            var cell = $("
");
            cell.css({
              'width':           options.cellWidth + 'px',
              'height':          options.cellHeight + 'px',
              'margin':          options.cellMargin + 'px',
              'cursor':          'pointer',
              'lineHeight':      options.cellHeight + 'px',
              'fontSize':        '1px',
              'float':           'left',
              'backgroundColor': '#'+options.colors[i]
            });
            chooser.append(cell);
            if (options.onCellEnter || options.livePreview) {
              cell.bind('mouseenter', function(event) {
                if (options.onCellEnter) {
                  options.onCellEnter(this.id);
                }
                if (options.livePreview) {
                  display_box.css('backgroundColor', '#' + this.id);
                  if (options.displayColorCode) {
                    display_box.text('#' + this.id);
                  }
                }
              });
            }
            cell.bind('click', {
              input: event.data.input,
              chooser: chooser,
              display_box: display_box
            },
            function(event) {
              event.data.input.value = '#' + this.id;
              $(event.data.input).change();
              event.data.display_box.css('backgroundColor', '#' + this.id);
              event.data.chooser.hide();
              event.data.display_box.show();

              // If 'displayColorCode' is turned on, display the currently selected color code as text inside the button.
              if (options.displayColorCode) {
                event.data.display_box.text('#' + this.id);
              }
              // If an onSelect callback function is defined then excecute it.
              if (options.onSelect) {
                options.onSelect(this.id);
              }

            });
          }
        }
      };

      var callback_params = {
        container: container,
        input: this,
        display_box: display_box
      };

      // Also bind the display box button to display the chooser.
      display_box.bind('click', callback_params, select_callback);

      $(this).after(container);

    };

    this.each(buildSelector);

    $('.simpleColorDisplay').each(function() {
      $(this).click(function(e){
        e.stopPropagation();
      });
    });

    return this;
  };

  /*
   * Close the given color selectors
   */
  $.fn.closeSelector = function() {
    this.each( function(index) {
      var container = $(this).parent().find('div.simpleColorContainer');
      container.find('.simpleColorChooser').hide();
      container.find('.simpleColorDisplay').show();
    });

    return this;
  };

})(jQuery);
<|endoftext|>


<commit_before>//
//
// conn.js
//
// Connection Management
//
// 2012-05-10  inital version
//             (refactored code from aere_game_app, aere_lobby_user, aere_mjgame_user)
//
// Functions:
//		isConnected(connID)		// check if a connection still exists
//		getConnObject(connID)
//		getConnections(conn || name)				// get all connections associated with a connection object or name
//		getConnCount()
//		createConnObject(type, conn_obj, from)
//		destroyConnObject(connID)
//		setSessionName(conn, name)					// link a session_token with a login name
//		unsetSessionName(conn)						// unlink a session_token with a login name
//		getSessionName(conn)

// retired Functions:
//		setConnName(connID, newName, oldName)
//		getConnName(connID)

// objects:
// 		ConnHandler(conn_module)

//-----------------------------------------

// NOTE: conn_module must implement the following public methods:
//
//       'onConnect'           (a new connection is made)
//       'onDisconnect'		(an existing connection is disconnected)
//

var l_timeoutWaitDispose = 1000;

//
// private methods
//

// data storing connection info, indexed by connID (uuid)
var l_conn = {};

// mapping from account name to session_token
var l_names = {};

var l_name = 'SR.Conn';

//
// public methods
//

// check if a connection still exists
exports.isConnected = function (connID) {
	return l_conn.hasOwnProperty(connID);
};

// get connection object
exports.getConnObject = function (connID) {
	if (l_conn.hasOwnProperty(connID))
		return l_conn[connID];
	else
		return undefined;
};

// get all connections with the same session_token
exports.getConnections = function (conn) {
	var list = [];
	var token = undefined;

	if (!conn) {
		LOG.warn('no connection object or name specified', l_name);
		return list;
	}

	// if connection name's provided, lookup session token directly
	if (typeof conn === 'string') {
		if (l_names.hasOwnProperty(conn) === false) {
			LOG.warn('no connection object associated with name: ' + conn, l_name);
			return list;
		}

		token = l_names[conn];
	} else if (typeof conn === 'object' && typeof conn.session_token === 'string')
		token = conn.session_token;

	if (token) {
		// get a list of all connections with the same session token
		for (var connID in l_conn) {
			if (l_conn[connID].session_token === token)
				list.push(l_conn[connID]);
		}
	}
	LOG.sys('returning ' + list.length + ' connections from same session', l_name);

	return list;
};

// get a connection name from connection ID
//exports.getConnName = function (connID) {

//	var name = (l_conn.hasOwnProperty(connID) ? l_conn[connID].name : undefined);
//    return name;
//}

// set user account given a connection ID
//exports.setConnName = function (connID, newName, oldName) {

//    // first check if connection exists
//    if (l_conn.hasOwnProperty(connID) === false)
//        return false;

//    // if old account is provided, check if it matches with the one on record
//    if (oldName && l_conn[connID].name !== oldName)
//        return false;

//	var conn = l_conn[connID];

//    // change is successful
//    conn.name = newName;

//    return true;
//}

// set user's session_token to a name so we may lookup all connections associated with a given session
// this is useful when the incoming request may not have a persistent connection (such as HTTP)
exports.setSessionName = function (conn, name) {

	if (!conn || typeof conn.session_token === 'undefined')
		return false;

	l_names[name] = conn.session_token;
	return true;
};

// opposite of setSessionName
exports.unsetSessionName = function (conn) {

	if (!conn || typeof conn.session_token === 'undefined')
		return false;

	for (var name in l_names) {
		if (l_names[name] === conn.session_token)
			delete l_names[name];
	}

	return true;
};

// get the name for current session
exports.getSessionName = function (conn) {
	for (var name in l_names) {
		if (l_name[name] === conn.session_token)
			return name;
	}
	return undefined;
};

// get number of connections currently registered
exports.getConnCount = function () {
	return Object.keys(l_conn).length;
};

// definition for a connection object
function Connection (type, sender, from) {

	// provide default 'from' values
	from = from || {};

	// connection-specific UUID
	this.connID = UTIL.createUUID();

	// a project-specific / supplied name (can be account or app name)
	this.name = '';

	// sender function associated with this connection
	this.connector = sender;

	// store pointers on how to respond
	this.type = type;

	// where the connection comes from (default to nothing)
	this.host = from.host || '';
	this.port = from.port || 0;

	// time when this connection is established
	this.time =	new Date();

	// append cookie, if available (this is useful if the event will be relayed to another server to execute,
	// cookie can then be preserved
	if (from.cookie)
		this.cookie = from.cookie;
}

// for sockets only currently
// TODO: make it more general?
Connection.prototype.send = function (msg) {

	if (this.type === 'socket') {
		LOG.sys('socket send direct triggered...', l_name);
		this.connector.send(msg);
	}
};

Connection.prototype.close = function () {

	// TODO: should remove socket-specific tasks, or try to make it more general
	if (this.type === 'socket') {
		LOG.warn('socket close direct triggered...', l_name);
		this.connector.end();
	}
};

// get duration of the connection since its start
Connection.prototype.getDuration = function () {

	return (new Date).getTime() - this.time.getTime();
};

// create a connection object
var l_createConnObject = exports.createConnObject = function (type, sender_func, from) {

	// create info for the connection & create unique UUID
	var conn = new Connection (type, sender_func, from);

	// perform socket-specific functions ('socket' or 'sockio', 'sockjs')
	// TODO: find a better way to identify persistent connections
	if (type.startsWith('sock')) {

		// record the connection (so it can later be retrived based on connID, or be removed when disconnect)
		l_conn[conn.connID] = conn;
	}

	LOG.sys('[' + conn.type + '] connection created: ' + conn.connID + ' (' + conn.host + ':' + conn.port + ')', l_name);

	return conn;
};

// remove a connection object
var l_destroyConnObject = exports.destroyConnObject = function (connID) {

	if (l_conn.hasOwnProperty(connID)) {

		LOG.sys('connection destroyed: ' + connID + ' type: ' + l_conn[connID].type, l_name);
		delete l_conn[connID];

		// remove session data for this connection
		// NOTE: this is not symmetric (storing session should be here as well)
		if (SR.State.get(connID) !== undefined) {
			LOG.warn('removing session info for connID [' + connID + ']', l_name);
			SR.State.delete(connID);
		}

		return true;
	}
	return false;
};

// dummy connection module
var l_conn_module = {
	onConnect: function () {
	},
	onDisconnect: function () {
	}
};

exports.ConnHandler = function (conn_module) {

	// use one or the default dummy
	conn_module = conn_module || l_conn_module;

	// private record for all connections at this handler
	var connections = {};

	// add a new handler for connection/disconnection
	// TODO: simplify/remove this?
	this.addConnHandler = function (handler) {

		var old_module = conn_module;

		// replace with new one
		conn_module = {

			onConnect: function (conn) {
				UTIL.safeCall(handler.onConnect, conn);

				// call original
				old_module.onConnect(conn);
			},

			onDisconnect: function (conn, onDone) {
				UTIL.safeCall(handler.onDisconnect, conn);

				// call original
				old_module.onDisconnect(conn, onDone);
			}
		};
	};

	// handle new connection
	this.addConnection = function (send_func, type, from) {

		var conn = l_createConnObject(type, send_func, from);

		// record the connection info
		connections[conn.connID] = true;

		// notify project-specific app
		UTIL.safeCall(conn_module.onConnect, conn);

		return conn;
	};

	//
	// handle when a disconnection occurs, does the following:
	// 1. checks if there are pending messages at the socket to be sent,
	// 2. call custom onDisconnect()
	// 3. destroy the connection object on record
	//
	this.removeConnection = function (socket, onDone) {

		if (socket === undefined) {
			LOG.error('socket undefined', l_name);
			return UTIL.safeCall(onDone);
		}

		// close socket (may cause additional events be fired?)
		if (typeof socket.end === 'function')
			socket.end();

		// if connection ID doesn't exist
		if (socket.hasOwnProperty('connID') === false) {
			LOG.error('connID not found for this socket', l_name);
			return UTIL.safeCall(onDone);
		}

		// wait for all event processing done on this socket
		SR.EventManager.waitSocketsEmpty(socket,
			function () {

				LOG.sys('no pending events on socket: ' + socket.connID, l_name);

				// if connection record doesn't exist
				if (l_conn.hasOwnProperty(socket.connID) === false) {
					LOG.error('connID not found: ' + socket.connID, l_name);
					return UTIL.safeCall(onDone);
				}

				LOG.sys('calling custom onDisconnect...', l_name);

				// NOTE: we make a copy of the conn object parameters to pass to user-implemented onDisconnect
				// TODO: remove this?
				var conn = {};
				var original = l_conn[socket.connID];
				for (var key in original) {
					if (typeof original[key] !== 'function')
						conn[key] = original[key];
				}

				var callback = function () {
					LOG.warn('no need to call onDone callback in onDisconnect() now, please remove it', l_name);
				};

				// TODO: remove the usage of custom onDisconnect
				UTIL.safeCall(conn_module.onDisconnect, conn, callback);

				// delete conn info
				l_destroyConnObject(socket.connID);
				delete connections[socket.connID];

				UTIL.safeCall(onDone);
			}
		);
	};

	// close down all connections
	this.removeAll = function (onDone) {

		// NOTE: log may be closed b now
		// if nothing to delete
		if (Object.keys(connections).length === 0) {

			return UTIL.safeCall(onDone);
		}

		var pending_count = 0;
		// loop through each connection to disconnect
		for (var connID in connections) {
			LOG.sys('removing connID: ' + connID, l_name);
			if (l_conn[connID].type.startsWith('sock')) {
				pending_count++;
				this.removeConnection(l_conn[connID].connector, function () {
					pending_count--;
					if (pending_count === 0)
						UTIL.safeCall(onDone);
				});
			}
			// TODO: remove other types of connections?
		}
	};
};
<commit_msg>fix: change if connection obj is valid<commit_after>//
//
// conn.js
//
// Connection Management
//
// 2012-05-10  inital version
//             (refactored code from aere_game_app, aere_lobby_user, aere_mjgame_user)
//
// Functions:
//		isConnected(connID)		// check if a connection still exists
//		getConnObject(connID)
//		getConnections(conn || name)				// get all connections associated with a connection object or name
//		getConnCount()
//		createConnObject(type, conn_obj, from)
//		destroyConnObject(connID)
//		setSessionName(conn, name)					// link a session_token with a login name
//		unsetSessionName(conn)						// unlink a session_token with a login name
//		getSessionName(conn)

// retired Functions:
//		setConnName(connID, newName, oldName)
//		getConnName(connID)

// objects:
// 		ConnHandler(conn_module)

//-----------------------------------------

// NOTE: conn_module must implement the following public methods:
//
//       'onConnect'           (a new connection is made)
//       'onDisconnect'		(an existing connection is disconnected)
//

var l_timeoutWaitDispose = 1000;

//
// private methods
//

// data storing connection info, indexed by connID (uuid)
var l_conn = {};

// mapping from account name to session_token
var l_names = {};

var l_name = 'SR.Conn';

//
// public methods
//

// check if a connection still exists
exports.isConnected = function (connID) {
	return l_conn.hasOwnProperty(connID);
};

// get connection object
exports.getConnObject = function (connID) {
	if (l_conn.hasOwnProperty(connID))
		return l_conn[connID];
	else
		return undefined;
};

// get all connections with the same session_token
exports.getConnections = function (conn) {
	var list = [];
	var token = undefined;

	if (!conn) {
		LOG.warn('no connection object or name specified', l_name);
		return list;
	}

	// if connection name's provided, lookup session token directly
	if (typeof conn === 'string') {
		if (l_names.hasOwnProperty(conn) === false) {
			LOG.warn('no connection object associated with name: ' + conn, l_name);
			return list;
		}

		token = l_names[conn];
	} else if (typeof conn === 'object' && typeof conn.session_token === 'string')
		token = conn.session_token;

	if (token) {
		// get a list of all connections with the same session token
		for (var connID in l_conn) {
			if (l_conn[connID].session_token === token)
				list.push(l_conn[connID]);
		}
	}
	LOG.sys('returning ' + list.length + ' connections from same session', l_name);

	return list;
};

// get a connection name from connection ID
//exports.getConnName = function (connID) {

//	var name = (l_conn.hasOwnProperty(connID) ? l_conn[connID].name : undefined);
//    return name;
//}

// set user account given a connection ID
//exports.setConnName = function (connID, newName, oldName) {

//    // first check if connection exists
//    if (l_conn.hasOwnProperty(connID) === false)
//        return false;

//    // if old account is provided, check if it matches with the one on record
//    if (oldName && l_conn[connID].name !== oldName)
//        return false;

//	var conn = l_conn[connID];

//    // change is successful
//    conn.name = newName;

//    return true;
//}

// set user's session_token to a name so we may lookup all connections associated with a given session
// this is useful when the incoming request may not have a persistent connection (such as HTTP)
exports.setSessionName = function (conn, name) {

	if (!conn || typeof conn.session_token === 'undefined')
		return false;

	l_names[name] = conn.session_token;
	return true;
};

// opposite of setSessionName
exports.unsetSessionName = function (conn) {

	if (!conn || typeof conn.session_token === 'undefined')
		return false;

	for (var name in l_names) {
		if (l_names[name] === conn.session_token)
			delete l_names[name];
	}

	return true;
};

// get the name for current session
exports.getSessionName = function (conn) {
	for (var name in l_names) {
		if (l_name[name] === conn.session_token)
			return name;
	}
	return undefined;
};

// get number of connections currently registered
exports.getConnCount = function () {
	return Object.keys(l_conn).length;
};

// definition for a connection object
function Connection (type, sender, from) {

	// provide default 'from' values
	from = from || {};

	// connection-specific UUID
	this.connID = UTIL.createUUID();

	// a project-specific / supplied name (can be account or app name)
	this.name = '';

	// sender function associated with this connection
	this.connector = sender;

	// store pointers on how to respond
	this.type = type;

	// where the connection comes from (default to nothing)
	this.host = from.host || '';
	this.port = from.port || 0;

	// time when this connection is established
	this.time =	new Date();

	// append cookie, if available (this is useful if the event will be relayed to another server to execute,
	// cookie can then be preserved
	if (from.cookie)
		this.cookie = from.cookie;
}

// for sockets only currently
// TODO: make it more general?
Connection.prototype.send = function (msg) {

	if (this.type === 'socket') {
		LOG.sys('socket send direct triggered...', l_name);
		this.connector.send(msg);
	}
};

Connection.prototype.close = function () {

	// TODO: should remove socket-specific tasks, or try to make it more general
	if (this.type === 'socket') {
		LOG.warn('socket close direct triggered...', l_name);
		this.connector.end();
	}
};

// get duration of the connection since its start
Connection.prototype.getDuration = function () {

	return (new Date).getTime() - this.time.getTime();
};

// create a connection object
var l_createConnObject = exports.createConnObject = function (type, sender_func, from) {

	// create info for the connection & create unique UUID
	var conn = new Connection (type, sender_func, from);

	// perform socket-specific functions ('socket' or 'sockio', 'sockjs')
	// TODO: find a better way to identify persistent connections
	if (type.startsWith('sock')) {

		// record the connection (so it can later be retrived based on connID, or be removed when disconnect)
		l_conn[conn.connID] = conn;
	}

	LOG.sys('[' + conn.type + '] connection created: ' + conn.connID + ' (' + conn.host + ':' + conn.port + ')', l_name);

	return conn;
};

// remove a connection object
var l_destroyConnObject = exports.destroyConnObject = function (connID) {

	if (l_conn.hasOwnProperty(connID)) {

		LOG.sys('connection destroyed: ' + connID + ' type: ' + l_conn[connID].type, l_name);
		delete l_conn[connID];

		// remove session data for this connection
		// NOTE: this is not symmetric (storing session should be here as well)
		if (SR.State.get(connID) !== undefined) {
			LOG.warn('removing session info for connID [' + connID + ']', l_name);
			SR.State.delete(connID);
		}

		return true;
	}
	return false;
};

// dummy connection module
var l_conn_module = {
	onConnect: function () {
	},
	onDisconnect: function () {
	}
};

exports.ConnHandler = function (conn_module) {

	// use one or the default dummy
	conn_module = conn_module || l_conn_module;

	// private record for all connections at this handler
	var connections = {};

	// add a new handler for connection/disconnection
	// TODO: simplify/remove this?
	this.addConnHandler = function (handler) {

		var old_module = conn_module;

		// replace with new one
		conn_module = {

			onConnect: function (conn) {
				UTIL.safeCall(handler.onConnect, conn);

				// call original
				old_module.onConnect(conn);
			},

			onDisconnect: function (conn, onDone) {
				UTIL.safeCall(handler.onDisconnect, conn);

				// call original
				old_module.onDisconnect(conn, onDone);
			}
		};
	};

	// handle new connection
	this.addConnection = function (send_func, type, from) {

		var conn = l_createConnObject(type, send_func, from);

		// record the connection info
		connections[conn.connID] = true;

		// notify project-specific app
		UTIL.safeCall(conn_module.onConnect, conn);

		return conn;
	};

	//
	// handle when a disconnection occurs, does the following:
	// 1. checks if there are pending messages at the socket to be sent,
	// 2. call custom onDisconnect()
	// 3. destroy the connection object on record
	//
	this.removeConnection = function (socket, onDone) {

		if (socket === undefined) {
			LOG.error('socket undefined', l_name);
			return UTIL.safeCall(onDone);
		}

		// close socket (may cause additional events be fired?)
		if (typeof socket.end === 'function')
			socket.end();

		// if connection ID doesn't exist
		if (socket.hasOwnProperty('connID') === false) {
			LOG.error('connID not found for this socket', l_name);
			return UTIL.safeCall(onDone);
		}

		// wait for all event processing done on this socket
		SR.EventManager.waitSocketsEmpty(socket,
			function () {

				LOG.sys('no pending events on socket: ' + socket.connID, l_name);

				// if connection record doesn't exist
				if (l_conn.hasOwnProperty(socket.connID) === false) {
					LOG.error('connID not found: ' + socket.connID, l_name);
					return UTIL.safeCall(onDone);
				}

				LOG.sys('calling custom onDisconnect...', l_name);

				// NOTE: we make a copy of the conn object parameters to pass to user-implemented onDisconnect
				// TODO: remove this?
				var conn = {};
				var original = l_conn[socket.connID];
				for (var key in original) {
					if (typeof original[key] !== 'function')
						conn[key] = original[key];
				}

				var callback = function () {
					LOG.warn('no need to call onDone callback in onDisconnect() now, please remove it', l_name);
				};

				// TODO: remove the usage of custom onDisconnect
				UTIL.safeCall(conn_module.onDisconnect, conn, callback);

				// delete conn info
				l_destroyConnObject(socket.connID);
				delete connections[socket.connID];

				UTIL.safeCall(onDone);
			}
		);
	};

	// close down all connections
	this.removeAll = function (onDone) {

		// NOTE: log may be closed b now
		// if nothing to delete
		if (Object.keys(connections).length === 0) {

			return UTIL.safeCall(onDone);
		}

		var pending_count = 0;
		// loop through each connection to disconnect
		for (var connID in connections) {
			LOG.sys('removing connID: ' + connID, l_name);
			if (l_conn.hasOwnProperty(connID) && l_conn[connID].type.startsWith('sock')) {
				pending_count++;
				this.removeConnection(l_conn[connID].connector, function () {
					pending_count--;
					if (pending_count === 0)
						UTIL.safeCall(onDone);
				});
			}
			// TODO: remove other types of connections?
		}
	};
};
<|endoftext|>