<commit_before>/*
 * Copyright (c) 2014 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

define(function (require, exports, module) {
    "use strict";

    var Fluxxor = require("fluxxor"),
        Immutable = require("immutable"),
        _ = require("lodash");

    var events = require("../events"),
        collection = require("js/util/collection"),
        log = require("js/util/log");

    var DialogStore = Fluxxor.createStore({
        /**
         * The set of open Dialog IDs
         *
         * @private
         * @type {Immutable.Set.<string>}
         */
        _openDialogs: Immutable.Set(),

        /**
         * Information/state for all registered dialogs
         *
         * @private
         * @type {Immutable.Map.<string, {policy: object, documentID: number, selectionType: string}>}
         */
        _registeredDialogs: Immutable.Map(),

        initialize: function () {
            this.bindActions(
                events.dialog.REGISTER_DIALOG, this._handleRegister,
                events.dialog.DEREGISTER_DIALOG, this._handleDeregister,
                events.dialog.OPEN_DIALOG, this._handleOpen,
                events.dialog.CLOSE_DIALOG, this._handleClose,
                events.dialog.CLOSE_ALL_DIALOGS, this._handleCloseAll,
                events.document.SELECT_LAYERS_BY_ID, this._handleSelectionChange,
                events.document.SELECT_LAYERS_BY_INDEX, this._handleSelectionChange,
                events.document.GROUP_SELECTED, this._handleSelectionChange,
                events.document.CLOSE_DOCUMENT, this._handleDocumentChange,
                events.document.SELECT_DOCUMENT, this._handleDocumentChange
            );
        },

        getState: function () {
            return {
                openDialogs: this._openDialogs
            };
        },

        /**
         * Fetch the current document ID, or null if there is no current document.
         *
         * @private
         * @return {?number}
         */
        _getCurrentDocumentID: function () {
            var applicationStore = this.flux.store("application");

            return applicationStore.getCurrentDocumentID();
        },

        /**
         * Fetch the type of the current layer selection, or none if there is no
         * consistent type.
         *
         * @private
         * @return {string}
         */
        _getCurrentSelectionType: function () {
            var applicationStore = this.flux.store("application"),
                currentDocument = applicationStore.getCurrentDocument();

            if (!currentDocument) {
                return null;
            }

            var kinds = collection.pluck(currentDocument.layers.selected, "kind");

            return collection.uniformValue(kinds);
        },

        /**
         * Handle a registerDialog event
         * (does not emit a change event)
         * merges state if this event has already been registered
         *
         * @private
         * @param {{id: string, dismissalPolicy: object}} payload
         */
        _handleRegister: function (payload) {
            var id = payload.id,
                state = {
                    policy: payload.dismissalPolicy
                };

            this._registeredDialogs = this._registeredDialogs.update(id, {}, function (val) {
                return _.merge(_.clone(val), state);
            });
        },

        /**
         * Handle a deregisterDialog event
         * emits a change event if this dialog was open
         *
         * @private
         * @param {{id: string, dismissalPolicy: object}} payload
         */
        _handleDeregister: function (payload) {
            var id = payload.id;
            this._registeredDialogs = this._registeredDialogs.delete(id);
            if (this._openDialogs.has(id)) {
                this._openDialogs = this._openDialogs.delete(id);
                this.emit("change");
            }
        },

        /**
         * Handle an open-dialog event.
         * If this dialog has not been pre-registered, it must contain a dismissal policy in the payload
         * Otherwise, the optional dismissal policy will overwrite the previous
         *
         * @private
         * @param {{id: string, dismissalPolicy: object=}} payload
         */
        _handleOpen: function (payload) {
            var id = payload.id,
                newState;

            // register on the fly, IFF a dismissal policy was provided
            if (!this._registeredDialogs.has(id)) {
                if (_.isEmpty(payload.dismissalPolicy)) {
                    log.warn("Opening an un-registered dialog without providing a dismissalPolicy");
                }
                newState = {
                    policy: payload.dismissalPolicy || {}
                };
            } else {
                // merge the new policy, if it was provided
                newState = _.merge(this._registeredDialogs.get(id), {policy: payload.dismissalPolicy});
            }

            if (newState.policy.documentChange) {
                newState.documentID = this._getCurrentDocumentID();
            }

            if (newState.policy.selectionTypeChange) {
                newState.selectionType = this._getCurrentSelectionType();
            }

            // update the registry
            this._registeredDialogs = this._registeredDialogs.set(id, newState);

            // Close dialogs with the "openDialog" dismissal policy
            // and add this new one
            this._openDialogs = this._openDialogs
                .filterNot(function (dialogID) {
                    return this._registeredDialogs.get(dialogID).policy.dialogOpen;
                }, this)
                .add(id);

            this.emit("change");
        },

        /**
         * Handle a close-dialog event.
         *
         * @private
         * @param {{id: number}} payload
         */
        _handleClose: function (payload) {
            this._openDialogs = this._openDialogs.delete(payload.id);
            this.emit("change");
        },

        /**
         * Handle a close-all-dialogs event.
         *
         * @private
         */
        _handleCloseAll: function () {
            this._openDialogs = this._openDialogs.clear();
            this.emit("change");
        },

        /**
         * Close all dialogs with a documentChange dismissal policy.
         *
         * @private
         */
        _handleDocumentChange: function () {
            this.waitFor(["document", "application"], function () {
                this._openDialogs = this._openDialogs.filterNot(function (dialogID) {
                    var state = this._registeredDialogs.get(dialogID);
                    return state.policy.documentChange && state.documentID !== this._getCurrentDocumentID();
                }, this);

                this.emit("change");
            });
        },

        /**
         * Close all dialogs with a selectionTypeChange dismissal policy.
         *
         * @private
         */
        _handleSelectionChange: function () {
            this.waitFor(["document"], function () {
                this._openDialogs = this._openDialogs.filterNot(function (dialogID) {
                    var state = this._registeredDialogs.get(dialogID);
                    return state.policy.selectionTypeChange && state.selectionType !== this._getCurrentSelectionType();
                }, this);

                this.emit("change");
            });
        }
    });

    module.exports = DialogStore;
});
<commit_msg>Converting the internal dialog store structures to be immutable, deeply<commit_after>/*
 * Copyright (c) 2014 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

define(function (require, exports, module) {
    "use strict";

    var Fluxxor = require("fluxxor"),
        Immutable = require("immutable"),
        _ = require("lodash");

    var events = require("../events"),
        collection = require("js/util/collection"),
        log = require("js/util/log");

    var DialogStore = Fluxxor.createStore({
        /**
         * The set of open Dialog IDs
         *
         * @private
         * @type {Immutable.Set.<string>}
         */
        _openDialogs: Immutable.Set(),

        /**
         * Information/state for all registered dialogs
         *
         * The value of the map represents the state of the given dialog.
         * It is a map with the following optional keys:
         *     policy: Immutable.Map.<string, boolean>,
         *     documentID: number,
         *     selectionType: string

         * @private
         * @type {Immutable.Map.<string, Immutable.Map<string, object>>}
         */
        _registeredDialogs: Immutable.Map(),

        initialize: function () {
            this.bindActions(
                events.dialog.REGISTER_DIALOG, this._handleRegister,
                events.dialog.DEREGISTER_DIALOG, this._handleDeregister,
                events.dialog.OPEN_DIALOG, this._handleOpen,
                events.dialog.CLOSE_DIALOG, this._handleClose,
                events.dialog.CLOSE_ALL_DIALOGS, this._handleCloseAll,
                events.document.SELECT_LAYERS_BY_ID, this._handleSelectionChange,
                events.document.SELECT_LAYERS_BY_INDEX, this._handleSelectionChange,
                events.document.GROUP_SELECTED, this._handleSelectionChange,
                events.document.CLOSE_DOCUMENT, this._handleDocumentChange,
                events.document.SELECT_DOCUMENT, this._handleDocumentChange
            );
        },

        getState: function () {
            return {
                openDialogs: this._openDialogs
            };
        },

        /**
         * Fetch the current document ID, or null if there is no current document.
         *
         * @private
         * @return {?number}
         */
        _getCurrentDocumentID: function () {
            var applicationStore = this.flux.store("application");

            return applicationStore.getCurrentDocumentID();
        },

        /**
         * Fetch the type of the current layer selection, or none if there is no
         * consistent type.
         *
         * @private
         * @return {string}
         */
        _getCurrentSelectionType: function () {
            var applicationStore = this.flux.store("application"),
                currentDocument = applicationStore.getCurrentDocument();

            if (!currentDocument) {
                return null;
            }

            var kinds = collection.pluck(currentDocument.layers.selected, "kind");

            return collection.uniformValue(kinds);
        },

        /**
         * Handle a registerDialog event
         * (does not emit a change event)
         * merges state if this event has already been registered
         *
         * @private
         * @param {{id: string, dismissalPolicy: object}} payload
         */
        _handleRegister: function (payload) {
            var id = payload.id,
                state = {
                    policy: Immutable.Map(payload.dismissalPolicy)
                };

            this._registeredDialogs = this._registeredDialogs.update(id, Immutable.Map(), function (val) {
                return val.merge(state);
            });
        },

        /**
         * Handle a deregisterDialog event
         * emits a change event if this dialog was open
         *
         * @private
         * @param {{id: string, dismissalPolicy: object}} payload
         */
        _handleDeregister: function (payload) {
            var id = payload.id;
            this._registeredDialogs = this._registeredDialogs.delete(id);
            if (this._openDialogs.has(id)) {
                this._openDialogs = this._openDialogs.delete(id);
                this.emit("change");
            }
        },

        /**
         * Handle an open-dialog event.
         * If this dialog has not been pre-registered, it must contain a dismissal policy in the payload
         * Otherwise, the optional dismissal policy will overwrite the previous
         *
         * @private
         * @param {{id: string, dismissalPolicy: object=}} payload
         */
        _handleOpen: function (payload) {
            var id = payload.id,
                newState = Immutable.Map({
                  policy: Immutable.Map(payload.dismissalPolicy || {})
                });

            // register on the fly, but warn if a dismissal policy was not provided
            if (!this._registeredDialogs.has(id)) {
                if (_.isEmpty(payload.dismissalPolicy)) {
                    log.warn("Opening an un-registered dialog without providing a dismissalPolicy");
                }
            } else {
                // merge the new policy, if it was provided
                newState = this._registeredDialogs.get(id).merge(newState);
            }

            if (newState.getIn(["policy", "documentChange"])) {
                newState = newState.set("documentID", this._getCurrentDocumentID());
            }

            if (newState.getIn(["policy", "selectionTypeChange"])) {
                newState = newState.set("selectionType", this._getCurrentSelectionType());
            }

            // update the registry
            this._registeredDialogs = this._registeredDialogs.set(id, newState);

            // Close dialogs with the "openDialog" dismissal policy
            // and add this new one
            this._openDialogs = this._openDialogs
                .filterNot(function (dialogID) {
                    return this._registeredDialogs.getIn([dialogID, "policy", "dialogOpen"], false);
                }, this)
                .add(id);

            this.emit("change");
        },

        /**
         * Handle a close-dialog event.
         *
         * @private
         * @param {{id: number}} payload
         */
        _handleClose: function (payload) {
            this._openDialogs = this._openDialogs.delete(payload.id);
            this.emit("change");
        },

        /**
         * Handle a close-all-dialogs event.
         *
         * @private
         */
        _handleCloseAll: function () {
            this._openDialogs = this._openDialogs.clear();
            this.emit("change");
        },

        /**
         * Close all dialogs with a documentChange dismissal policy.
         *
         * @private
         */
        _handleDocumentChange: function () {
            this.waitFor(["document", "application"], function () {
                this._openDialogs = this._openDialogs.filterNot(function (dialogID) {
                    var state = this._registeredDialogs.get(dialogID);
                    return state.get("policy").get("documentChange") &&
                        state.get("documentID") !== this._getCurrentDocumentID();
                }, this);

                this.emit("change");
            });
        },

        /**
         * Close all dialogs with a selectionTypeChange dismissal policy.
         *
         * @private
         */
        _handleSelectionChange: function () {
            this.waitFor(["document"], function () {
                this._openDialogs = this._openDialogs.filterNot(function (dialogID) {
                    var state = this._registeredDialogs.get(dialogID);
                    return state.get("policy").get("selectionTypeChange") &&
                        state.get("selectionType") !== this._getCurrentSelectionType();
                }, this);

                this.emit("change");
            });
        }
    });

    module.exports = DialogStore;
});
<|endoftext|>


