_handleResetLayers: function (payload) { var documentID = payload.documentID, documentExports = this._documentExportsMap.get(documentID), layers = collection.pluck(payload.layers, "descriptor"), nextDocumentExports = documentExports.resetLayers(layers); this._documentExportsMap = this._documentExportsMap.set(documentID, nextDocumentExports); this.emit("change"); },
_getOptions: function (type) { var searchTypeInfo = this._registeredSearchTypes[type]; if (searchTypeInfo) { var items = searchTypeInfo.getOptions(); var itemMap; if (items === null) { itemMap = []; } else { // Get shortest unique paths var ancestors = collection.pluck(items, "pathInfo"), shortenedPaths = searchTypeInfo.shortenPaths ? pathUtil.getShortestUniquePaths(ancestors) : ancestors; itemMap = items.map(function (item, index) { var newPathInfo = shortenedPaths.get(index) || "", name = item.name, untitledString = nls.localize("strings.SEARCH.UNTITLED"), style; // Don't show the path info if it is just the same as the item name if (name === newPathInfo) { newPathInfo = ""; } if (name === "") { name = untitledString; style = { "fontStyle": "italic" }; } return { id: type + "-" + item.id.toString(), title: name, pathInfo: newPathInfo, svgType: item.iconID, category: item.category, style: style, type: "item" }; }, this); } // Label to separate groups of options var headerLabel = { id: type + "-header", title: HEADERS[type], category: [], type: "header" }; return itemMap.unshift(headerLabel); } return Immutable.List(); },
var registerLibrarySearch = function (libraries) { var libraryIDs = libraries.length > 0 ? collection.pluck(libraries, "id") : Immutable.List(), libraryNames = libraries.length > 0 ? collection.pluck(libraries, "name") : Immutable.List(), assetTypes = ["LIBRARY", "GRAPHIC", "LAYERSTYLE", "CHARACTERSTYLE"]; var filters = libraryIDs.concat(assetTypes), displayFilters = libraryNames.concat(assetTypes); var payload = { "type": "LIBRARY", "getOptions": _getLibrarySearchOptions.bind(this), "filters": filters, "displayFilters": displayFilters, "handleExecute": _confirmSearch.bind(this), "shortenPaths": false, "getSVGClass": _getSVGClass }; return this.dispatchAsync(events.search.REGISTER_SEARCH_PROVIDER, payload); };
.then(function (cefHasFocus) { var el = window.document.activeElement, data; if (cefHasFocus && _isInput(el)) { if (_isTextInput(el)) { data = el.value.substring(el.selectionStart, el.selectionEnd); if (cut) { el.setRangeText(""); } } else { data = el.value; } } else { // Even if CEF doesn't have focus, a disabled input could have a selection var selection = window.document.getSelection(); if (selection.type === "Range") { data = selection.toString(); } } if (typeof data === "string") { var cutCopyEvent = new window.Event(cut ? "cut" : "copy", { bubbles: true }); el.dispatchEvent(cutCopyEvent); return os.clipboardWrite(data); } // If we're on modal state (type edit), we should go with native copy/cut if (this.flux.store("tool").getModalToolState()) { if (cut) { this.flux.actions.edit.nativeCut(); } else { this.flux.actions.edit.nativeCopy(); } } else if (!cut) { var applicationStore = this.flux.store("application"), document = applicationStore.getCurrentDocument(); if (!document || document.unsupported) { return; } var layerIDs = collection.pluck(document.layers.selectedNormalized, "id"), payload = { document: document.id, layers: layerIDs }, rawPayload = JSON.stringify(payload); headlights.logEvent("edit", "layers", "copy_layers"); return os.clipboardWrite(rawPayload, LAYER_CLIPBOARD_FORMAT); } });
var _strokeChangeDispatch = function (document, layers, strokeProperties, eventName, coalesce) { var payload = { documentID: document.id, layerIDs: collection.pluck(layers, "id"), strokeProperties: strokeProperties, coalesce: coalesce, history: { newState: true } }; return this.dispatchAsync(eventName, payload); };
var _fillChangeDispatch = function (document, layers, fillProperties, eventName, coalesce) { // TODO layers param needs to be made fa real var payload = { documentID: document.id, layerIDs: collection.pluck(layers, "id"), fillProperties: fillProperties, coalesce: coalesce, history: { newState: true } }; return this.dispatchAsync(eventName, payload); };
var deleteEffect = function (document, layers, effectIndex, effectType) { var payload = { documentID: document.id, layerIDs: collection.pluck(layers, "id"), layerEffectType: effectType, layerEffectIndex: effectIndex, history: { newState: true, name: nls.localize("strings.ACTIONS.SET_LAYER_EFFECTS") } }; // Synchronously update the stores this.dispatch(events.document.history.LAYER_EFFECT_DELETED, payload); // Then update photoshop return _syncStoreToPs.call(this, document, layers, null, effectType, null); };
var _upsertEffectProperties = function (document, layers, layerEffectIndex, newProps, coalesce, type) { var layerIDs = collection.pluck(layers, "id"), layerEffectPropsList = [], layerEffectIndexList = []; // Prepare some per-layer items for the payload layers.forEach(function (curLayer) { var curLayerEffects = curLayer.getLayerEffectsByType(type), curLayerEffect, props; if (curLayerEffects && curLayerEffects.has(layerEffectIndex)) { // updating existing layer effect curLayerEffect = curLayerEffects.get(layerEffectIndex); layerEffectIndexList.push(layerEffectIndex); } else { // adding new layer effect curLayerEffect = {}; layerEffectIndexList.push(null); // will use push on top of any existing layer effects } // if newProps is a function, apply it props = _.isFunction(newProps) ? newProps(curLayerEffect) : newProps; // force it back enabled unless explicitly set to false props.enabled = (props.enabled === undefined) || props.enabled; layerEffectPropsList.push(props); }, this); var payload = { documentID: document.id, layerIDs: layerIDs, layerEffectType: type, layerEffectIndex: Immutable.List(layerEffectIndexList), layerEffectProperties: Immutable.List(layerEffectPropsList), coalesce: !!coalesce, history: { newState: true, name: nls.localize("strings.ACTIONS.SET_LAYER_EFFECTS") } }; // Synchronously update the stores this.dispatch(events.document.history.LAYER_EFFECT_CHANGED, payload); // Then update photoshop return _syncStoreToPs.call(this, document, layers, coalesce, type); };
var _playCombine = function (document, layers, playObject) { if (layers.isEmpty()) { return Promise.resolve(); } var dispatchPromise, payload = { documentID: document.id, history: { newState: true, name: nls.localize("strings.ACTIONS.COMBINE_SHAPES") } }; if (layers.size > 1) { payload.layerIDs = collection.pluck(layers.butLast(), "id"); dispatchPromise = this.dispatchAsync(events.document.history.DELETE_LAYERS, payload); } else { dispatchPromise = this.dispatchAsync(events.history.NEW_HISTORY_STATE, payload); } var options = { historyStateInfo: { name: nls.localize("strings.ACTIONS.COMBINE_SHAPES"), target: documentLib.referenceBy.id(document.id) } }, playPromise = descriptor.playObject(playObject, options); return Promise.join(dispatchPromise, playPromise) .bind(this) .then(function () { if (layers.size > 1) { // The "highest" layer wins but the resultant layer is shifted down // by the number of "losing" layers // Important note: the resultant layer has a NEW LAYER ID var winningLayerIndex = document.layers.indexOf(layers.last()), adjustedLayerIndex = winningLayerIndex - layers.size + 1; return this.transfer(layerActions.resetLayersByIndex, document, adjustedLayerIndex); } else { return this.transfer(layerActions.resetLayers, document, layers); } }); };
var _refreshStrokes = function (document, layers) { var layerIDs = collection.pluck(layers, "id"), refs = layerLib.referenceBy.id(layerIDs.toArray()); return descriptor.batchMultiGetProperties(refs._ref, ["AGMStrokeStyleInfo"]) .bind(this) .then(function (batchGetResponse) { if (!batchGetResponse || batchGetResponse.length !== layers.size) { throw new Error("Bad response from photoshop for AGMStrokeStyleInfo batchGet"); } var payload = { documentID: document.id, layerIDs: layerIDs, strokeStyleDescriptor: Immutable.List(_.pluck(batchGetResponse, "AGMStrokeStyleInfo")), history: { newState: true, amendRogue: true } }; this.dispatch(events.document.history.STROKE_ADDED, payload); }); };
var duplicateLayerEffects = function (document, targetLayers, source, options) { targetLayers = targetLayers || document.layers.selected; var layerIDs = collection.pluck(targetLayers, "id"), masterEffectIndexMap = {}, // Immutable.Map<String, Immutable.List<Immutable.List<number>>> masterEffectPropsMap = {}, // Immutable.Map<String, Immutable.List<Immutable.List<object>>> masterEffectTypes = [], // Immutable.List<String>, for keys of above maps sourceEffects = _.map(source.toObject(), function (effects, effectType) { return { type: effectType, effects: effects }; }); // 1. Build lists to update / insert the properties from source layer to target layers // // For each effectType // For each effect at index i // Build every layer's ID array, with index for that layer, and props for that layer's new effect // Build a different master props/index list for each effect type sourceEffects.forEach(function (sourceEffect) { var curType = sourceEffect.type, effectPropsList = [], effectIndexList = []; masterEffectTypes.push(curType); // Each effect will have either an index, or null to be inserted into each layer, // which are saved in effectPropsList and effectIndexList sourceEffect.effects.forEach(function (effectObj, effectIndex) { var perEffectIndexList = [], perEffectPropsList = []; targetLayers.forEach(function (targetLayer) { var targetLayerEffects = targetLayer.getLayerEffectsByType(curType), newEffectProps = effectObj; if (targetLayerEffects && targetLayerEffects.has(effectIndex)) { // If it already exists, we can just push the ID for that effect perEffectIndexList.push(effectIndex); // The order we push on these lists is the layer ordering } else { // If it doesn't exist, we push a null object/null index // THIS IS GONNA BE A PROBLEM WITH MULTIPLES perEffectIndexList.push(null); // null signifies this will be the first effect } var enabled = (newEffectProps.enabled === undefined) || newEffectProps.enabled; // We have to keep this immutable so we don't lose the data structures like Color in the Record newEffectProps.set("enabled", enabled); perEffectPropsList.push(newEffectProps); }); effectIndexList.push(Immutable.List(perEffectIndexList)); effectPropsList.push(Immutable.List(perEffectPropsList)); }); // Now that we've build per effect per layer lists, we can add them to master list masterEffectIndexMap[curType] = Immutable.List(effectIndexList); masterEffectPropsMap[curType] = Immutable.List(effectPropsList); }); // 2. Expand the lists to delete the effects that are not exist in the source layer. masterEffectTypes.forEach(function (effectType) { var effectIndexList = masterEffectIndexMap[effectType], effectPropsMap = masterEffectPropsMap[effectType], sourceEffectSize = source.get(effectType).size, maxExistingEffectsNumber = 0; targetLayers.forEach(function (targetLayer) { maxExistingEffectsNumber = Math.max(targetLayer.getLayerEffectsByType(effectType).size, maxExistingEffectsNumber); }); if (sourceEffectSize < maxExistingEffectsNumber) { for (var i = sourceEffectSize; i < maxExistingEffectsNumber; i++) { var deletedEffectIndexList = [], deletedEffectPropsList = []; for (var j = 0; j < layerIDs.size; j++) { // the index of the deleted effects are always the size of the effects in the source layer, // which is the index after the last effect. This is becuase the LayerStructure model updates // the effects index by index, and indexes may become invalid after a deletion. // // For example: // // 1 say we have layer effects [A, B, C] // 2 we want to delete the last two effects (because they don't exist in the source layer), // so the index may looks like [1 , 2] // 3 after layerstructure deletes the second effect (index = 1), the new layer // effects list become [A, C] // 4 when it tries to delete the third effect (index = 2), it will hit out-of-range error. // // For this case, a working index list should be [1, 1] deletedEffectIndexList.push(sourceEffectSize); deletedEffectPropsList.push(null); } effectIndexList = effectIndexList.push(Immutable.List(deletedEffectIndexList)); effectPropsMap = effectPropsMap.push(Immutable.List(deletedEffectPropsList)); } masterEffectIndexMap[effectType] = effectIndexList; masterEffectPropsMap[effectType] = effectPropsMap; } }); var payload = { documentID: document.id, layerIDs: layerIDs, layerEffectTypes: Immutable.List(masterEffectTypes), layerEffectIndex: Immutable.Map(masterEffectIndexMap), layerEffectProps: Immutable.Map(masterEffectPropsMap), coalesce: false, history: { newState: true, name: nls.localize("strings.ACTIONS.SET_LAYER_EFFECTS") } }; this.dispatchAsync(events.style.HIDE_HUD); // Synchronously update the stores this.dispatch(events.document.history.LAYER_EFFECTS_BATCH_CHANGED, payload); // Then update photoshop return _syncStoreToPs.call(this, document, targetLayers, false, masterEffectTypes, options); };
.reduce(function (reducedModel, property) { var values = collection.pluck(paragraphStyles, property); reducedModel[property] = collection.uniformValue(values); return reducedModel; }, {});