<commit_before>var utils = require('./utils');

module.exports = {
  isDSModel: isDSModel,
  isModule: isModule,
  isEmberComponent: isEmberComponent,
  isEmberController: isEmberController,
  // isEmberRoute: isEmberRoute,
};

// Private

function isLocalModule(callee, element) {
  return (utils.isIdentifier(callee) && callee.name === element) ||
    (utils.isIdentifier(callee.object) && callee.object.name === element);
}

function isEmberModule(callee, element, module) {
  memberExp = utils.isMemberExpression(callee.object) ? callee.object : callee;

  return isLocalModule(memberExp, module) &&
    utils.isIdentifier(memberExp.property) &&
    memberExp.property.name === element;
}

// Public

function isModule(node, element, module) {
  module = module || 'Ember';

  return utils.isCallExpression(node) &&
    (isLocalModule(node.callee, element) || isEmberModule(node.callee, element, module));
}

function isDSModel(node) {
  return isModule(node, 'Model', 'DS');
}

function isEmberComponent(node) {
  return isModule(node, 'Component');
}

function isEmberController(node) {
  return isModule(node, 'Controller');
}

// function isEmberRoute(node) {
//   return isModule(node, 'Route');
// }
<commit_msg>add helper for getting properties from module<commit_after>var utils = require('./utils');

module.exports = {
  isDSModel: isDSModel,
  isModule: isModule,
  isEmberComponent: isEmberComponent,
  isEmberController: isEmberController,
  getModuleProperties: getModuleProperties,
  // isEmberRoute: isEmberRoute,
};

// Private

function isLocalModule(callee, element) {
  return (utils.isIdentifier(callee) && callee.name === element) ||
    (utils.isIdentifier(callee.object) && callee.object.name === element);
}

function isEmberModule(callee, element, module) {
  memberExp = utils.isMemberExpression(callee.object) ? callee.object : callee;

  return isLocalModule(memberExp, module) &&
    utils.isIdentifier(memberExp.property) &&
    memberExp.property.name === element;
}

// Public

function isModule(node, element, module) {
  module = module || 'Ember';

  return utils.isCallExpression(node) &&
    (isLocalModule(node.callee, element) || isEmberModule(node.callee, element, module));
}

function isDSModel(node) {
  return isModule(node, 'Model', 'DS');
}

function isEmberComponent(node) {
  return isModule(node, 'Component');
}

function isEmberController(node) {
  return isModule(node, 'Controller');
}

function getModuleProperties(module) {
  return utils.findNodes(module.arguments, 'ObjectExpression')[0];
}

// function isEmberRoute(node) {
//   return isModule(node, 'Route');
// }
<|endoftext|>


<commit_before>define([
	'config/types',
	'config/isClient',
	'render/shared/initMustache',
	'render/shared/updateMustache',
	'render/shared/resolveMustache',
	'render/shared/updateSection',
	'render/DomFragment/Section/reassignFragment',
	'render/DomFragment/Section/reassignFragments',
	'render/DomFragment/Section/prototype/merge',
	'shared/teardown',
	'circular'
], function (
	types,
	isClient,
	initMustache,
	updateMustache,
	resolveMustache,
	updateSection,
	reassignFragment,
	reassignFragments,
	merge,
	teardown,
	circular
) {

	'use strict';

	var DomSection, DomFragment;

	circular.push( function () {
		DomFragment = circular.DomFragment;
	});

	// Section
	DomSection = function ( options, docFrag ) {
		this.type = types.SECTION;
		this.inverted = !!options.descriptor.n;

		this.fragments = [];
		this.length = 0; // number of times this section is rendered

		if ( docFrag ) {
			this.docFrag = document.createDocumentFragment();
		}

		this.initialising = true;
		initMustache( this, options );

		if ( docFrag ) {
			docFrag.appendChild( this.docFrag );
		}

		this.initialising = false;
	};

	DomSection.prototype = {
		update: updateMustache,
		resolve: resolveMustache,

		smartUpdate: function ( methodName, args ) {
			var fragmentOptions;

			if ( methodName === 'push' || methodName === 'unshift' || methodName === 'splice' ) {
				fragmentOptions = {
					descriptor: this.descriptor.f,
					root:       this.root,
					pNode:      this.pNode,
					owner:      this
				};

				if ( this.descriptor.i ) {
					fragmentOptions.indexRef = this.descriptor.i;
				}
			}

			if ( this[ methodName ] ) { // if not, it's sort or reverse, which doesn't affect us (i.e. our length)
				this[ methodName ]( fragmentOptions, args );
			}
		},

		pop: function () {
			// teardown last fragment
			if ( this.length ) {
				this.fragments.pop().teardown( true );
				this.length -= 1;
			}
		},

		push: function ( fragmentOptions, args ) {
			var start, end, i;

			// append list item to context stack
			start = this.length;
			end = start + args.length;

			for ( i=start; i<end; i+=1 ) {
				fragmentOptions.contextStack = this.contextStack.concat( this.keypath + '.' + i );
				fragmentOptions.index = i;

				this.fragments[i] = this.createFragment( fragmentOptions );
			}

			this.length += args.length;

			// append docfrag in front of next node
			this.pNode.insertBefore( this.docFrag, this.parentFragment.findNextNode( this ) );
		},

		shift: function () {
			this.splice( null, [ 0, 1 ] );
		},

		unshift: function ( fragmentOptions, args ) {
			this.splice( fragmentOptions, [ 0, 0 ].concat( new Array( args.length ) ) );
		},

		splice: function ( fragmentOptions, args ) {
			var insertionPoint, addedItems, removedItems, balance, i, start, end, spliceArgs, reassignStart;

			if ( !args.length ) {
				return;
			}

			// figure out where the changes started...
			start = +( args[0] < 0 ? this.length + args[0] : args[0] );

			// ...and how many items were added to or removed from the array
			addedItems = Math.max( 0, args.length - 2 );
			removedItems = ( args[1] !== undefined ? args[1] : this.length - start );

			// It's possible to do e.g. [ 1, 2, 3 ].splice( 2, 2 ) - i.e. the second argument
			// means removing more items from the end of the array than there are. In these
			// cases we need to curb JavaScript's enthusiasm or we'll get out of sync
			removedItems = Math.min( removedItems, this.length - start );

			balance = addedItems - removedItems;

			if ( !balance ) {
				// The array length hasn't changed - we don't need to add or remove anything
				return;
			}

			// If more items were removed than added, we need to remove some things from the DOM
			if ( balance < 0 ) {
				end = start - balance;

				for ( i=start; i<end; i+=1 ) {
					this.fragments[i].teardown( true );
				}

				// Keep in sync
				this.fragments.splice( start, -balance );
			}

			// Otherwise we need to add some things to the DOM
			else {
				end = start + balance;

				// Figure out where these new nodes need to be inserted
				insertionPoint = ( this.fragments[ start ] ? this.fragments[ start ].firstNode() : this.parentFragment.findNextNode( this ) );

				// Make room for the new fragments. (Just trust me, this works...)
				spliceArgs = [ start, 0 ].concat( new Array( balance ) );
				this.fragments.splice.apply( this.fragments, spliceArgs );

				for ( i=start; i<end; i+=1 ) {
					fragmentOptions.contextStack = this.contextStack.concat( this.keypath + '.' + i );
					fragmentOptions.index = i;

					this.fragments[i] = this.createFragment( fragmentOptions );
				}

				// Append docfrag in front of insertion point
				this.pNode.insertBefore( this.docFrag, insertionPoint );
			}

			this.length += balance;

			// Now we need to reassign existing fragments (e.g. items.4 -> items.3 - the keypaths,
			// context stacks and index refs will have changed)
			reassignStart = ( start + addedItems );

			reassignFragments( this.root, this, reassignStart, this.length, balance );
		},

		merge: merge,

		detach: function () {
			var i, len;

			len = this.fragments.length;
			for ( i = 0; i < len; i += 1 ) {
				this.docFrag.appendChild( this.fragments[i].detach() );
			}

			return this.docFrag;
		},

		teardown: function ( destroy ) {
			this.teardownFragments( destroy );

			teardown( this );
		},

		firstNode: function () {
			if ( this.fragments[0] ) {
				return this.fragments[0].firstNode();
			}

			return this.parentFragment.findNextNode( this );
		},

		findNextNode: function ( fragment ) {
			if ( this.fragments[ fragment.index + 1 ] ) {
				return this.fragments[ fragment.index + 1 ].firstNode();
			}

			return this.parentFragment.findNextNode( this );
		},

		teardownFragments: function ( destroy ) {
			var id;

			while ( this.fragments.length ) {
				this.fragments.shift().teardown( destroy );
			}

			if ( this.fragmentsById ) {
				for ( id in this.fragmentsById ) {
					if ( this.fragments[ id ] ) {
						this.fragmentsById[ id ].teardown( destroy );
						this.fragmentsById[ id ] = null;
					}
				}
			}
		},

		render: function ( value ) {
			var nextNode, wrapped;

			// with sections, we need to get the fake value if we have a wrapped object
			if ( wrapped = this.root._wrapped[ this.keypath ] ) {
				value = wrapped.get();
			}

			// prevent sections from rendering multiple times (happens if
			// evaluators evaluate while update is happening)
			if ( this.rendering ) {
				return;
			}

			this.rendering = true;
			updateSection( this, value );
			this.rendering = false;

			// if we have no new nodes to insert (i.e. the section length stayed the
			// same, or shrank), we don't need to go any further
			if ( this.docFrag && !this.docFrag.childNodes.length ) {
				return;
			}

			// if this isn't the initial render, we need to insert any new nodes in
			// the right place
			if ( !this.initialising && isClient ) {

				// Normally this is just a case of finding the next node, and inserting
				// items before it...
				nextNode = this.parentFragment.findNextNode( this );

				if ( nextNode && ( nextNode.parentNode === this.pNode ) ) {
					this.pNode.insertBefore( this.docFrag, nextNode );
				}

				// ...but in some edge cases the next node will not have been attached to
				// the DOM yet, in which case we append to the end of the parent node
				else {
					// TODO could there be a situation in which later nodes could have
					// been attached to the parent node, i.e. we need to find a sibling
					// to insert before?
					this.pNode.appendChild( this.docFrag );
				}
			}
		},

		createFragment: function ( options ) {
			var fragment = new DomFragment( options );

			if ( this.docFrag ) {
				this.docFrag.appendChild( fragment.docFrag );
			}

			return fragment;
		},

		toString: function () {
			var str, i, id, len;

			str = '';

			i = 0;
			len = this.length;

			for ( i=0; i<len; i+=1 ) {
				str += this.fragments[i].toString();
			}

			if ( this.fragmentsById ) {
				for ( id in this.fragmentsById ) {
					if ( this.fragmentsById[ id ] ) {
						str += this.fragmentsById[ id ].toString();
					}
				}
			}

			return str;
		},

		find: function ( selector ) {
			var i, len, queryResult;

			len = this.fragments.length;
			for ( i = 0; i < len; i += 1 ) {
				if ( queryResult = this.fragments[i].find( selector ) ) {
					return queryResult;
				}
			}

			return null;
		},

		findAll: function ( selector, queryResult ) {
			var i, len;

			len = this.fragments.length;
			for ( i = 0; i < len; i += 1 ) {
				this.fragments[i].findAll( selector, queryResult );
			}
		}
	};

	return DomSection;

});<commit_msg>fixes #339 - short-circuit double section renders that start with a smart update<commit_after>define([
	'config/types',
	'config/isClient',
	'render/shared/initMustache',
	'render/shared/updateMustache',
	'render/shared/resolveMustache',
	'render/shared/updateSection',
	'render/DomFragment/Section/reassignFragment',
	'render/DomFragment/Section/reassignFragments',
	'render/DomFragment/Section/prototype/merge',
	'shared/teardown',
	'circular'
], function (
	types,
	isClient,
	initMustache,
	updateMustache,
	resolveMustache,
	updateSection,
	reassignFragment,
	reassignFragments,
	merge,
	teardown,
	circular
) {

	'use strict';

	var DomSection, DomFragment;

	circular.push( function () {
		DomFragment = circular.DomFragment;
	});

	// Section
	DomSection = function ( options, docFrag ) {
		this.type = types.SECTION;
		this.inverted = !!options.descriptor.n;

		this.fragments = [];
		this.length = 0; // number of times this section is rendered

		if ( docFrag ) {
			this.docFrag = document.createDocumentFragment();
		}

		this.initialising = true;
		initMustache( this, options );

		if ( docFrag ) {
			docFrag.appendChild( this.docFrag );
		}

		this.initialising = false;
	};

	DomSection.prototype = {
		update: updateMustache,
		resolve: resolveMustache,

		smartUpdate: function ( methodName, args ) {
			var fragmentOptions;

			if ( methodName === 'push' || methodName === 'unshift' || methodName === 'splice' ) {
				fragmentOptions = {
					descriptor: this.descriptor.f,
					root:       this.root,
					pNode:      this.pNode,
					owner:      this
				};

				if ( this.descriptor.i ) {
					fragmentOptions.indexRef = this.descriptor.i;
				}
			}

			if ( this[ methodName ] ) { // if not, it's sort or reverse, which doesn't affect us (i.e. our length)
				this.rendering = true;
				this[ methodName ]( fragmentOptions, args );
				this.rendering = false;
			}
		},

		pop: function () {
			// teardown last fragment
			if ( this.length ) {
				this.fragments.pop().teardown( true );
				this.length -= 1;
			}
		},

		push: function ( fragmentOptions, args ) {
			var start, end, i;

			// append list item to context stack
			start = this.length;
			end = start + args.length;

			for ( i=start; i<end; i+=1 ) {
				fragmentOptions.contextStack = this.contextStack.concat( this.keypath + '.' + i );
				fragmentOptions.index = i;

				this.fragments[i] = this.createFragment( fragmentOptions );
			}

			this.length += args.length;

			// append docfrag in front of next node
			this.pNode.insertBefore( this.docFrag, this.parentFragment.findNextNode( this ) );
		},

		shift: function () {
			this.splice( null, [ 0, 1 ] );
		},

		unshift: function ( fragmentOptions, args ) {
			this.splice( fragmentOptions, [ 0, 0 ].concat( new Array( args.length ) ) );
		},

		splice: function ( fragmentOptions, args ) {
			var insertionPoint, addedItems, removedItems, balance, i, start, end, spliceArgs, reassignStart;

			if ( !args.length ) {
				return;
			}

			// figure out where the changes started...
			start = +( args[0] < 0 ? this.length + args[0] : args[0] );

			// ...and how many items were added to or removed from the array
			addedItems = Math.max( 0, args.length - 2 );
			removedItems = ( args[1] !== undefined ? args[1] : this.length - start );

			// It's possible to do e.g. [ 1, 2, 3 ].splice( 2, 2 ) - i.e. the second argument
			// means removing more items from the end of the array than there are. In these
			// cases we need to curb JavaScript's enthusiasm or we'll get out of sync
			removedItems = Math.min( removedItems, this.length - start );

			balance = addedItems - removedItems;

			if ( !balance ) {
				// The array length hasn't changed - we don't need to add or remove anything
				return;
			}

			// If more items were removed than added, we need to remove some things from the DOM
			if ( balance < 0 ) {
				end = start - balance;

				for ( i=start; i<end; i+=1 ) {
					this.fragments[i].teardown( true );
				}

				// Keep in sync
				this.fragments.splice( start, -balance );
			}

			// Otherwise we need to add some things to the DOM
			else {
				end = start + balance;

				// Figure out where these new nodes need to be inserted
				insertionPoint = ( this.fragments[ start ] ? this.fragments[ start ].firstNode() : this.parentFragment.findNextNode( this ) );

				// Make room for the new fragments. (Just trust me, this works...)
				spliceArgs = [ start, 0 ].concat( new Array( balance ) );
				this.fragments.splice.apply( this.fragments, spliceArgs );

				for ( i=start; i<end; i+=1 ) {
					fragmentOptions.contextStack = this.contextStack.concat( this.keypath + '.' + i );
					fragmentOptions.index = i;

					this.fragments[i] = this.createFragment( fragmentOptions );
				}

				// Append docfrag in front of insertion point
				this.pNode.insertBefore( this.docFrag, insertionPoint );
			}

			this.length += balance;

			// Now we need to reassign existing fragments (e.g. items.4 -> items.3 - the keypaths,
			// context stacks and index refs will have changed)
			reassignStart = ( start + addedItems );

			reassignFragments( this.root, this, reassignStart, this.length, balance );
		},

		merge: merge,

		detach: function () {
			var i, len;

			len = this.fragments.length;
			for ( i = 0; i < len; i += 1 ) {
				this.docFrag.appendChild( this.fragments[i].detach() );
			}

			return this.docFrag;
		},

		teardown: function ( destroy ) {
			this.teardownFragments( destroy );

			teardown( this );
		},

		firstNode: function () {
			if ( this.fragments[0] ) {
				return this.fragments[0].firstNode();
			}

			return this.parentFragment.findNextNode( this );
		},

		findNextNode: function ( fragment ) {
			if ( this.fragments[ fragment.index + 1 ] ) {
				return this.fragments[ fragment.index + 1 ].firstNode();
			}

			return this.parentFragment.findNextNode( this );
		},

		teardownFragments: function ( destroy ) {
			var id;

			while ( this.fragments.length ) {
				this.fragments.shift().teardown( destroy );
			}

			if ( this.fragmentsById ) {
				for ( id in this.fragmentsById ) {
					if ( this.fragments[ id ] ) {
						this.fragmentsById[ id ].teardown( destroy );
						this.fragmentsById[ id ] = null;
					}
				}
			}
		},

		render: function ( value ) {
			var nextNode, wrapped;

			// with sections, we need to get the fake value if we have a wrapped object
			if ( wrapped = this.root._wrapped[ this.keypath ] ) {
				value = wrapped.get();
			}

			// prevent sections from rendering multiple times (happens if
			// evaluators evaluate while update is happening)
			if ( this.rendering ) {
				return;
			}

			this.rendering = true;
			updateSection( this, value );
			this.rendering = false;

			// if we have no new nodes to insert (i.e. the section length stayed the
			// same, or shrank), we don't need to go any further
			if ( this.docFrag && !this.docFrag.childNodes.length ) {
				return;
			}

			// if this isn't the initial render, we need to insert any new nodes in
			// the right place
			if ( !this.initialising && isClient ) {

				// Normally this is just a case of finding the next node, and inserting
				// items before it...
				nextNode = this.parentFragment.findNextNode( this );

				if ( nextNode && ( nextNode.parentNode === this.pNode ) ) {
					this.pNode.insertBefore( this.docFrag, nextNode );
				}

				// ...but in some edge cases the next node will not have been attached to
				// the DOM yet, in which case we append to the end of the parent node
				else {
					// TODO could there be a situation in which later nodes could have
					// been attached to the parent node, i.e. we need to find a sibling
					// to insert before?
					this.pNode.appendChild( this.docFrag );
				}
			}
		},

		createFragment: function ( options ) {
			var fragment = new DomFragment( options );

			if ( this.docFrag ) {
				this.docFrag.appendChild( fragment.docFrag );
			}

			return fragment;
		},

		toString: function () {
			var str, i, id, len;

			str = '';

			i = 0;
			len = this.length;

			for ( i=0; i<len; i+=1 ) {
				str += this.fragments[i].toString();
			}

			if ( this.fragmentsById ) {
				for ( id in this.fragmentsById ) {
					if ( this.fragmentsById[ id ] ) {
						str += this.fragmentsById[ id ].toString();
					}
				}
			}

			return str;
		},

		find: function ( selector ) {
			var i, len, queryResult;

			len = this.fragments.length;
			for ( i = 0; i < len; i += 1 ) {
				if ( queryResult = this.fragments[i].find( selector ) ) {
					return queryResult;
				}
			}

			return null;
		},

		findAll: function ( selector, queryResult ) {
			var i, len;

			len = this.fragments.length;
			for ( i = 0; i < len; i += 1 ) {
				this.fragments[i].findAll( selector, queryResult );
			}
		}
	};

	return DomSection;

});<|endoftext|>


<commit_before>for (var conrad = 0; conrad == -1; conrad++) {
  document.writeln("Conrad is frickin aweome! Yeah, I said aweome!");
}
<commit_msg>Update conrad.js<commit_after>var never = -1;
var endTheAwesomeness = never;

for (var conrad = 0; conrad == endTheAwesomeness; conrad++) {
  document.writeln("Conrad is frickin aweome! Yeah, I said aweome!");
}
<|endoftext|>


<commit_before>FaultedEarth = Ext.extend(gxp.Viewer, {
    constructor: function(config) {

        function adjustIframeSize(cmp) {
            cmp.body.setWidth(cmp.ownerCt.getWidth());
        }

        Ext.applyIf(config, {
            proxy: "/proxy?url=",

            mapItems: [{
                xtype: "gx_zoomslider",
                vertical: true,
                height: 100
            }],
            portalItems: [{
                region: "center",
                layout: "border",
                tbar: {
                    id: "paneltbar",
                    items: ["-"]
                },
                items: [{
                    id: "west",
                    region: "west",
                    layout: "accordion",
                    width: 280,
                    split: true,
                    collapsible: true,
                    collapseMode: "mini",
                    header: false,
                    border: false,
                    defaults: {
                       padding: 10,
                       hideBorders: true,
                       autoScroll: true

                    },
                    items: [{
                        id: "tree",
                        title: "Layers",
                        padding: 0
                    }, {
                        id: 'summary_table',
                        title: "Summary Form"
                    }, {
                        id: 'observations',
                        title: "Observations",
                        padding: 0,
                        bodyCfg: {
                            tag: "iframe",
                            src: "/observations/obsform/",
                            style: {border: "0px none"}
                        },
                        listeners: {
                            "resize": adjustIframeSize,
                            "afterlayout": adjustIframeSize
                        }
                    }//, {
                        //id: "fault_source",
                        //title: "Fault Section"
                    //}
                    ]
                }, "map", {
                    id: "featuregrid",
                    layout: "fit",
                    region: "south",
                    border: false,
                    height: 200,
                    split: true,
                    collapseMode: "mini"
                }]
            }],

            tools: [{
                actionTarget: {target: "paneltbar", index: 0},
                outputAction: 0,
                outputConfig: {
                    title: "Faulted Earth",
                    width: 300,
                    height: 300,
                    modal: true,
                    bodyCfg: {
                        tag: "iframe",
                        src: "about.html",
                        style: {border: 0}
                    }
                },
                actions: [{
                    iconCls: "icon-geoexplorer",
                    text: "Faulted Earth"
                }]
            },{
                ptype: "gxp_layertree",
                outputTarget: "tree"
            }, {
                ptype: "gxp_featuremanager",
                id: "featuremanager",
                autoLoadFeatures: true,
                maxFeatures: 50,
                format: "JSON"
            }, {
                ptype: "gxp_featureeditor",
                featureManager: "featuremanager",
                actionTarget: "paneltbar"
            }, {
                ptype: "gxp_featuregrid",
                outputConfig: {id: "grid"},
                alwaysDisplayOnMap: true,
                displayMode: "selected",
                featureManager: "featuremanager",
                outputTarget: "featuregrid"
            }, {
                ptype: "gxp_selectedfeatureactions",
                featureManager: "featuremanager",
                actions: [{
                    menuText: "Feature context demo",
                    text: "Feature context demo",
                    urlTemplate: "/geoserver/wms/reflect?layers={layer}&width=377&height=328&format=application/openlayers&featureid={fid}"
                }],
                actionTarget: ["grid.contextMenu", "grid.bbar"],
                outputConfig: {
                    width: 410,
                    height: 410
                }
            }],

            defaultSourceType: "gxp_wmscsource",
            sources: {
                local: {
                    url: "/geoserver/ows",
                    version: "1.1.1",
                    title: "Local GeoServer"
                },
                osm: {
                    ptype: "gxp_osmsource"
                },
                google: {
                    ptype: "gxp_googlesource"
                },
                ol: {
                    ptype: "gxp_olsource"
                }
            },

            map: {
                id: "map",
                region: "center",
                projection: "EPSG:900913",
                units: "m",
                maxResolution: 156543.0339,
                maxExtent: [
                    -20037508.34, -20037508.34,
                    20037508.34, 20037508.34
                ],
                layers: [{
                    source: "google",
                    title: "Google Terrain",
                    name: "TERRAIN",
                    group: "background"
                }, {
                    source: "osm",
                    name: "mapnik",
                    group: "background",
                    visibility: false
                }, {
                    source: "ol",
                    group: "background",
                    fixed: true,
                    type: "OpenLayers.Layer",
                    args: [
                        "None", {visibility: false}
                    ]
                },{
                    source: "local",
                    name: "gem:fault summary",
                    selected: true
                }],
                center: [0, 0],
                zoom: 2
            }
        });

        FaultedEarth.superclass.constructor.apply(this, arguments);
    }

});
<commit_msg>resloving layer config conflict<commit_after>FaultedEarth = Ext.extend(gxp.Viewer, {
    constructor: function(config) {

        function adjustIframeSize(cmp) {
            cmp.body.setWidth(cmp.ownerCt.getWidth());
        }

        Ext.applyIf(config, {
            proxy: "/proxy?url=",

            mapItems: [{
                xtype: "gx_zoomslider",
                vertical: true,
                height: 100
            }],
            portalItems: [{
                region: "center",
                layout: "border",
                tbar: {
                    id: "paneltbar",
                    items: ["-"]
                },
                items: [{
                    id: "west",
                    region: "west",
                    layout: "accordion",
                    width: 280,
                    split: true,
                    collapsible: true,
                    collapseMode: "mini",
                    header: false,
                    border: false,
                    defaults: {
                       padding: 10,
                       hideBorders: true,
                       autoScroll: true

                    },
                    items: [{
                        id: "tree",
                        title: "Layers",
                        padding: 0
                    }, {
                        id: 'summary_table',
                        title: "Summary Form"
                    }, {
                        id: 'observations',
                        title: "Observations",
                        padding: 0,
                        bodyCfg: {
                            tag: "iframe",
                            src: "/observations/obsform/",
                            style: {border: "0px none"}
                        },
                        listeners: {
                            "resize": adjustIframeSize,
                            "afterlayout": adjustIframeSize
                        }
                    }//, {
                        //id: "fault_source",
                        //title: "Fault Section"
                    //}
                    ]
                }, "map", {
                    id: "featuregrid",
                    layout: "fit",
                    region: "south",
                    border: false,
                    height: 200,
                    split: true,
                    collapseMode: "mini"
                }]
            }],

            tools: [{
                actionTarget: {target: "paneltbar", index: 0},
                outputAction: 0,
                outputConfig: {
                    title: "Faulted Earth",
                    width: 300,
                    height: 300,
                    modal: true,
                    bodyCfg: {
                        tag: "iframe",
                        src: "about.html",
                        style: {border: 0}
                    }
                },
                actions: [{
                    iconCls: "icon-geoexplorer",
                    text: "Faulted Earth"
                }]
            },{
                ptype: "gxp_layertree",
                outputTarget: "tree"
            }, {
                ptype: "gxp_featuremanager",
                id: "featuremanager",
                autoLoadFeatures: true,
                maxFeatures: 50,
                format: "JSON"
            }, {
                ptype: "gxp_featureeditor",
                featureManager: "featuremanager",
                actionTarget: "paneltbar"
            }, {
                ptype: "gxp_featuregrid",
                outputConfig: {id: "grid"},
                alwaysDisplayOnMap: true,
                displayMode: "selected",
                featureManager: "featuremanager",
                outputTarget: "featuregrid"
            }, {
                ptype: "gxp_selectedfeatureactions",
                featureManager: "featuremanager",
                actions: [{
                    menuText: "Feature context demo",
                    text: "Feature context demo",
                    urlTemplate: "/geoserver/wms/reflect?layers={layer}&width=377&height=328&format=application/openlayers&featureid={fid}"
                }],
                actionTarget: ["grid.contextMenu", "grid.bbar"],
                outputConfig: {
                    width: 410,
                    height: 410
                }
            }]
        });

        FaultedEarth.superclass.constructor.apply(this, arguments);
    }

});
<|endoftext|>


<commit_before>FaultedEarth = Ext.extend(gxp.Viewer, {

    summaryId: null,

    constructor: function(config) {

        Ext.Window.prototype.shadow = false;

        // property names for FeatureEditor and FeatureGrid
        var propertyNames = {
            // custom fied names for the fault summary table
            "name": "Fault Name",
            "is_episodi": "Episodic behaviour (yes/no, active/inactive)",
            "length": "Length (km, pref, min, max)",
            "u_sm_d_min": "Upper seismogenic depth min (km, 1, 2, 3, or 4)",
            "u_sm_d_max": "Upper seismogenic depth max (km, 1, 2, 3, or 4)",
            "u_sm_d_pre": "Upper seismogenic depth pref (km, 1, 2, 3, or 4)",
            "low_d_min": "Lower seismogenic depth min (km, 1, 2, 3, or 4)",
            "low_d_max": "Lower seismogenic depth max (km, 1, 2, 3, or 4)",
            "low_d_pref": "Lower seismogenic depth pref (km, 1, 2, 3, or 4)",
            "strike": "Strike (...\u00B0)",
            "dip_min": "Dip min (...\u00B0 1, 2, 3, or 4)",
            "dip_max": "Dip max (...\u00B0 1, 2, 3, or 4)",
            "dip_pref": "Dip pref (...\u00B0 1, 2, 3, or 4)",
            "dip_dir": "Dip direction (...\u00B0 1, 2, 3, or 4)",
            "down_thro": "Downthrow side (N, S, W, E or NW etc.)",
            "slip_typ": "Slip type (Reverse etc.)",
            "slip_r_min": "Slip rate min (mm/yr, 1, 2, 3, or 4)",
            "slip_r_max": "Slip rate max (mm/yr, 1, 2, 3, or 4)",
            "slip_r_pre": "Slip rate pref (mm/yr, 1, 2, 3, or 4)",
            "aseis_slip": " Aseismic-slip factor 0-1 (1, 2, 3, or 4)",
            "dis_min": "Displacement min (m)",
            "dis_max": "Displacement max (m)",
            "dis_pref": "Displacement pref (m)",
            "re_int_min": "Recurrence interval min (yr)",
            "re_int_max": "Recurrence interval max (yr)",
            "re_int_pre": "Recurrence interval pref (yr)",
            "mov_min": "Age of last movement min (yr BP)",
            "mov_max": "Age of last movement max (yr BP)",
            "mov_pref": "Age of last movement pref (yr BP)",
            "completene": "Data completeness (1, 2, 3, or 4)",
            "created": "Date created (name)",
            "compiler": "Compiled by (name)",
            "contrib": "Contributed by (date)",
            // custom fied names for the observations table
            "observationType": "Observation Type",
            "slipType": "Slip Type",
            "hv_ratio": "H:V Ratio",
            "rake": "Rake (deg)",
            "net_slip_rate_min": "Net Slip Rate Min (mm/yr)",
            "net_slip_rate_max": "Net Slip Rate Max (mm/yr)",
            "net_slip_rate_pref": "Net Slip Rate Pref (mm/yr)",
            "dip_slip_rate_min": "Dip Slip Rate Min (mm/yr)",
            "dip_slip_rate_max": "Dip Slip Rate Max (mm/yr)",
            "dip_slip_rate_pref": "Dip Slip Rate Pref (mm/yr)",
            "marker_age": "Marker Age (yrs BP)",
            "slip_rate_category": "Slip Rate Category",
            "strike_slip_rate_min": "Strike Slip Rate Min (mm/yr)",
            "strike_slip_rate_max": "Strike Slip Rate Max (mm/yr)",
            "strike_slip_rate_pref": "Strike Slip Rate Pref (mm/yr)",
            "vertical_slip_rate_min": "Vertical Slip Rate Min (mm/yr)",
            "vertical_slip_rate_max": "Vertical Slip Rate Max (mm/yr)",
            "vertical_slip_rate_pref": "Vertical Slip Rate Pref (mm/yr)",
            "site": "Site",
            "notes": "Notes",
            "summary_id": "Fault Summary ID"
        };

        Ext.applyIf(config, {
            proxy: "/proxy?url=",

            mapItems: [{
                xtype: "gx_zoomslider",
                vertical: true,
                height: 100
            }],
            portalItems: [{
                region: "center",
                layout: "border",
                tbar: {
                    id: "paneltbar",
                    items: ["-"]
                },
                items: [{
                    id: "west",
                    region: "west",
                    layout: "accordion",
                    width: 280,
                    split: true,
                    collapsible: true,
                    collapseMode: "mini",
                    header: false,
                    border: false,
                    defaults: {
                       hideBorders: true,
                       autoScroll: true

                    },
                    items: [{
                        id: "tree",
                        title: "Layers"
                    }, {
                        id: 'summary',
                        title: "Summary Form",
                        padding: 10
                    }, {
                        id: 'observations',
                        title: "Observations",
                        layout: "fit",
                        autoScroll: false
                    }//, {
                        //id: "fault_source",
                        //title: "Fault Section"
                    //}
                    ]
                }, "map", {
                    id: "featuregrid",
                    layout: "fit",
                    region: "south",
                    border: false,
                    height: 200,
                    split: true,
                    collapseMode: "mini"
                }]
            }],

            tools: [{
                actionTarget: {target: "paneltbar", index: 0},
                outputAction: 0,
                outputConfig: {
                    title: "Faulted Earth",
                    width: 300,
                    height: 300,
                    modal: true,
                    bodyCfg: {
                        tag: "iframe",
                        src: "about.html",
                        style: {border: 0}
                    }
                },
                actions: [{
                    iconCls: "icon-geoexplorer",
                    text: "Faulted Earth"
                }]
            },{
                ptype: "gxp_layertree",
                outputTarget: "tree"
            }, {
                ptype: "gxp_featuremanager",
                id: "featuremanager",
                autoLoadFeatures: true,
                autoSetLayer: false,
                paging: false,
                maxFeatures: 100
            }, {
                ptype: "gxp_featuregrid",
                alwaysDisplayOnMap: true,
                displayMode: "selected",
                featureManager: "featuremanager",
                outputTarget: "featuregrid",
                outputConfig: {
                    id: "grid",
                    propertyNames: propertyNames
                }
            }, {
                ptype: "gxp_selectedfeatureactions",
                featureManager: "featuremanager",
                actions: [{
                    menuText: "Feature context demo",
                    text: "Feature context demo",
                    urlTemplate: "/geoserver/wms/reflect?layers={layer}&width=377&height=328&format=application/openlayers&featureid={fid}"
                }],
                actionTarget: ["grid.contextMenu", "grid.bbar"],
                outputConfig: {
                    width: 410,
                    height: 410
                }
            }, {
                ptype: "app_summaryform",
                id: "summaryform",
                featureManager: "featuremanager",
                featureEditor: "featureeditor",
                outputTarget: "summary"
            }, {
                ptype: "gxp_featureeditor",
                id: "featureeditor",
                featureManager: "featuremanager",
                actionTarget: "summaryform_tooltarget",
                createFeatureActionText: "Draw",
                editFeatureActionText: "Modify",
                outputConfig: {
                    propertyNames: propertyNames
                }
            }, {
                ptype: "app_observations",
                featureManager: "featuremanager",
                outputTarget: "observations"
            }]
        });

        FaultedEarth.superclass.constructor.apply(this, arguments);
    }

});
<commit_msg>add ome more custom field name to the faut_summary table<commit_after>FaultedEarth = Ext.extend(gxp.Viewer, {

    summaryId: null,

    constructor: function(config) {

        Ext.Window.prototype.shadow = false;

        // property names for FeatureEditor and FeatureGrid
        var propertyNames = {
            // custom fied names for the fault summary table
            "name": "Fault Name",
            "episodi_is": "Episodic behaviour (yes/no)",
            "episodi_ac": "Episodic behaviour (active/inactive)",
            "length": "Length (km, pref, min, max)",
            "u_sm_d_min": "Upper seismogenic depth min (km, 1, 2, 3, or 4)",
            "u_sm_d_max": "Upper seismogenic depth max (km, 1, 2, 3, or 4)",
            "u_sm_d_pre": "Upper seismogenic depth pref (km, 1, 2, 3, or 4)",
            "low_d_min": "Lower seismogenic depth min (km, 1, 2, 3, or 4)",
            "low_d_max": "Lower seismogenic depth max (km, 1, 2, 3, or 4)",
            "low_d_pref": "Lower seismogenic depth pref (km, 1, 2, 3, or 4)",
            "strike": "Strike (...\u00B0)",
            "dip_min": "Dip min (...\u00B0 1, 2, 3, or 4)",
            "dip_max": "Dip max (...\u00B0 1, 2, 3, or 4)",
            "dip_pref": "Dip pref (...\u00B0 1, 2, 3, or 4)",
            "dip_dir": "Dip direction (...\u00B0 1, 2, 3, or 4)",
            "down_thro": "Downthrown side (N, S, W, E or NW etc.)",
            "slip_typ": "Slip type (Reverse etc.)",
            "slip_r_min": "Slip rate min (mm/yr, 1, 2, 3, or 4)",
            "slip_r_max": "Slip rate max (mm/yr, 1, 2, 3, or 4)",
            "slip_r_pre": "Slip rate pref (mm/yr, 1, 2, 3, or 4)",
            "aseis_slip": " Aseismic-slip factor 0-1 (1, 2, 3, or 4)",
            "dis_min": "Displacement min (m)",
            "dis_max": "Displacement max (m)",
            "dis_pref": "Displacement pref (m)",
            "re_int_min": "Recurrence interval min (yr)",
            "re_int_max": "Recurrence interval max (yr)",
            "re_int_pre": "Recurrence interval pref (yr)",
            "mov_min": "Age of last movement min (yr BP)",
            "mov_max": "Age of last movement max (yr BP)",
            "mov_pref": "Age of last movement pref (yr BP)",
            "completene": "Data completeness (1, 2, 3, or 4)",
            "created": "Date created (date)",
            "compiler": "Compiled by (name)",
            "contrib": "Contributed by (name)",
            // custom fied names for the observations table
            "observationType": "Observation Type",
            "slipType": "Slip Type",
            "hv_ratio": "H:V Ratio",
            "rake": "Rake (deg)",
            "net_slip_rate_min": "Net Slip Rate Min (mm/yr)",
            "net_slip_rate_max": "Net Slip Rate Max (mm/yr)",
            "net_slip_rate_pref": "Net Slip Rate Pref (mm/yr)",
            "dip_slip_rate_min": "Dip Slip Rate Min (mm/yr)",
            "dip_slip_rate_max": "Dip Slip Rate Max (mm/yr)",
            "dip_slip_rate_pref": "Dip Slip Rate Pref (mm/yr)",
            "marker_age": "Marker Age (yrs BP)",
            "slip_rate_category": "Slip Rate Category",
            "strike_slip_rate_min": "Strike Slip Rate Min (mm/yr)",
            "strike_slip_rate_max": "Strike Slip Rate Max (mm/yr)",
            "strike_slip_rate_pref": "Strike Slip Rate Pref (mm/yr)",
            "vertical_slip_rate_min": "Vertical Slip Rate Min (mm/yr)",
            "vertical_slip_rate_max": "Vertical Slip Rate Max (mm/yr)",
            "vertical_slip_rate_pref": "Vertical Slip Rate Pref (mm/yr)",
            "site": "Site",
            "notes": "Notes",
            "summary_id": "Fault Summary ID"
        };

        Ext.applyIf(config, {
            proxy: "/proxy?url=",

            mapItems: [{
                xtype: "gx_zoomslider",
                vertical: true,
                height: 100
            }],
            portalItems: [{
                region: "center",
                layout: "border",
                tbar: {
                    id: "paneltbar",
                    items: ["-"]
                },
                items: [{
                    id: "west",
                    region: "west",
                    layout: "accordion",
                    width: 280,
                    split: true,
                    collapsible: true,
                    collapseMode: "mini",
                    header: false,
                    border: false,
                    defaults: {
                       hideBorders: true,
                       autoScroll: true

                    },
                    items: [{
                        id: "tree",
                        title: "Layers"
                    }, {
                        id: 'summary',
                        title: "Summary Form",
                        padding: 10
                    }, {
                        id: 'observations',
                        title: "Observations",
                        layout: "fit",
                        autoScroll: false
                    }//, {
                        //id: "fault_source",
                        //title: "Fault Section"
                    //}
                    ]
                }, "map", {
                    id: "featuregrid",
                    layout: "fit",
                    region: "south",
                    border: false,
                    height: 200,
                    split: true,
                    collapseMode: "mini"
                }]
            }],

            tools: [{
                actionTarget: {target: "paneltbar", index: 0},
                outputAction: 0,
                outputConfig: {
                    title: "Faulted Earth",
                    width: 300,
                    height: 300,
                    modal: true,
                    bodyCfg: {
                        tag: "iframe",
                        src: "about.html",
                        style: {border: 0}
                    }
                },
                actions: [{
                    iconCls: "icon-geoexplorer",
                    text: "Faulted Earth"
                }]
            },{
                ptype: "gxp_layertree",
                outputTarget: "tree"
            }, {
                ptype: "gxp_featuremanager",
                id: "featuremanager",
                autoLoadFeatures: true,
                autoSetLayer: false,
                paging: false,
                maxFeatures: 100
            }, {
                ptype: "gxp_featuregrid",
                alwaysDisplayOnMap: true,
                displayMode: "selected",
                featureManager: "featuremanager",
                outputTarget: "featuregrid",
                outputConfig: {
                    id: "grid",
                    propertyNames: propertyNames
                }
            }, {
                ptype: "gxp_selectedfeatureactions",
                featureManager: "featuremanager",
                actions: [{
                    menuText: "Feature context demo",
                    text: "Feature context demo",
                    urlTemplate: "/geoserver/wms/reflect?layers={layer}&width=377&height=328&format=application/openlayers&featureid={fid}"
                }],
                actionTarget: ["grid.contextMenu", "grid.bbar"],
                outputConfig: {
                    width: 410,
                    height: 410
                }
            }, {
                ptype: "app_summaryform",
                id: "summaryform",
                featureManager: "featuremanager",
                featureEditor: "featureeditor",
                outputTarget: "summary"
            }, {
                ptype: "gxp_featureeditor",
                id: "featureeditor",
                featureManager: "featuremanager",
                actionTarget: "summaryform_tooltarget",
                createFeatureActionText: "Draw",
                editFeatureActionText: "Modify",
                outputConfig: {
                    propertyNames: propertyNames
                }
            }, {
                ptype: "app_observations",
                featureManager: "featuremanager",
                outputTarget: "observations"
            }]
        });

        FaultedEarth.superclass.constructor.apply(this, arguments);
    }

});
<|endoftext|>


<commit_before>import React, {PureComponent, PropTypes} from 'react';
import autobind from 'autobind-decorator';
import HelpTooltip from '../help-tooltip';
import {isMac} from '../../../common/constants';

@autobind
class General extends PureComponent {
  _handleUpdateSetting (e) {
    let value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;

    if (e.target.type === 'number') {
      value = parseInt(value, 10);
    }

    this.props.updateSetting(e.target.name, value);
  }

  _handleToggleMenuBar (e) {
    this._handleUpdateSetting(e);
    this.props.handleToggleMenuBar(e.target.checked);
  }

  render () {
    const {settings} = this.props;
    return (

          Follow redirects automatically
            <input type="checkbox"
                   name="followRedirects"
                   checked={settings.followRedirects}
                   onChange={this._handleUpdateSetting}/>

          Validate SSL Certificates
            <input type="checkbox"
                   name="validateSSL"
                   checked={settings.validateSSL}
                   onChange={this._handleUpdateSetting}/>

          Show passwords in plain-text
            <input type="checkbox"
                   name="showPasswords"
                   checked={settings.showPasswords}
                   onChange={this._handleUpdateSetting}/>

          Use bulk header editor by default
            <input type="checkbox"
                   name="useBulkHeaderEditor"
                   checked={settings.useBulkHeaderEditor}
                   onChange={this._handleUpdateSetting}/>

          Always use vertical layout
            <input type="checkbox"
                   name="forceVerticalLayout"
                   checked={settings.forceVerticalLayout}
                   onChange={this._handleUpdateSetting}/>

        {!isMac() && (

            Hide Menu Bar
              <input type="checkbox"
                     name="autoHideMenuBar"
                     checked={settings.autoHideMenuBar}
                     onChange={this._handleToggleMenuBar}/>

        )}

          Wrap Long Lines
            <input type="checkbox"
                   name="editorLineWrapping"
                   checked={settings.editorLineWrapping}
                   onChange={this._handleUpdateSetting}/>

            Disable Usage Tracking
            {' '}
            <HelpTooltip>Requires restart to take effect</HelpTooltip>
            <input type="checkbox"
                   name="disableAnalyticsTracking"
                   checked={settings.disableAnalyticsTracking}
                   onChange={this._handleUpdateSetting}/>

            Text Editor Font Size (px)
              <input type="number"
                     name="editorFontSize"
                     min={8}
                     max={20}
                     defaultValue={settings.editorFontSize}
                     onChange={this._handleUpdateSetting}/>

            Text Editor Indent Size
              <input type="number"
                     name="editorIndentSize"
                     min={1}
                     max={16}
                     defaultValue={settings.editorIndentSize}
                     onChange={this._handleUpdateSetting}/>

            Text Editor Key Map
              <select defaultValue={settings.editorKeyMap}
                      name="editorKeyMap"
                      onChange={this._handleUpdateSetting}>
                <option value="default">Default</option>
                <option value="vim">Vim</option>
                <option value="emacs">Emacs</option>
              </select>

          Network Request Timeout
            <HelpTooltip className="space-left">
              Request timeout in milliseconds (0 for no timeout)
            </HelpTooltip>
            <input type="number"
                   name="timeout"
                   min={0}
                   defaultValue={settings.timeout}
                   onChange={this._handleUpdateSetting}/>

          HTTP Network Proxy
          <HelpTooltip className="space-left txt-md" style={{maxWidth: '20rem', lineWrap: 'word'}}>
            Enable global network proxy. Supports authentication via Basic Auth, digest, or NTLM
          </HelpTooltip>

          Enable Proxy
            <input type="checkbox"
                   name="proxyEnabled"
                   checked={settings.proxyEnabled}
                   onChange={this._handleUpdateSetting}/>

            HTTP Proxy
              <input type="text"
                     name="httpProxy"
                     disabled={!settings.proxyEnabled}
                     placeholder="localhost:8005"
                     defaultValue={settings.httpProxy}
                     onChange={this._handleUpdateSetting}/>

            HTTPS Proxy
              <input placeholder="localhost:8005"
                     disabled={!settings.proxyEnabled}
                     name="httpsProxy"
                     type="text"
                     defaultValue={settings.httpsProxy}
                     onChange={this._handleUpdateSetting}/>

              No Proxy <HelpTooltip>Comma-separated list of hostnames that do not require a
              proxy to be contacted</HelpTooltip>
              <input placeholder="localhost,127.0.0.1"
                     disabled={!settings.proxyEnabled}
                     name="noProxy"
                     type="text"
                     defaultValue={settings.noProxy}
                     onChange={this._handleUpdateSetting}/>

    );
  }
}

General.propTypes = {
  settings: PropTypes.object.isRequired,
  updateSetting: PropTypes.func.isRequired,
  handleToggleMenuBar: PropTypes.func.isRequired
};

export default General;
<commit_msg>Add Sublime text editor mode back<commit_after>import React, {PureComponent, PropTypes} from 'react';
import autobind from 'autobind-decorator';
import HelpTooltip from '../help-tooltip';
import {isMac} from '../../../common/constants';

@autobind
class General extends PureComponent {
  _handleUpdateSetting (e) {
    let value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;

    if (e.target.type === 'number') {
      value = parseInt(value, 10);
    }

    this.props.updateSetting(e.target.name, value);
  }

  _handleToggleMenuBar (e) {
    this._handleUpdateSetting(e);
    this.props.handleToggleMenuBar(e.target.checked);
  }

  render () {
    const {settings} = this.props;
    return (

          Follow redirects automatically
            <input type="checkbox"
                   name="followRedirects"
                   checked={settings.followRedirects}
                   onChange={this._handleUpdateSetting}/>

          Validate SSL Certificates
            <input type="checkbox"
                   name="validateSSL"
                   checked={settings.validateSSL}
                   onChange={this._handleUpdateSetting}/>

          Show passwords in plain-text
            <input type="checkbox"
                   name="showPasswords"
                   checked={settings.showPasswords}
                   onChange={this._handleUpdateSetting}/>

          Use bulk header editor by default
            <input type="checkbox"
                   name="useBulkHeaderEditor"
                   checked={settings.useBulkHeaderEditor}
                   onChange={this._handleUpdateSetting}/>

          Always use vertical layout
            <input type="checkbox"
                   name="forceVerticalLayout"
                   checked={settings.forceVerticalLayout}
                   onChange={this._handleUpdateSetting}/>

        {!isMac() && (

            Hide Menu Bar
              <input type="checkbox"
                     name="autoHideMenuBar"
                     checked={settings.autoHideMenuBar}
                     onChange={this._handleToggleMenuBar}/>

        )}

          Wrap Long Lines
            <input type="checkbox"
                   name="editorLineWrapping"
                   checked={settings.editorLineWrapping}
                   onChange={this._handleUpdateSetting}/>

            Disable Usage Tracking
            {' '}
            <HelpTooltip>Requires restart to take effect</HelpTooltip>
            <input type="checkbox"
                   name="disableAnalyticsTracking"
                   checked={settings.disableAnalyticsTracking}
                   onChange={this._handleUpdateSetting}/>

            Text Editor Font Size (px)
              <input type="number"
                     name="editorFontSize"
                     min={8}
                     max={20}
                     defaultValue={settings.editorFontSize}
                     onChange={this._handleUpdateSetting}/>

            Text Editor Indent Size
              <input type="number"
                     name="editorIndentSize"
                     min={1}
                     max={16}
                     defaultValue={settings.editorIndentSize}
                     onChange={this._handleUpdateSetting}/>

            Text Editor Key Map
              <select defaultValue={settings.editorKeyMap}
                      name="editorKeyMap"
                      onChange={this._handleUpdateSetting}>
                <option value="default">Default</option>
                <option value="vim">Vim</option>
                <option value="emacs">Emacs</option>
                <option value="sublime">Sublime</option>
              </select>

          Network Request Timeout
            <HelpTooltip className="space-left">
              Request timeout in milliseconds (0 for no timeout)
            </HelpTooltip>
            <input type="number"
                   name="timeout"
                   min={0}
                   defaultValue={settings.timeout}
                   onChange={this._handleUpdateSetting}/>

          HTTP Network Proxy
          <HelpTooltip className="space-left txt-md" style={{maxWidth: '20rem', lineWrap: 'word'}}>
            Enable global network proxy. Supports authentication via Basic Auth, digest, or NTLM
          </HelpTooltip>

          Enable Proxy
            <input type="checkbox"
                   name="proxyEnabled"
                   checked={settings.proxyEnabled}
                   onChange={this._handleUpdateSetting}/>

            HTTP Proxy
              <input type="text"
                     name="httpProxy"
                     disabled={!settings.proxyEnabled}
                     placeholder="localhost:8005"
                     defaultValue={settings.httpProxy}
                     onChange={this._handleUpdateSetting}/>

            HTTPS Proxy
              <input placeholder="localhost:8005"
                     disabled={!settings.proxyEnabled}
                     name="httpsProxy"
                     type="text"
                     defaultValue={settings.httpsProxy}
                     onChange={this._handleUpdateSetting}/>

              No Proxy <HelpTooltip>Comma-separated list of hostnames that do not require a
              proxy to be contacted</HelpTooltip>
              <input placeholder="localhost,127.0.0.1"
                     disabled={!settings.proxyEnabled}
                     name="noProxy"
                     type="text"
                     defaultValue={settings.noProxy}
                     onChange={this._handleUpdateSetting}/>

    );
  }
}

General.propTypes = {
  settings: PropTypes.object.isRequired,
  updateSetting: PropTypes.func.isRequired,
  handleToggleMenuBar: PropTypes.func.isRequired
};

export default General;
<|endoftext|>


<commit_before>import { Meteor } from 'meteor/meteor';
import { Accounts } from 'meteor/accounts-base';

import { baseURI } from './baseuri';

export const APIClient = {
	delete(endpoint, params) {
		return APIClient._jqueryCall('DELETE', endpoint, params);
	},

	get(endpoint, params) {
		return APIClient._jqueryCall('GET', endpoint, params);
	},

	post(endpoint, params, body) {
		if (!body) {
			body = params;
			params = {};
		}

		return APIClient._jqueryCall('POST', endpoint, params, body);
	},

	upload(endpoint, params, formData, xhrOptions) {
		if (!formData) {
			formData = params;
			params = {};
		}

		return APIClient._jqueryFormDataCall(endpoint, params, formData, xhrOptions);
	},

	_generateQueryFromParams(params) {
		let query = '';
		if (params && typeof params === 'object') {
			Object.keys(params).forEach((key) => {
				query += query === '' ? '?' : '&';

				query += `${ key }=${ params[key] }`;
			});
		}

		return query;
	},

	_jqueryCall(method, endpoint, params, body) {
		const query = APIClient._generateQueryFromParams(params);

		return new Promise(function _rlRestApiGet(resolve, reject) {
			jQuery.ajax({
				method,
				url: `${ baseURI }api/${ endpoint }${ query }`,
				headers: {
					'Content-Type': 'application/json',
					'X-User-Id': Meteor._localStorage.getItem(Accounts.USER_ID_KEY),
					'X-Auth-Token': Meteor._localStorage.getItem(Accounts.LOGIN_TOKEN_KEY),
				},
				data: JSON.stringify(body),
				success: function _rlGetSuccess(result) {
					resolve(result);
				},
				error: function _rlGetFailure(xhr, status, errorThrown) {
					const error = new Error(errorThrown);
					error.xhr = xhr;
					reject(error);
				},
			});
		});
	},

	_jqueryFormDataCall(endpoint, params, formData, { progress = () => {}, error = () => {} } = {}) {
		const ret = { };

		const query = APIClient._generateQueryFromParams(params);

		if (!(formData instanceof FormData)) {
			throw new Error('The formData parameter MUST be an instance of the FormData class.');
		}

		ret.promise = new Promise(function _jqueryFormDataPromise(resolve, reject) {
			ret.xhr = jQuery.ajax({
				xhr() {
					const xhr = new window.XMLHttpRequest();

					xhr.upload.addEventListener('progress', function(evt) {
						if (evt.lengthComputable) {
							const percentComplete = evt.loaded / evt.total;
							progress(percentComplete * 100);
						}
					}, false);

					xhr.upload.addEventListener('error', error, false);
					xhr.upload.addEventListener('abort', abort, false);

					return xhr;
				},
				url: `${ baseURI }api/${ endpoint }${ query }`,
				headers: {
					'X-User-Id': Meteor._localStorage.getItem(Accounts.USER_ID_KEY),
					'X-Auth-Token': Meteor._localStorage.getItem(Accounts.LOGIN_TOKEN_KEY),
				},
				data: formData,
				processData: false,
				contentType: false,
				type: 'POST',
				success: function _jqueryFormDataSuccess(result) {
					resolve(result);
				},
				error: function _jqueryFormDataError(xhr, status, errorThrown) {
					const error = new Error(errorThrown);
					error.xhr = xhr;
					reject(error);
				},
			});
		});

		return ret;
	},

	v1: {
		delete(endpoint, params) {
			return APIClient.delete(`v1/${ endpoint }`, params);
		},

		get(endpoint, params) {
			return APIClient.get(`v1/${ endpoint }`, params);
		},

		post(endpoint, params, body) {
			return APIClient.post(`v1/${ endpoint }`, params, body);
		},

		upload(endpoint, params, formData) {
			return APIClient.upload(`v1/${ endpoint }`, params, formData);
		},
	},
};
<commit_msg>Update app/utils/client/lib/RestApiClient.js<commit_after>import { Meteor } from 'meteor/meteor';
import { Accounts } from 'meteor/accounts-base';

import { baseURI } from './baseuri';

export const APIClient = {
	delete(endpoint, params) {
		return APIClient._jqueryCall('DELETE', endpoint, params);
	},

	get(endpoint, params) {
		return APIClient._jqueryCall('GET', endpoint, params);
	},

	post(endpoint, params, body) {
		if (!body) {
			body = params;
			params = {};
		}

		return APIClient._jqueryCall('POST', endpoint, params, body);
	},

	upload(endpoint, params, formData, xhrOptions) {
		if (!formData) {
			formData = params;
			params = {};
		}

		return APIClient._jqueryFormDataCall(endpoint, params, formData, xhrOptions);
	},

	_generateQueryFromParams(params) {
		let query = '';
		if (params && typeof params === 'object') {
			Object.keys(params).forEach((key) => {
				query += query === '' ? '?' : '&';

				query += `${ key }=${ params[key] }`;
			});
		}

		return query;
	},

	_jqueryCall(method, endpoint, params, body) {
		const query = APIClient._generateQueryFromParams(params);

		return new Promise(function _rlRestApiGet(resolve, reject) {
			jQuery.ajax({
				method,
				url: `${ baseURI }api/${ endpoint }${ query }`,
				headers: {
					'Content-Type': 'application/json',
					'X-User-Id': Meteor._localStorage.getItem(Accounts.USER_ID_KEY),
					'X-Auth-Token': Meteor._localStorage.getItem(Accounts.LOGIN_TOKEN_KEY),
				},
				data: JSON.stringify(body),
				success: function _rlGetSuccess(result) {
					resolve(result);
				},
				error: function _rlGetFailure(xhr, status, errorThrown) {
					const error = new Error(errorThrown);
					error.xhr = xhr;
					reject(error);
				},
			});
		});
	},

	_jqueryFormDataCall(endpoint, params, formData, { progress = () => {}, error = () => {} } = {}) {
		const ret = { };

		const query = APIClient._generateQueryFromParams(params);

		if (!(formData instanceof FormData)) {
			throw new Error('The formData parameter MUST be an instance of the FormData class.');
		}

		ret.promise = new Promise(function _jqueryFormDataPromise(resolve, reject) {
			ret.xhr = jQuery.ajax({
				xhr() {
					const xhr = new window.XMLHttpRequest();

					xhr.upload.addEventListener('progress', function(evt) {
						if (evt.lengthComputable) {
							const percentComplete = evt.loaded / evt.total;
							progress(percentComplete * 100);
						}
					}, false);

					xhr.upload.addEventListener('error', error, false);

					return xhr;
				},
				url: `${ baseURI }api/${ endpoint }${ query }`,
				headers: {
					'X-User-Id': Meteor._localStorage.getItem(Accounts.USER_ID_KEY),
					'X-Auth-Token': Meteor._localStorage.getItem(Accounts.LOGIN_TOKEN_KEY),
				},
				data: formData,
				processData: false,
				contentType: false,
				type: 'POST',
				success: function _jqueryFormDataSuccess(result) {
					resolve(result);
				},
				error: function _jqueryFormDataError(xhr, status, errorThrown) {
					const error = new Error(errorThrown);
					error.xhr = xhr;
					reject(error);
				},
			});
		});

		return ret;
	},

	v1: {
		delete(endpoint, params) {
			return APIClient.delete(`v1/${ endpoint }`, params);
		},

		get(endpoint, params) {
			return APIClient.get(`v1/${ endpoint }`, params);
		},

		post(endpoint, params, body) {
			return APIClient.post(`v1/${ endpoint }`, params, body);
		},

		upload(endpoint, params, formData) {
			return APIClient.upload(`v1/${ endpoint }`, params, formData);
		},
	},
};
<|endoftext|>


<commit_before>import { merge } from "./extend";

/**
 * @typedef {{
 *      domain: string,
 *      path: string,
 *      secure: boolean,
 *      ?expires: number|Date,
 * }} mojave.CookieOptions
 */

/**
 * Sets a cookie's value. If the value is an object or array, it'll be JSON.stringify and saved as a string
 *
 * @param {string} key
 * @param {*} value
 * @param {mojave.CookieOptions} options
 */
export function setCookie (key, value, options = {})
{
    document.cookie = formatCookieString(key, value, options);
}

/**
 * Formats the cookie string
 *
 * @private
 * @param {string} key
 * @param {*} value
 * @param {mojave.CookieOptions} options
 * @returns {string}
 */
export function formatCookieString (key, value, options = {})
{
    options = merge({
        path: "/",
        secure: window.location.protocol === "https",
        expires: 30,
    }, options);

    if (typeof options.expires === "number")
    {
        options.expires = new Date((new Date()).getTime() + (options.expires * 864e+5));
    }

    options.expires = !options.expires ? "" : options.expires.toUTCString();

    if (/[^a-z0-9\-_.]/i.test(key))
    {
        throw new Error("Invalid cookie name: only A-Z 0-9 -_. allowed.");
    }

    const encodedValue = encodeCookieValue(value);
    const encodedOptions = encodeCookieOptions(options);

    return `${key}=${encodedValue};${encodedOptions}`;
}

/**
 * Retrieves a cookie's value
 *
 * @param {string} key
 *
 * @returns {*|null}
 */
export function getCookie (key)
{
    const matcher = new RegExp(`; ${key}=([^;]+)`);
    const match = matcher.exec(`; ${document.cookie}`);

    return null !== match
        ? JSON.parse(decodeURIComponent(match[1]))
        : null;
}

/**
 * Removes the given cookie
 *
 * @param {string} key
 */
export function removeCookie (key)
{
    setCookie(key, "", {
        expires: -1,
    });
}

/**
 * @private
 * @param {*} value
 *
 * @return {string}
 */
function encodeCookieValue (value)
{
    return encodeURIComponent(JSON.stringify(value))
        .replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);
}

/**
 * @private
 * @param {Object.<string, string|boolean>} options
 *
 * @return {string}
 */
function encodeCookieOptions (options)
{
    const encodedOptions = [];

    // eslint-disable-next-line guard-for-in
    for (let optionName in options)
    {
        const optionValue = options[optionName];

        // Flags with a `false` value needs to be omitted
        if (!options.hasOwnProperty(optionName) || optionValue === false)
        {
            continue;
        }

        // RFC 6265 section 5.2:
        // =====================
        // 3.  If the remaining unparsed-attributes contains a %x3B (";") character:
        // Consume the characters of the unparsed-attributes up to, not including, the first %x3B (";") character.
        const sanitizedOption = (optionValue === true)
            ? optionName
            : `${optionName}=${("" + optionValue).split(";")[0]}`;

        encodedOptions.push(sanitizedOption);
    }

    return encodedOptions.join(" ;");
}
<commit_msg>Add comment about encoding cookie values + inline call<commit_after>import { merge } from "./extend";

/**
 * @typedef {{
 *      domain: string,
 *      path: string,
 *      secure: boolean,
 *      ?expires: number|Date,
 * }} mojave.CookieOptions
 */

/**
 * Sets a cookie's value. If the value is an object or array, it'll be JSON.stringify and saved as a string
 *
 * @param {string} key
 * @param {*} value
 * @param {mojave.CookieOptions} options
 */
export function setCookie (key, value, options = {})
{
    document.cookie = formatCookieString(key, value, options);
}

/**
 * Formats the cookie string
 *
 * @private
 * @param {string} key
 * @param {*} value
 * @param {mojave.CookieOptions} options
 * @returns {string}
 */
export function formatCookieString (key, value, options = {})
{
    options = merge({
        path: "/",
        secure: window.location.protocol === "https",
        expires: 30,
    }, options);

    if (typeof options.expires === "number")
    {
        options.expires = new Date((new Date()).getTime() + (options.expires * 864e+5));
    }

    options.expires = !options.expires ? "" : options.expires.toUTCString();

    if (/[^a-z0-9\-_.]/i.test(key))
    {
        throw new Error("Invalid cookie name: only A-Z 0-9 -_. allowed.");
    }

    // encode the cookie value and de-encode characters, that don't need to be escaped.
    const encodedValue = encodeURIComponent(JSON.stringify(value))
        .replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);
    const encodedOptions = encodeCookieOptions(options);

    return `${key}=${encodedValue};${encodedOptions}`;
}

/**
 * Retrieves a cookie's value
 *
 * @param {string} key
 *
 * @returns {*|null}
 */
export function getCookie (key)
{
    const matcher = new RegExp(`; ${key}=([^;]+)`);
    const match = matcher.exec(`; ${document.cookie}`);

    return null !== match
        ? JSON.parse(decodeURIComponent(match[1]))
        : null;
}

/**
 * Removes the given cookie
 *
 * @param {string} key
 */
export function removeCookie (key)
{
    setCookie(key, "", {
        expires: -1,
    });
}

/**
 * @private
 * @param {Object.<string, string|boolean>} options
 *
 * @return {string}
 */
function encodeCookieOptions (options)
{
    const encodedOptions = [];

    // eslint-disable-next-line guard-for-in
    for (let optionName in options)
    {
        const optionValue = options[optionName];

        // Flags with a `false` value needs to be omitted
        if (!options.hasOwnProperty(optionName) || optionValue === false)
        {
            continue;
        }

        // RFC 6265 section 5.2:
        // =====================
        // 3.  If the remaining unparsed-attributes contains a %x3B (";") character:
        // Consume the characters of the unparsed-attributes up to, not including, the first %x3B (";") character.
        const sanitizedOption = (optionValue === true)
            ? optionName
            : `${optionName}=${("" + optionValue).split(";")[0]}`;

        encodedOptions.push(sanitizedOption);
    }

    return encodedOptions.join(" ;");
}
<|endoftext|>