├── .eslintignore ├── docs ├── .nojekyll ├── index.html ├── en │ ├── LOGS.md │ ├── README.md │ └── TUTORIAL.md ├── LOGS.md ├── README.md └── TUTORIAL.md ├── .babelrc ├── screenshot ├── 1.PNG ├── 1.gif ├── 2.gif └── 1_en.PNG ├── .gitignore ├── src ├── index.js ├── mvvm │ ├── observer.js │ └── index.js ├── https │ ├── checkVersion.js │ └── index.js ├── ui │ ├── moveGroupWindow.js │ ├── moveItemWindow.js │ ├── previewProgress.js │ ├── presetWindow.js │ ├── outputGroupWindow.js │ ├── moduleWindow.js │ ├── rightClickMenu.js │ └── settingWindow.js ├── layer │ └── progressFactory.js ├── polyfill.js ├── startup.js ├── preset.js └── i18n │ └── index.js ├── lib ├── AutoSave.js ├── OperatorOverload.js ├── ReloadPic.js ├── UIParser.js ├── GridView.js └── Translate.js ├── package.json ├── README.md ├── .eslintrc.js ├── index.js └── LICENSE /.eslintignore: -------------------------------------------------------------------------------- 1 | dist -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-2"], 3 | "comments": false 4 | } -------------------------------------------------------------------------------- /screenshot/1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smallpath/memory/HEAD/screenshot/1.PNG -------------------------------------------------------------------------------- /screenshot/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smallpath/memory/HEAD/screenshot/1.gif -------------------------------------------------------------------------------- /screenshot/2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smallpath/memory/HEAD/screenshot/2.gif -------------------------------------------------------------------------------- /screenshot/1_en.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smallpath/memory/HEAD/screenshot/1_en.PNG -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | Sp_memory 4 | 5 | dist/* 6 | 7 | !dist/Sp_memory.jsx 8 | 9 | *.log -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | progressFactory: require('./layer/progressFactory'), 3 | previewProgress: require('./ui/previewProgress'), 4 | settingWindow: require('./ui/settingWindow'), 5 | fns: require('./ui/function') 6 | } 7 | -------------------------------------------------------------------------------- /src/mvvm/observer.js: -------------------------------------------------------------------------------- 1 | exports.observer = observer 2 | exports.isObj = isObj 3 | 4 | function isObj(obj) { 5 | return Object.prototype.toString.call(obj).slice(8, -1) === 'Object' 6 | } 7 | 8 | function isInBlackList(name, list) { 9 | for (var i = 0; i < list.length; i++) { 10 | if (list[i] === name) return true 11 | } 12 | return false 13 | } 14 | 15 | function observer(obj, callback, nameBlackList, index) { 16 | index = index || 0 17 | if (!isObj(obj)) return 18 | for (var i in obj) { 19 | if (index === 0 && isInBlackList(i, nameBlackList)) continue 20 | obj.watch(i, callback) 21 | if (isObj(obj[i])) observer(obj[i], callback, nameBlackList, index + 1) 22 | } 23 | return obj 24 | } 25 | -------------------------------------------------------------------------------- /lib/AutoSave.js: -------------------------------------------------------------------------------- 1 | // 批量自动保存每一层为新Item 2 | $.global.autoSave = autoSave 3 | function autoSave() { 4 | if (confirm(loc(sp.auto)) === false) return 5 | if (!(app.project.activeItem instanceof CompItem)) return alert(loc(sp.needComp)) 6 | if (!sp.droplist.selection) return 7 | 8 | try { 9 | var preRenameValue = sp.autoNameValue 10 | sp.autoNameValue = true 11 | for (var i = 0; i < app.project.activeItem.numLayers; i++) { 12 | for (var j = 1; j <= app.project.activeItem.numLayers; j++) { 13 | app.project.activeItem.layer(j).selected = false 14 | } 15 | app.project.activeItem.layer(i + 1).selected = true 16 | sp.fns.newItem() 17 | app.project.activeItem.layer(i + 1).selected = false 18 | } 19 | sp.autoNameValue = preRenameValue 20 | } catch (err) { } 21 | sp.droplist.notify('onChange') 22 | sp.gv.refresh() 23 | } 24 | -------------------------------------------------------------------------------- /lib/OperatorOverload.js: -------------------------------------------------------------------------------- 1 | function OperatorOverload(call, operator) { 2 | var meta = [ 3 | // Unary operator 4 | '+', '-', '~', 5 | // Binary operator 6 | '*', '/', '%', '^', '<', '<=', '==', '<<', '>>', '>>>', '&', '|', '===' 7 | ] 8 | var toObject = function() { 9 | for (var i = 0; i < arguments.length; i++) { this[arguments[i]] = true } 10 | return this 11 | } 12 | var metaObj = toObject.apply({}, meta) 13 | if (!metaObj.hasOwnProperty(operator)) { return alert('Operator not supported.') } 14 | 15 | this.call = call 16 | this[operator] = function(operand, rev) { 17 | this.call(operand, rev) 18 | return this 19 | } 20 | return this 21 | } 22 | 23 | var cout = $.global.cout = new OperatorOverload(function(operand, rev) { 24 | if (!rev) { $.writeln(operand) } else { alert(operand) } 25 | }, '<<') 26 | $.global.cout = cout 27 | -------------------------------------------------------------------------------- /src/mvvm/index.js: -------------------------------------------------------------------------------- 1 | var mvvm = require('./observer') 2 | 3 | var nameBlackList = [ 4 | 'win', // the parent window of grid view instance 5 | 'gv', // the grid view instance 6 | 'isOutside', // ensure mouse cursor is in image rect or not 7 | 'previewHelper', // helper object for preview feature 8 | 'isLoopPreview', // preview loop boolean value 9 | 'droplist', 10 | 'parentDroplist', 11 | 'menu' 12 | ] 13 | 14 | function watch(name, oldValue, newValue) { 15 | // $.writeln('name: ', name, ' oldValue: ', oldValue, ' newValue: ', newValue) 16 | if (typeof oldValue === 'function') { 17 | return oldValue 18 | } else if (typeof newValue === 'boolean') { 19 | var settingName = name.replace('Value', '') 20 | // $.writeln('write to ', settingName, ' has?: ', $.global.sp.haveSetting(name.replace('Value', ''))) 21 | if ($.global.sp.haveSetting(settingName)) { 22 | $.global.sp.saveSetting(settingName, newValue) 23 | return newValue 24 | } else { 25 | return oldValue 26 | } 27 | } else { 28 | return newValue 29 | } 30 | } 31 | 32 | module.exports = function(obj) { 33 | return mvvm.observer(obj, watch, nameBlackList) 34 | } 35 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Sp_memory - a script for adobe after effects to save any layers 6 | 7 | 8 | 9 | 10 | 11 | 15 |
16 | 17 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/https/checkVersion.js: -------------------------------------------------------------------------------- 1 | module.exports = function(win, isStarting) { 2 | clearOutput && clearOutput() 3 | var targetAlert = isStarting ? writeLn : alert 4 | return function() { 5 | var latestVersion = sp.getVersion() 6 | var nowVersion = sp.version 7 | var compare = sp.compareSemver(latestVersion, nowVersion) 8 | if (compare > 0) { 9 | targetAlert(loc(sp.newVersionFind) + latestVersion.toString()) 10 | var scriptLink = sp.downloadLinkPrefix + latestVersion + sp.downloadLinkSuffix 11 | if (confirm(loc(sp.shouldUpdateScript))) { 12 | try { 13 | var scriptString = sp.request( 14 | 'GET', 15 | scriptLink, 16 | '' 17 | ) 18 | var file = new File($.fileName) 19 | file.writee(scriptString) 20 | targetAlert(loc(sp.downloaded)) 21 | win.close() 22 | sp.win.close() 23 | } catch (err) { err.printa() } 24 | } else if (confirm(loc(sp.shouldDownloadScript))) { 25 | try { 26 | sp.openLink(scriptLink) 27 | } catch (err) { err.printa() } 28 | } 29 | } else if (compare === 0) { 30 | targetAlert(loc(sp.newVersionNotFind) + nowVersion.toString()) 31 | } else if (compare < 0) { 32 | targetAlert(loc(sp.tryVersionFind) + nowVersion.toString()) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /docs/en/LOGS.md: -------------------------------------------------------------------------------- 1 | ### 3.0 Date:2016-04-07 2 | - Add preview feature 3 | - Add module 4 | - New checkbox->Save preview 5 | - Fix CC2015 save preview error 6 | 7 | ### 2.2 Date:2016-03-20 8 | - Refactoring 9 | - Fixing expression error automatically 10 | - Add a new type of thumbnail 11 | - Fix memory leaks 12 | - Fix script freezing 13 | 14 | ### 2.1 Date:2015-11-05 15 | - Add shortcut for Palette-type open 16 | - Add checkbox of limiting text 17 | - Speed up 10% for saving layers 18 | 19 | ### 2.0 Date:2015-10-07 20 | - Add new UI 21 | - Add some options in Setting window 22 | - Support keyframe interpolation and roving 23 | - Support MaskIndex and LayerIndex property 24 | - Support ray-traced layer 25 | - Support check update 26 | - Support remember size and position of script open in Palette type 27 | - Fix some bugs 28 | 29 | ### 1.3 Date:2015-07-02 30 | - Support CC2015 31 | - Add auto-save feature 32 | - Add export groups feature 33 | - Fix some bugs 34 | 35 | ### 1.2 Date:2015-05-16 36 | - Support CS3,CS4,CS5 and CS5.5 37 | - New checkbox->Empty property 38 | - New checkbox->Offset keyframe 39 | - Fix some bugs 40 | 41 | ### 1.1 Date:2015-04-19 42 | - New checkbox->Save material 43 | - Add option for empty temp folder 44 | - Add option for rename group 45 | - Add button for rename item 46 | - Add option for change script language 47 | - Fix some bugs 48 | 49 | ### 1.0 Date:2015-04-13 50 | - Support Composition layer 51 | - Support CS6,CC and CC2014 52 | -------------------------------------------------------------------------------- /src/https/index.js: -------------------------------------------------------------------------------- 1 | var vbsString = `set namedArgs = WScript.Arguments.Named 2 | 3 | sMethod = namedArgs.Item("Method") 4 | sUrl = namedArgs.Item("URL") 5 | sRequest = namedArgs.Item("Query") 6 | 7 | HTTPPost sMethod, sUrl, sRequest 8 | 9 | Function HTTPPost(sMethod, sUrl, sRequest) 10 | 11 | set oHTTP = CreateObject("Microsoft.XMLHTTP") 12 | 13 | If sMethod = "POST" Then 14 | oHTTP.open "POST", sUrl,false 15 | ElseIf sMethod = "GET" Then 16 | oHTTP.open "GET", sUrl,false 17 | End If 18 | 19 | oHTTP.setRequestHeader "Content-Type", "application/x-www-form-urlencoded" 20 | oHTTP.setRequestHeader "Content-Length", Len(sRequest) 21 | oHTTP.send sRequest 22 | 23 | HTTPPost = oHTTP.responseText 24 | 25 | WScript.Echo HTTPPost 26 | 27 | End Function 28 | ` 29 | 30 | module.exports = function(method, endpoint, query) { 31 | var response = null 32 | 33 | var tempVbsFile = new File($.layer.tempFolder.toString() + $.layer.slash.toString() + 'curl.vbs') 34 | 35 | if (!tempVbsFile.exists) { 36 | tempVbsFile.writee(vbsString) 37 | } 38 | var wincurl = tempVbsFile.fsName 39 | var curlCmd = '' 40 | 41 | try { 42 | if (sp.os === 'win') { 43 | curlCmd = `cscript "${wincurl}" /Method:${method} /URL:${endpoint} /Query:${query} //nologo` 44 | } else { 45 | curlCmd = `curl -s -G -d "${query}" ${endpoint}` 46 | } 47 | response = system.callSystem(curlCmd) 48 | } catch (err) {} 49 | 50 | return response 51 | } 52 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "memory", 3 | "version": "3.1.0", 4 | "description": "a script for adobe after effects to save any layers", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "webpack --config ./build/webpack.config.js --watch --progress --hide-modules", 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "build": "cross-env NODE_ENV=production webpack --config ./build/webpack.config.js --progress --hide-modules", 10 | "lint": "eslint . --quiet", 11 | "fix": "eslint . --fix --quiet" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://smallpath@github.com/smallpath/memory.git" 16 | }, 17 | "keywords": [ 18 | "ae", 19 | "layer", 20 | "sp_memory" 21 | ], 22 | "author": "smallpath", 23 | "license": "Apache-2.0", 24 | "bugs": { 25 | "url": "https://github.com/smallpath/memory/issues" 26 | }, 27 | "homepage": "https://github.com/smallpath/memory#readme", 28 | "devDependencies": { 29 | "after-effects": "^0.4.11", 30 | "babel-core": "^6.24.0", 31 | "babel-eslint": "^7.2.1", 32 | "babel-loader": "^6.4.1", 33 | "babel-polyfill": "^6.23.0", 34 | "babel-preset-es2015": "^6.24.0", 35 | "babel-preset-stage-2": "^6.22.0", 36 | "cross-env": "^4.0.0", 37 | "eslint": "^3.19.0", 38 | "eslint-config-standard": "^10.2.0", 39 | "eslint-plugin-import": "^2.2.0", 40 | "eslint-plugin-node": "^4.2.2", 41 | "eslint-plugin-promise": "^3.5.0", 42 | "eslint-plugin-standard": "^3.0.1", 43 | "osascript": "^1.2.0", 44 | "webpack": "^2.3.3" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## memory 2 | 这是后期合成软件Adobe After Effects的层存储脚本,你可以将AE中任何层(包括合成层)保存下来,以便在其他工程中,在其他版本的AE中,甚至在其他电脑中生成新的层 3 | 4 | ## 文档 5 | - [中文文档](https://smallpath.github.io/memory) 6 | - [安装](https://smallpath.github.io/memory/#/?id=安装) 7 | - [English Document](https://smallpath.github.io/memory/#/en/) 8 | 9 | ## 脚本功能 10 | 1. 支持动态预览元素内容, 支持窗口大小自适应,同时拥有方便的右键菜单来节省界面空间 11 | 2. 支持一切层的存储与生成,包括形状层,文字层,图片音频层,甚至`合成层` 12 | 3. 支持几乎所有属性的存储与生成,包括层本身属性以及层内部属性组,例如插件,遮罩,文字动画器,形状效果器,图层样式等等 13 | 4. 支持任何素材层,例如图片,音频甚至是视频,即使他们被移动或删除,脚本也可以正确生成 14 | 5. 由语言版本不同造成的表达式报错将被自动修复,默认支持英文,中文,日文三种语言 15 | 6. 支持自定义预设,脚本提供插件,遮罩,动画器等9种属性组的自由搭配选项 16 | 7. 存储得到的数据兼容于AE任何版本,例如,用本脚本在CC2017上存储的一个工程,可以在CC上正确地生成 17 | 18 | ## 开发 19 | - [x] Node.js > v4.0 20 | - [x] Yarn.js 21 | 22 | ``` 23 | # 安装依赖 24 | yarn 25 | 26 | # 开发 27 | yarn run dev 28 | 29 | # 构建 30 | yarn run build 31 | ``` 32 | 33 | ## 感谢 34 | - 阿木亮([GridView.js](https://github.com/smallpath/memory/blob/master/lib/GridView.js),[UIParser.js](https://github.com/smallpath/memory/blob/master/lib/UIParser.js)) 35 | - 水果硬糖([UIParser.js](https://github.com/smallpath/memory/blob/master/lib/UIParser.js)) 36 | 37 | ## License 38 | ``` 39 | Copyright (C) 2015 smallpath 40 | 41 | Licensed under the Apache License, Version 2.0 (the "License"); 42 | you may not use this file except in compliance with the License. 43 | You may obtain a copy of the License at 44 | 45 | http://www.apache.org/licenses/LICENSE-2.0 46 | 47 | Unless required by applicable law or agreed to in writing, software 48 | distributed under the License is distributed on an "AS IS" BASIS, 49 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 50 | See the License for the specific language governing permissions and 51 | limitations under the License. 52 | ``` -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: 'babel-eslint', 4 | parserOptions: { 5 | sourceType: 'module' 6 | }, 7 | // https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style 8 | extends: 'standard', 9 | globals: { 10 | "sp": true, 11 | "$": true, 12 | "Panel": true, 13 | "Folder": true, 14 | "GridView": true, 15 | "Window": true, 16 | "app": true, 17 | "File": true, 18 | "prompt": true, 19 | "alert": true, 20 | "XML": true, 21 | "confirm": true, 22 | "loc": true, 23 | "CompItem": true, 24 | "ScriptUI": true, 25 | "Socket": true, 26 | "ImportOptions": true, 27 | "ImportAsType": true, 28 | "cout": true, 29 | "clearOutput": true, 30 | "writeLn": true, 31 | "PropertyType": true, 32 | "Language": true, 33 | "system": true, 34 | "TextLayer": true, 35 | "LightLayer": true, 36 | "ShapeLayer": true, 37 | "AVLayer": true, 38 | "SolidSource": true, 39 | "FileSource": true, 40 | "CameraLayer": true, 41 | "Shape": true, 42 | "KeyframeEase": true, 43 | "PropertyValueType": true, 44 | "MarkerValue": true, 45 | "PurgeTarget": true, 46 | "memoryGlobal": true 47 | }, 48 | // add your custom rules here 49 | 'rules': { 50 | // allow paren-less arrow functions 51 | 'arrow-parens': 0, 52 | // allow async-await 53 | 'generator-star-spacing': 0, 54 | 'space-before-function-paren': ['error', 'never'], 55 | 'no-return-assign': 0, 56 | 'no-extend-native': 'off', 57 | 'no-unused-expressions': 'off', 58 | 'no-caller': 'off', 59 | 'no-mixed-operators': 'off', 60 | 'no-template-curly-in-string': 'off', 61 | 'new-cap': 'off', 62 | 'no-multi-str': 'off', 63 | 'no-useless-escape': 'off', 64 | // allow debugger during development 65 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/ReloadPic.js: -------------------------------------------------------------------------------- 1 | /** ***********************************自动重载图片**************************************/ 2 | $.global.reloadPic = reloadPic 3 | function reloadPic() { 4 | var thisComp = app.project.activeItem 5 | if (!(thisComp instanceof CompItem)) return alert(loc(sp.needComp)) 6 | if (!sp.droplist.selection) return 7 | 8 | if (confirm(loc(sp.refresh)) === false) return 9 | 10 | var frames = prompt(loc(sp.reloadNeedFrames), '') 11 | var shouldLimit = true 12 | if (!frames || isNaN(frames)) shouldLimit = false 13 | if (frames === '') shouldLimit = false 14 | 15 | try { 16 | frames = parseInt(frames) 17 | var SpeciallimitTime = frames / thisComp.frameRate 18 | } catch (err) { } 19 | 20 | var preRenameValue = sp.autoNameValue 21 | sp.autoNameValue = true 22 | var preCompValue = sp.preComposeValue 23 | sp.preComposeValue = false 24 | 25 | for (var i = 0; i < sp.gv.children.length; i++) { 26 | try { 27 | sp.gv.children[i].selected = true 28 | 29 | try { 30 | sp.gv.children[i - 1].selected = false 31 | } catch (err) { } 32 | 33 | sp.gv.lastSelectedItem = sp.gv.children[i] 34 | 35 | var layerArr = sp.fns.newLayer() 36 | var j 37 | for (j = 0; j < thisComp.selectedLayers.length; j++) { 38 | thisComp.selectedLayers[j].selected = false 39 | } 40 | 41 | if (!layerArr) continue 42 | 43 | if (!(layerArr instanceof Array)) layerArr = [layerArr] 44 | 45 | for (j = 0; j < layerArr.length; j++) { 46 | layerArr[j].selected = true 47 | 48 | if (shouldLimit === false) continue 49 | 50 | if (layerArr[j].outPoint > SpeciallimitTime) { 51 | layerArr[j].outPoint = SpeciallimitTime 52 | } 53 | } 54 | 55 | sp.fns.cover() 56 | 57 | for (j = layerArr.length - 1; j >= 0; j--) { 58 | layerArr[j].remove() 59 | } 60 | 61 | sp.gv.children[i].selected = false 62 | } catch (err) { alert(err.line.toString() + '\r' + err.toString()) } 63 | } 64 | 65 | sp.autoNameValue = preRenameValue 66 | sp.preComposeValue = preCompValue 67 | sp.gv.refresh() 68 | } 69 | -------------------------------------------------------------------------------- /docs/LOGS.md: -------------------------------------------------------------------------------- 1 | ### 3.1 Date:soon 2 | ## 优化 3 | - 更换打包工具以提供直观的报错定位 4 | - 支持存储视频, 去除素材的大小限制 5 | - 生成层进度条 6 | - 存储层进度条 7 | - 进度条显示脚本耗时 8 | - 存储预览进度条 9 | - 优化预览CPU占用 10 | - 生成单个预合成时直接拉伸至当前合成大小 11 | - 增加允许截取工作区预览的检测框 12 | - 修复检查更新功能 13 | - 增加自动更新功能 14 | - 增加windows缩放比例参数 15 | 16 | ## 漏洞修复 17 | - 修复音频层关键帧未生成的问题 18 | - 修复windows缩放比例不为1时的界面越界问题 19 | - 修复界面中一些特殊文字的错位问题 20 | - 修复windows禁止字符导致预览存储失败的问题 21 | - 修复最小化时关掉脚本导致的脚本大小归零的问题 22 | - 修复windows特殊字符串导致的模块,组以及元素生成失败的问题 23 | - 修复mac CC2017中表达式翻译无法使用的问题 24 | - 修复setInterpolationTypeAtKey的关键帧生成报错 25 | - 修复非1080p的右键菜单越界的问题 26 | 27 | ### 3.0 Date:2016-04-07 28 | 29 | [发布地址](http://tieba.baidu.com/p/4462854806) 30 | 31 | - 添加预览动画 32 | - 界面支持预览动画的播放 33 | - 添加组的分类-模块 34 | - 修正CC2015无法裁剪缩略图的问题 35 | 36 | ### 2.2 Date:2016-03-20 37 | - 重构代码 38 | - 生成时将默认翻译表达式 39 | - 添加一种新类型的缩略图,支持CS6-CC2014 40 | - 生成加速5% 41 | - 解决内存泄漏的问题 42 | - 解决脚本停止运行的问题 43 | - 修正win10下右侧滑块不能下拉到最下面的问题 44 | - 修正检测框状态保存失败的问题 45 | - 修正老界面删除组失败的相关问题 46 | - 修正存储时相同名字导致的图片更新不及时的问题 47 | 48 | ### 2.1 Date:2015-11-05 49 | - 添加组级别的出错处理 50 | - 非Panel模式打开脚本时添加快捷键 51 | - 添加裁剪新界面上文字的长度的选项 52 | - 存储过程加速10% 53 | 54 | ### 2.0 Date:2015-10-07 55 | 56 | [发布地址](http://tieba.baidu.com/p/4087269643) 57 | 58 | - 添加新界面(支持CC,CC2014,CC2015),支持图片自动排列,支持多选 59 | - 添加一些设置选项 60 | - 添加关键帧插值与roving的支持 61 | - 添加LayerIndex与MaskIndex类型的属性的支持 62 | - 添加光线追踪层的支持 63 | - 添加检查更新的功能 64 | - 添加记忆脚本窗口位置与大小的功能 65 | - 修正一些BUG 66 | 67 | ### 1.3 Date:2015-07-02 68 | 69 | [发布地址](http://tieba.baidu.com/p/3866311869) 70 | 71 | - 添加AE CC2015的支持 72 | - 添加快速存储辅助脚本 73 | - 添加批量导出功能 74 | - 修正一些BUG 75 | 76 | ### 1.2 Date:2015-05-16 77 | 78 | [发布地址](http://tieba.baidu.com/p/3766894868) 79 | 80 | - 添加AE CS3,CS4,CS5,CS5.5的支持 81 | - 新增第五检测框,关键帧偏移检测框,并在右键菜单中新增其选项 82 | - 新增第六检测框,清空层内容检测框 83 | - 新增预设功能 84 | - 修正一些BUG 85 | 86 | ### 1.1 Date:2015-04-19 87 | 88 | [发布地址](http://tieba.baidu.com/p/3711493077) 89 | 90 | - 启用第四检测框,存储素材 91 | - 在设置菜单中添加清空临时文件夹的快捷按钮 92 | - 在设置菜单中增加重命名选中组的按钮 93 | - 在右键菜单中增加重命名选中元素的按钮 94 | - 在设置菜单中增加中英文界面切换的按钮 95 | - 修正一些BUG 96 | 97 | ### 1.0 Date:2015-04-13 98 | 99 | [发布地址](http://tieba.baidu.com/p/3699068264) 100 | 101 | - 新增合成层支持 102 | - 添加AE CS6,CC,CC2014的支持 103 | -------------------------------------------------------------------------------- /src/ui/moveGroupWindow.js: -------------------------------------------------------------------------------- 1 | var settingsButtonFunc = require('./settingWindow') 2 | 3 | module.exports = function(xmlItem, groupItem, win) { 4 | var moveWin = new Window('dialog', 'Move', undefined, { 5 | resizeable: 0, 6 | maximizeButton: 0 7 | }) 8 | var outRes = `Group{ 9 | orientation: 'column', alignment:['fill', 'fill'], alignChildren:['fill', 'fill'],\ 10 | wlist:ListBox{properties:{multiselect:0}}, 11 | oc:Group{ 12 | alignment:['fill', 'fill'], alignChildren:['fill', 'fill'], 13 | ok:Button{text:'` + loc(sp.ok) + `'}, 14 | cancel:Button{text:'` + loc(sp.cancel) + `'} 15 | } 16 | }` 17 | try { 18 | outRes = moveWin.add(outRes) 19 | } catch (err) { 20 | alert(err) 21 | } 22 | sp.xmlGroupNames.forEach(function(item, index) { 23 | this.add('item', item) 24 | }, outRes.wlist) 25 | 26 | outRes.oc.cancel.onClick = function() { 27 | moveWin.close() 28 | win.close() 29 | settingsButtonFunc() 30 | } 31 | 32 | outRes.oc.ok.onClick = function() { 33 | if (!outRes.wlist.selection) return 34 | if (outRes.wlist.selection.text === groupItem.text) return 35 | var xml = new XML(sp.settingsFile.readd()) 36 | var parentGroup = xml.ParentGroup 37 | var xmlIndex = xmlItem.index 38 | var groupIndex = groupItem.index 39 | 40 | var editXml = parentGroup.child(groupIndex).child(xmlIndex) 41 | var targetXml = parentGroup.child(outRes.wlist.selection.index) 42 | targetXml.appendChild(new XML(editXml)) 43 | 44 | parentGroup.child(groupIndex).child(xmlIndex).setLocalName('waitToDelete') 45 | delete parentGroup.child(groupIndex).waitToDelete 46 | sp.settingsFile.writee(xml) 47 | 48 | sp.reloadParentDroplist() 49 | var selection = parseInt(sp.getSetting('parentSelection')) 50 | sp.parentDroplist.selection = (selection <= sp.parentDroplist.items.length - 1 && selection >= 0) ? selection : 0 51 | selection = parseInt(sp.getSetting('thisSelection')) 52 | sp.droplist.selection = (selection <= sp.droplist.items.length - 1 && selection >= 0) ? selection : 0 53 | 54 | moveWin.close() 55 | win.close() 56 | settingsButtonFunc() 57 | } // last 58 | 59 | outRes.wlist.size = [200, 300] 60 | moveWin.show() 61 | } 62 | -------------------------------------------------------------------------------- /src/layer/progressFactory.js: -------------------------------------------------------------------------------- 1 | var global = $.global 2 | 3 | var parentProgress = require('../ui/previewProgress') 4 | 5 | var progressFactory = { 6 | createWindow: function(len, title, prefixString, suffixString) { 7 | if (global.progressWin) { 8 | global.progressBar.maxvalue = len 9 | return 10 | } 11 | parentProgress.createWindow(len, title, prefixString, suffixString) 12 | }, 13 | update: function(len, prefixString, suffixString, timePrefix, timeSuffix) { 14 | parentProgress.update(len, prefixString, suffixString, timePrefix, timeSuffix) 15 | }, 16 | complete: function(timePrefix, timeSuffix) { 17 | parentProgress.complete(timePrefix, timeSuffix) 18 | global.progressWin = null 19 | global.progressTimeText = null 20 | global.progressText = null 21 | global.progressBar = null 22 | } 23 | } 24 | 25 | var timeSuffix = loc(sp.second) 26 | // saving process window 27 | var savingReport = loc(sp.savingReport) 28 | var savingPrefixString = loc(sp.savingProcessingPrefix) 29 | var savingSuffixString = loc(sp.savingProcessAfter) 30 | var savingTitle = loc(sp.savingProcessTitle) 31 | $.layer.willSaveLayers = function(layers) { 32 | var len = $.layer.countLayers(layers, true) 33 | progressFactory.createWindow( 34 | len, 35 | savingTitle, 36 | savingPrefixString, 37 | savingSuffixString 38 | ) 39 | } 40 | $.layer.didSaveLayer = function(count) { 41 | progressFactory.update( 42 | count, 43 | savingPrefixString, 44 | savingSuffixString, 45 | savingReport, 46 | timeSuffix 47 | ) 48 | } 49 | $.layer.didSaveLayers = function() { 50 | $.layer.didSaveLayer(0) 51 | progressFactory.complete(savingReport, timeSuffix) 52 | } 53 | 54 | // generating process window 55 | var creatingReport = loc(sp.creatingReport) 56 | var creatingPrefixString = loc(sp.creatingProcessingPrefix) 57 | var creatingSuffixString = loc(sp.creatingProcessAfter) 58 | var creatingTitle = loc(sp.creatingProcessTitle) 59 | 60 | $.layer.willCreateLayers = function(len) { 61 | progressFactory.createWindow( 62 | len, 63 | creatingTitle, 64 | creatingPrefixString, 65 | creatingSuffixString 66 | ) 67 | } 68 | $.layer.didCreateLayer = function(count) { 69 | progressFactory.update( 70 | count, 71 | creatingPrefixString, 72 | creatingSuffixString, 73 | creatingReport, 74 | timeSuffix 75 | ) 76 | } 77 | $.layer.didCreateLayers = function() { 78 | $.layer.didCreateLayer(0) 79 | progressFactory.complete(creatingReport, timeSuffix) 80 | } 81 | 82 | module.exports = progressFactory 83 | -------------------------------------------------------------------------------- /src/polyfill.js: -------------------------------------------------------------------------------- 1 | module.exports = (function() { 2 | require('lib/OperatorOverload') 3 | 4 | sp.extend(sp, { 5 | forEach: function(xml, callback, context) { 6 | if (!(xml instanceof XML)) return 7 | var i, 8 | len 9 | for (i = 0, len = xml.children().length(); i < len; i++) { 10 | if (callback.call(context, xml.child(i), i, xml) === false) { 11 | break 12 | } 13 | } 14 | } 15 | }) 16 | 17 | String.prototype.trim = String.prototype.trim || function() { 18 | return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '') 19 | } 20 | 21 | Array.prototype.includes = function(value) { 22 | for (var i = 0, len = this.length; i < len; i++) { 23 | if (this[i] === value) { 24 | return true 25 | } 26 | } 27 | return false 28 | } 29 | 30 | Array.prototype.forEach = function(callback, context) { 31 | if (Object.prototype.toString.call(this) === '[object Array]') { 32 | var i, len 33 | for (i = 0, len = this.length; i < len; i++) { 34 | if (typeof callback === 'function' && Object.prototype.hasOwnProperty.call(this, i)) { 35 | if (callback.call(context, this[i], i, this) === false) { 36 | break 37 | } 38 | } 39 | } 40 | } 41 | } 42 | 43 | Error.prototype.print = Error.prototype.print || function() { 44 | return 'Line #' + this.line.toString() + '\r\n' + this.toString() 45 | } 46 | 47 | Error.prototype.printc = Error.prototype.printc || function() { 48 | cout << '\n---------' 49 | cout << this.print() 50 | cout << '---------\n' 51 | } 52 | 53 | Error.prototype.printa = Error.prototype.printa || function() { 54 | this.print() << cout 55 | } 56 | 57 | File.prototype.writee = function(str) { // method to write file 58 | this.open('w') 59 | this.write(str) 60 | this.close() 61 | } 62 | 63 | File.prototype.readd = function() { // method to read from file 64 | this.open('r') 65 | var temp = this.read() 66 | this.close() 67 | return temp 68 | } 69 | 70 | Array.prototype.pushh = function(str) { // chains call for Array.push() 71 | this.push(str) 72 | return this 73 | } 74 | 75 | sp.deleteThisFolder = function(folder) { 76 | var waitClFile = folder.getFiles() 77 | for (var i = 0; i < waitClFile.length; i++) { 78 | if (waitClFile[i] instanceof Folder) { 79 | sp.deleteThisFolder(waitClFile[i]) 80 | waitClFile[i].remove() 81 | } else { 82 | waitClFile[i].remove() 83 | } 84 | } 85 | } 86 | })() 87 | -------------------------------------------------------------------------------- /src/startup.js: -------------------------------------------------------------------------------- 1 | module.exports = (function() { 2 | if (!(sp.settingsFile.exists) || sp.settingsFile.length === 0) { 3 | if (sp.settingsFile.exists) sp.settingsFile.remove() 4 | var settingsText = 5 | '\ 6 | \ 7 | \ 8 | ' 9 | var newsettingsxml = new XML(settingsText) 10 | var allFiles = sp.scriptFolder.getFiles() 11 | newsettingsxml.ParentGroup.appendChild(new XML("")) 12 | var i = 0 13 | allFiles.forEach(function(item, index) { 14 | if (item.toString().indexOf('.xml') !== -1 && item.name.indexOf('settings.xml') === -1) { 15 | newsettingsxml.ListItems.appendChild(new XML('' + item.displayName.replace('.xml', '') + '')) 16 | newsettingsxml.ParentGroup.child(0).appendChild(new XML('' + i + '')) 17 | i++ 18 | } 19 | }) 20 | sp.settingsFile.writee(newsettingsxml) 21 | } 22 | 23 | // If the file do not have the ParentGroup,add parentGroup to it 24 | var content = new XML(sp.settingsFile.readd()) 25 | if (!content.hasOwnProperty('ParentGroup')) { content.appendChild(new XML('')) } 26 | if (content.ParentGroup.children().length() === 0) { 27 | content.ParentGroup.appendChild(new XML("")) 28 | sp.forEach(content.ListItems, function(item, index) { 29 | content.ParentGroup.child(0).appendChild(new XML('' + index.toString() + '')) 30 | }) 31 | sp.settingsFile.writee(content) 32 | } 33 | 34 | // If the file do not have a group,give it 35 | content = new XML(sp.settingsFile.readd()) 36 | if (!content.hasOwnProperty('ListItems')) { content.appendChild(new XML('')) } 37 | if (content.ListItems.children().length() === 0) { 38 | allFiles = sp.scriptFolder.getFiles() 39 | allFiles.forEach(function(item, index) { 40 | if (item.toString().indexOf('.xml') !== -1 && item.name.indexOf('settings.xml') === -1) { 41 | content.ListItems.appendChild(new XML('' + item.displayName.replace('.xml', '') + '')) 42 | content.ParentGroup.child(0).appendChild(new XML('' + index.toString() + '')) 43 | } 44 | }) 45 | } 46 | if (content.ListItems.children().length() === 0) { 47 | content.ListItems.appendChild(new XML('Default')) 48 | content.ParentGroup.child(0).appendChild(new XML('' + 0 + '')) 49 | var file = sp.getFileByName('Default') 50 | sp.getImageFolderByName('Default') 51 | var str = '' 52 | file.writee(str) 53 | } 54 | 55 | sp.settingsFile.writee(content) 56 | })() 57 | -------------------------------------------------------------------------------- /docs/en/README.md: -------------------------------------------------------------------------------- 1 | ## memory 2 | This is a script for Adobe After Effects.It can save any layer even `composition layer` in AE ,so you can create these layers in another project,another version of ae and even another computer 3 | 4 | ## Support 5 | AE CC,CC2014,CC2015 and CC2017 on Windows system 6 | 7 | ## User Interface 8 | memory supports preview animation,what you have watched is what it will be after generating layers 9 | ![screenshot7](https://raw.githubusercontent.com/smallpath/memory/master/screenshot/1.gif) 10 | ![screenshot8](https://raw.githubusercontent.com/smallpath/memory/master/screenshot/2.gif) 11 | ![screenshot1](https://raw.githubusercontent.com/smallpath/memory/master/screenshot/1_en.PNG) 12 | Script layouts automatically according to window size.Moreover,it has right-click menu to reduce space for you 13 | 14 | ## Script Feature 15 | 1. Supporting preview element, you can set the frame numbers and frame rate of preview animation. 16 | 2. Supporting any layer, such as Shape layer,Text layer,Image layer,Music layer and even `Composition layer` 17 | 3. Supporting any property,including Plugin,Mask,Text Animator,Shape Effector,Layer Style and so on. 18 | 4. Supporting storing image and music.Even if the material has been removed, it can be generated correctly by memory 19 | 5. Supporting fixing the expression error caused by using different launguage of AE.Lauguage supported includes English,Chinese,Japanese and ADBE. 20 | 6. Supporting preset which has 9 types of PropertyGroup such as Plugin,Mask,Text Animator and Transform. 21 | 7. Supporting cross-version.For example, if you save a composition layer in AE CC2015,memory can generate that layer perfectly in AE CS4 22 | 23 | ## Installation 24 | Go to [release page](https://github.com/smallpath/memory/releases).Download the latest version by clicking `Sp_memory.zip` 25 | Extract the `Sp_memory.jsxbin` to your AE script folder,something like `Support Files\Scripts\ScriptUI Panels` 26 | If your windows is win8 or win10, make sure AE is running under the administrator access 27 | In AE, go to `Edit->Preferences->General`,make sure `Allow scripts to write File and access Network` is checked 28 | Open script from `Window->Sp_memory.jsxbin` 29 | 30 | !> The default language is not English. To force English, just add a `force_en.txt` file in `Sp_memory` folder and restart script. 31 | 32 | ## Usage 33 | >[Usage](en/TUTORIAL.md) 34 | 35 | 36 | ## ChangeLog 37 | >[Change Log](en/LOGS.md) 38 | 39 | 40 | ## Development 41 | - [x] Node.js > v4.0 42 | - [x] Yarn.js 43 | 44 | ``` 45 | # install 46 | yarn 47 | 48 | # development 49 | yarn run dev 50 | 51 | # build 52 | yarn run build 53 | ``` 54 | 55 | ## Feedback 56 | If you encounter any problems or have any feedback, please open an issue. 57 | -------------------------------------------------------------------------------- /src/ui/moveItemWindow.js: -------------------------------------------------------------------------------- 1 | 2 | var upAndDown = function(isUp, isW) { 3 | var file = sp.getFileByName(sp.droplist.selection.text) 4 | var xml = new XML(file.readd()) 5 | if (isUp === true && sp.gv.lastSelectedItem !== null && sp.gv.lastSelectedItem.index > 0) { 6 | var upxml = new XML(xml.child(sp.gv.lastSelectedItem.index)) 7 | xml.insertChildBefore(xml.child(sp.gv.lastSelectedItem.index - 1), upxml) 8 | xml.child(sp.gv.lastSelectedItem.index + 1).setLocalName('waitToDelete') 9 | delete xml.waitToDelete 10 | file.writee(xml) 11 | sp.gv.lastSelectedItem.moveUp() 12 | } else if (isUp === false && sp.gv.lastSelectedItem !== null && sp.gv.lastSelectedItem.index < xml.children().length() - 1) { 13 | var downxml = new XML(xml.child(sp.gv.lastSelectedItem.index)) 14 | xml.insertChildAfter(xml.child(sp.gv.lastSelectedItem.index + 1), downxml) 15 | xml.child(sp.gv.lastSelectedItem.index).setLocalName('waitToDelete') 16 | delete xml.waitToDelete 17 | file.writee(xml) 18 | sp.gv.lastSelectedItem.moveDown() 19 | } 20 | } 21 | 22 | module.exports = function(cu) { 23 | var udWin = new Window('palette', loc(sp.ud)) 24 | var udWins = udWin.add('Group{}') 25 | var a = udWins.add("Button{text:'" + loc(sp.up) + "'}") 26 | var b = udWins.add("Button{text:'" + loc(sp.down) + "'}") 27 | var c = udWins.add("Group{et:EditText{text:'0',characters:3,justify:'center'},j:Button{text:'" + loc(sp.jmp) + "'}}") 28 | udWin.frameLocation = cu 29 | udWin.show() 30 | a.onClick = function() { 31 | upAndDown(true, true) 32 | } 33 | b.onClick = function() { 34 | upAndDown(false, true) 35 | } 36 | c.j.onClick = function() { 37 | var d = parseInt(c.et.text) 38 | var file = sp.getFileByName(sp.droplist.selection.text) 39 | var xml = new XML(file.readd()) 40 | if (sp.gv.children.length === 0) return 41 | if (sp.gv.lastSelectedItem === null) return 42 | if (d >= 0 && d < sp.gv.children.length - 1 && sp.gv.lastSelectedItem.index !== d) { 43 | var upxml = new XML(xml.child(sp.gv.lastSelectedItem.index)) 44 | xml.insertChildBefore(xml.child(d), upxml) 45 | xml.child(sp.gv.lastSelectedItem.index + 1).setLocalName('waitToDelete') 46 | delete xml.waitToDelete 47 | file.writee(xml) 48 | sp.gv.lastSelectedItem.moveBefore(sp.gv.children[d]) 49 | } else if (d === sp.gv.children.length - 1 && sp.gv.lastSelectedItem.index !== d) { 50 | upxml = new XML(xml.child(sp.gv.lastSelectedItem.index)) 51 | xml.insertChildAfter(xml.child(d), upxml) 52 | xml.child(sp.gv.lastSelectedItem.index + 1).setLocalName('waitToDelete') 53 | delete xml.waitToDelete 54 | file.writee(xml) 55 | sp.gv.lastSelectedItem.moveAfter(sp.gv.children[d]) 56 | } else { 57 | try { 58 | alert(loc(sp.from) + '~' + (sp.gv.children.length - 1).toString()) 59 | } catch (er) { } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ## memory 2 | 这是后期合成软件Adobe After Effects的层存储脚本,你可以将AE中任何层(包括合成层)保存下来,以便在其他工程中,在其他版本的AE中,甚至在其他电脑中生成新的层 3 | 4 | ## 环境要求 5 | Windows系统, AE版本为CC,CC2014,CC2015,CC2017 6 | 7 | ## 界面演示 8 | 支持动态预览元素内容, 支持窗口大小自适应, 同时拥有方便的**右键菜单**来节省界面空间 9 | ![screenshot7](https://raw.githubusercontent.com/smallpath/memory/master/screenshot/1.gif) 10 | ![screenshot8](https://raw.githubusercontent.com/smallpath/memory/master/screenshot/2.gif) 11 | ![screenshot1](https://raw.githubusercontent.com/smallpath/memory/master/screenshot/1.PNG) 12 | 13 | ## 功能 14 | 1. 脚本界面支持预览动画,可以设置预览动画的帧率和帧数 15 | 2. 支持一切层,包括形状层,文字层,图片音频层,甚至`合成层` 16 | 3. 支持一切属性,包括层本身属性以及层内部属性组,例如插件,遮罩,文字动画器,形状效果器,图层样式等等 17 | 4. 支持图片和音频,即使他们被移动或删除,脚本也可以正确生成 18 | 5. 由语言版本不同造成的表达式报错将被自动修复,支持英文,中文,日文三种语言 19 | 6. 支持自定义预设,脚本提供插件,遮罩,动画器等9种属性组的自由搭配选项 20 | 7. 存储得到的数据兼容于AE任何版本,例如,用本脚本在CC2015上存储的一个工程,可以在CC上正确地生成 21 | 22 | ## 安装 23 | 进入 [版本页面](https://github.com/smallpath/memory/releases), 下载最新版本的`Sp_memory.zip` 24 | 请将`Sp_memory.jsx`脚本放置在你的AE脚本文件夹中, 通常在`path\to\ae\Support Files\Scripts\ScriptUI Panels` 25 | 在AE中打开`窗口`菜单中的`Sp_memory.jsx`即可 26 | 27 | !> 下载下来的压缩包中包含三个拥有预览动画的素材包用于演示, 请在解压后, 在脚本界面上`右键->导入组`并全选素材文件 28 | 29 | ## 常见使用问题 30 | 31 | ### 版本相关 32 | - memory推荐的AE版本是? 33 | - AE CC2017, 它存储预览的速度是之前的十倍以上, 很适合经常存储素材的用户 34 | - CC2015无法使用表达式翻译,怎样解决? 35 | - AE CC2015砍掉了脚本权限, 所有依赖表达式的脚本均无法工作, 建议使用其他版本 36 | - v3.1版本的进度条在windows上几秒钟就停止响应了,如何解决? 37 | - 这与脚本以及AE无关, 而是由windows自行控制, 提高windows注册表中的hungAppTimeout的值即可 38 | - 脚本可以成功运行在mac平台的AE上, 为什么文档中却说不支持mac平台? 39 | - mac版AE的性能非常弱, 会在文件大于50M时出现磁盘I/O断崖式下跌的现象, 导致脚本停止响应 40 | - 详细的数据是, 7M文件只需7秒, 70M文件则需要7分钟, 100M的文件半个小时也无法写完. 41 | - 因此, 不建议在mac上使用本脚本 42 | - 脚本如何升级? 43 | - 右键v3.1脚本, 设置中**检查更新**即可, 建议经常使用脚本的用户将**启动时检查更新**也勾选上 44 | - 如果没有找到设置中的**启动时检查更新**, 那么说明你使用的并不是最新的脚本 45 | 46 | ### 错误相关 47 | - 弹窗中错误提示数字看不懂, 代表什么意思? 48 | - 首先, v3.1将错误暴露给了用户, 打开源脚本查看对应行数的代码即可, 非最新版本的报错将不会被处理 49 | - 另外, `Sp_memory/tempFile/error.txt`包含脚本的运行时错误, 请在上报错误时一并提交文件内容 50 | - 脚本无法新建组与保存层,错误代码提示1251 51 | - win8-win10用户请使用管理员权限运行AE 52 | - 如果是首次使用脚本,请在AE中打开`编辑->预选项->一般`,勾选`允许脚本访问文件与网络` 53 | - AE CC2015.3 使用Memory无法存储, 错误代码提示1321 54 | - memory v3.0不向上兼容, 只有经过测试的版本才会加上支持. 开发版本已经添加了CC2015.3, 请查看脚本安装部分进行安装 55 | 56 | ### 使用相关 57 | - 手动存储每一个合成非常麻烦, 有更好的办法吗? 58 | - 可以通过`右键->自动存储每一层`功能来批量存储`Motion Graphics`合成层 59 | - 我存储的素材如何备份? 60 | - 直接备份脚本同目录的`Sp_memory`文件夹即可 61 | - v3版本无法预览之前存储的元素,应该怎样解决? 62 | - `右键->重载组内预览动画`,即可进行预览动画的生成 63 | - v2支持CS3至CC2017,为什么v3只支持CC至CC2017? 64 | - 因为预览特性对AE环境非常苛刻,目前只有CC及以上版本能够通过测试 65 | - 但是,v3存储的元素,一样能够正确导出到v2中并进行层的生成 66 | - 是否有更多的`Motion Graphics`素材包以供下载? 67 | - 因为版权原因, 只提供三个素材包作为示范, memory只作为一个存储生成层的平台, 不提供原始素材包 68 | 69 | ## 脚本使用教程 70 | >[文字教程](TUTORIAL.md) 71 | 72 | ## 版本更新记录 73 | >[更新历史](LOGS.md) 74 | 75 | ## 开发 76 | - [x] Node.js > v4.0 77 | - [x] Yarn.js 78 | 79 | ``` 80 | # 安装依赖 81 | yarn 82 | 83 | # 开发 84 | yarn run dev 85 | 86 | # 构建 87 | yarn run build 88 | ``` 89 | windows上开发时, 建议对AE窗口使用`ctrl + \`快捷键, 否则自动刷新每次都会缩放AE窗口 90 | 91 | ## 反馈 92 | 脚本使用中遇到任何问题, 请[新开issue](https://github.com/smallpath/memory/issues/new), 或联系smallpath2013@gmail.com 93 | -------------------------------------------------------------------------------- /docs/TUTORIAL.md: -------------------------------------------------------------------------------- 1 | ## 打开脚本 2 | 3 | #### Panel模式 4 | 将脚本复制至AE的ScriptUI Panels文件夹,重启AE后,在AE的窗口栏中打开Sp_memory.jsxbin 5 | 6 | #### 非Panel模式 7 | 点击`AE->文件->脚本->打开脚本`,打开Sp_memory.jsxbin即可. 8 | 非Panel模式打开时,脚本支持快捷键,具体快捷键信息在设置窗口中. 9 | 10 | ## 元素 11 | 脚本面板中,可以被选中的之后统称为元素 12 | 13 | ## 保存层 14 | 15 | `右键->新建元素`,即可保存AE中已被选中的层. 16 | 如果右键菜单中的'存储预览被打开',则脚本将存储选中层的预览动画,范围为选中层的最小入点到最大出点 17 | 快捷键为:脚本面板中进行`Ctrl+右键` 或者 `Alt+右键` 18 | 19 | ## 生成层 20 | `右键->生成层`,即可在当前合成中生成新层 21 | 快捷键为:双击元素 22 | 23 | ## 覆盖元素 24 | 选中元素后,在选中了层的情况下,`右键->覆盖元素`,即可用选中层的信息覆盖选中元素的内容 25 | 26 | ## 移动元素 27 | `Shift+右键`,即可弹出元素移动的窗口 28 | 29 | ## 批量存储 30 | `右键->辅助脚本->自动存储每一层`,可将当前合成的每一层都单独存储为一个元素 31 | 十分适合存储MG合成层 32 | 33 | ## 右键菜单的检测框 34 | ``` 35 | 一般: 36 | 显示文字: 是否显示元素的名称 37 | 38 | 保存层: 39 | 自动取名: 保存层为元素时,是否自动给元素取名 40 | 存储预览: 保存层时,是否存储层的预览动画 41 | 存储素材: 保存层时,是否存储图片和音频.存储素材之后,如果原图片和音频被删除,脚本将会自动生成图片和音频 42 | 43 | 生成层: 44 | 预合成: 生成层时,是否将生成的所有层进行预合成 45 | 仅生成效果: 生成层时,是否不生成层,而是只在选中的层上生成当前元素里的属性组(即插件,遮罩,文字动画器等等) 46 | 清空属性组: 如果仅生成效果被打开,则决定是否在生成属性组前将原层里的原属性组清空 47 | 关键帧偏移: 如果仅生成效果被打开,则决定是否将关键帧按被选中层的进入时间进行偏移 48 | ``` 49 | 50 | ## 重载预览动画 51 | `右键->辅助脚本->重载组内预览动画`,即可为v3.0前保存的组进行预览动画的生成操作 52 | 可选项为是否限定最大帧数,如输入最大帧数,则脚本将限定预览动画范围为(0-最大帧数) 53 | 54 | ## 修复表达式报错 55 | `右键->辅助脚本->表达式翻译`,按需求选择需翻译的合成和目标语言,可修正语言版本不同导致的表达式报错 56 | 目前支持语言为中文,英文,日文和ADBE(Adobe通用标志名称) 57 | 生成时,如侦测到错误表达式,脚本将会自动进行表达式翻译. 58 | 59 | ## 预览全部/预览选中 60 | 没有元素被选中时,右键菜单中的预览按钮变为`预览全部`,点击后所有元素将会播放预览动画. 61 | 有元素被选中时,右键菜单中的预览按钮变为`预览部分`,点击后被选中的元素将会播放预览动画. 62 | 可使用Ctrl和Shift对元素进行多选和反选 63 | 64 | ## 导入图片 65 | `右键->导入图片`,可为选中的元素导入图片. 66 | 图片将会被脚本自动裁剪至缩略图大小 67 | 68 | ## 导入组 69 | `右键->导入组`,可进行导入组的操作,支持多选 70 | 71 | ## 新建组 72 | `右键->新建组`,可新建一个组. 73 | 保存层时必须有当前组存在. 74 | 75 | ## 新建模块 76 | `右键->新建模块`,可新建一个模块 77 | 新建组和导入组时,会将新组建立在当前模块中 78 | 在设置窗口中,可将组剪贴到其他模块,或对模块进行移动和改名操作 79 | 80 | ## 设置 81 | `右键->设置`,可打开设置窗口 82 | 83 | #### 移动组 84 | 在右上角选中一个组,按小键盘的"↑"和"↓"键,即可上下移动元素 85 | 86 | #### 剪切选中组到其他模块 87 | 在右上角选中一个组后,可将此组剪贴到其他模块中 88 | 89 | #### 批量导出组 90 | 弹出所有组的窗口,可在多选后,批量导出组 91 | 92 | #### 清空素材文件夹 93 | 存储素材功能开启后,所有脚本生成的图片和音频都被生成在一个固定的文件夹中,点击此按钮即可清空此文件夹 94 | 95 | #### 限制新界面的文字 96 | 是否剪贴主界面上元素的文字长度,避免文字位置溢出,默认开启 97 | 98 | #### 覆盖时更新缩略图 99 | 是否在覆盖时更新缩略图.此指主缩略图,是否更新预览动画由`右键->存储预览`决定,默认关闭 100 | 101 | #### 启用另一种缩略图 102 | CC和CC2014中,可启用另一种缩略图.此缩略图为AE合成栏的当前视图,包含层边缘和3D轮廓等像素.默认关闭 103 | 104 | #### 删除时警告 105 | 是否在删除元素时警告.默认开启 106 | 107 | #### 生成供预览的图片序列时图片的数量 108 | CC和CC2014中最大值为50,CC2015最大值为300. 109 | 原因是CC2015存储预览动画的速度至少为其他版本的10倍. 110 | 111 | #### 收集生成层时的文件夹名 112 | 生成层时,所有生成的层将会被自动收集到一个与元素名同名的工程栏文件夹中. 113 | 脚本还会将这个文件夹放置在总文件夹中,以达到清爽的工程栏效果 114 | 此选项即决定了总文件夹的名称 115 | 116 | #### 默认开启仅生成效果的组名 117 | 以逗号分隔,如果当前组的名称被包含在这里,则默认勾选`右键->仅生成效果` 118 | 119 | #### 中文/English 120 | 切换中英文.切换完毕后请重启脚本 121 | 122 | #### 检查更新 123 | 手动检查脚本更新. 124 | 目前用作更新的服务器作者另有其他项目需要使用,因此更新功能暂时关闭 125 | 126 | ## 预设设置 127 | `右键->预设设置`,即可打开预设窗口 128 | 按左右分为两部分,'仅生成效果'和'清空属性组' 129 | 勾选和取消勾选都将会被动态存储,配置完毕后按OK即可退出 130 | 搭配两者的选项,将会实现十分强大的预设功能 131 | 132 | #### 仅生成效果 133 | 决定在`右键->仅生成效果`被勾选且时,将会被脚本生成的属性组. 134 | 默认为 135 | ``` 136 | 遮罩 137 | 效果 138 | 图层样式 139 | 形状层形状组 140 | 文字层动画器 141 | ``` 142 | 143 | #### 清空属性组 144 | 决定在`右键->清空属性组`和`右键->仅生成效果`都被勾选时,在生成前将会被清空的属性组 145 | 146 | 默认为 147 | ``` 148 | 图层样式 149 | 文字层动画器 150 | ``` 151 | 152 | 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /src/ui/previewProgress.js: -------------------------------------------------------------------------------- 1 | var global = $.global 2 | 3 | var width = 300 4 | var height = 80 5 | var progressHeight = 20 6 | 7 | var progressFactory = { 8 | createWindow: function(len, title, prefixString, suffixString) { 9 | global.progressWin = new Window('palette', title) 10 | 11 | var group = global.progressWin.add(`Group{ 12 | orientation:'column',alignment: ['fill','fill'], 13 | preferredSize: [-1, ${height}], 14 | progressBar: Progressbar{ 15 | value:0, minvalue:0, maxvalue:${len}, 16 | preferredSize: [${width}, ${progressHeight}] 17 | }, 18 | progressText: StaticText { 19 | alignment:['fill','fill'],text:"", justify:'center',properties:{multiline:0} 20 | }, 21 | progressTimeText: StaticText { 22 | alignment:['fill','fill'],text:"", justify:'center',properties:{multiline:0} 23 | } 24 | }`) 25 | global.progressWin.addEventListener('keydown', function() { 26 | global.progressWin.close() 27 | }) 28 | global.progressTimeText = group.progressTimeText 29 | global.progressText = group.progressText 30 | global.progressBar = group.progressBar 31 | var divide = '0' + '/' + global.progressBar.maxvalue 32 | global.progressText.text = prefixString + divide + suffixString 33 | global.progressTimeText 34 | global.progressWin.show() 35 | global.progressWin.center() 36 | var preY = global.progressText.location[1] + 10 37 | global.progressText.originY = preY 38 | global.progressText.location[1] = preY + (global.progressText.location[1] >> 1) 39 | global.progressWin.startTime = Date.now() 40 | global.progressWin.update && global.progressWin.update() 41 | }, 42 | update: function(len, prefixString, suffixString, timePrefix, timeSuffix) { 43 | global.progressBar.value = global.progressBar.value + len 44 | var divide = global.progressBar.value + '/' + global.progressBar.maxvalue 45 | var time = (Date.now() - global.progressWin.startTime) / 1000 46 | global.progressText.text = prefixString + divide + suffixString 47 | global.progressTimeText.text = timePrefix + time.toString() + timeSuffix 48 | var preY = global.progressText.location[1] 49 | var shouldRelocation = global.progressTimeText.text.length === 0 50 | if (shouldRelocation) { 51 | global.progressText.location[1] = preY + (global.progressText.location[1] >> 1) 52 | } else { 53 | global.progressText.location[1] = global.progressText.originY 54 | } 55 | global.progressWin.update && global.progressWin.update() 56 | }, 57 | complete: function(timePrefix, timeSuffix) { 58 | var time = (Date.now() - global.progressWin.startTime) / 1000 59 | var report = timePrefix + time.toString() + timeSuffix 60 | writeLn(report) 61 | return time 62 | } 63 | } 64 | 65 | var title = loc(sp.previewTitle) 66 | var previewPrefix = loc(sp.previewPrefix) 67 | var timePrefix = loc(sp.previewTime) 68 | var timeSuffix = loc(sp.second) 69 | sp.willSavePreviews = function(len) { 70 | progressFactory.createWindow( 71 | len, 72 | title, 73 | previewPrefix, 74 | timeSuffix 75 | ) 76 | } 77 | sp.didSavePreview = function() { 78 | progressFactory.update( 79 | 1, 80 | previewPrefix, 81 | '', 82 | timePrefix, 83 | timeSuffix 84 | ) 85 | } 86 | sp.didSavePreviews = function() { 87 | progressFactory.complete(timePrefix, timeSuffix) 88 | } 89 | 90 | module.exports = progressFactory 91 | -------------------------------------------------------------------------------- /src/ui/presetWindow.js: -------------------------------------------------------------------------------- 1 | module.exports = function() { 2 | var jinWin = new Window('dialog', loc(sp.settingPre)) 3 | var jinRes = `group{ 4 | orientation:'column',alignment:['fill','fill'],alignChildren:['fill','fill'], 5 | guluG:Group{ 6 | orientation:'row',alignment:['fill','fill'],alignChildren:['fill','fill'], 7 | jinGroup:Group{ 8 | orientation:'column',alignment:['fill','fill'],alignChildren:['fill','fill'], 9 | isJin:StaticText{text:'${loc(sp.isEffect)}'} 10 | isJinSt:StaticText{text:'${loc(sp.jinOne)}',properties:{multiline:1}} 11 | jin:Panel{ 12 | orientation:'column',alignment:['fill','fill'],alignChildren:['fill','fill'], 13 | _1:Checkbox{text:'${loc(sp._1)}'}, 14 | _2:Checkbox{text:'${loc(sp._2)}'}, 15 | _3:Checkbox{text:'${loc(sp._3)}'}, 16 | _4:Checkbox{text:'${loc(sp._4)}'}, 17 | _5:Checkbox{text:'${loc(sp._5)}'}, 18 | _6:Checkbox{text:'${loc(sp._6)}'}, 19 | _7:Checkbox{text:'${loc(sp._7)}'}, 20 | _8:Checkbox{text:'${loc(sp._8)}'}, 21 | _9:Checkbox{text:'${loc(sp._9)}'}, 22 | } 23 | }, 24 | delGroup:Group{ 25 | orientation:'column',alignment:['fill','fill'],alignChildren:['fill','fill'], 26 | isJin:StaticText{text:'${loc(sp.cleanProperty)}'}, 27 | isJinSt:StaticText{text:'${loc(sp.jinTwo)}',properties:{multiline:1}}, 28 | del:Panel{ 29 | orientation:'column',alignment:['fill','fill'],alignChildren:['fill','fill'], 30 | _1:Checkbox{text:'${loc(sp._1)}'}, 31 | _2:Checkbox{text:'${loc(sp._2)}'}, 32 | _3:Checkbox{text:'${loc(sp._3)}',enabled:0}, 33 | _4:Checkbox{text:'${loc(sp._4)}',enabled:0}, 34 | _5:Checkbox{text:'${loc(sp._5)}'}, 35 | _6:Checkbox{text:'${loc(sp._6)}'}, 36 | _7:Checkbox{text:'${loc(sp._7)}'}, 37 | _8:Checkbox{text:'${loc(sp._8)}',enabled:0}, 38 | _9:Checkbox{text:'${loc(sp._9)}',enabled:0}, 39 | } 40 | }, 41 | }, 42 | oc:Group{ 43 | orientation:'row',alignment:['fill','center'],alignChildren:['center','fill'], 44 | ok:Button{text:'Ok',preferredSize:[160,30]}, 45 | } 46 | }` 47 | var jinGulu = jinWin.add(jinRes) 48 | for (let i = 1; i <= 9; i++) { 49 | if (sp.haveSetting('_1_' + i) === false) { 50 | if (i === 1 || i === 2 || i === 5) { 51 | sp.saveSetting('_1_' + i, '1') 52 | } else { 53 | sp.saveSetting('_1_' + i, '0') 54 | } 55 | } 56 | try { 57 | jinGulu.guluG.jinGroup.jin['_' + i].value = sp.getSetting('_1_' + i) === '1' 58 | jinGulu.guluG.jinGroup.jin['_' + i].onClick = function() { 59 | sp.getSetting('_1_' + i) 60 | sp.saveSetting('_1_' + i, (jinGulu.guluG.jinGroup.jin['_' + i].value === true) ? '1' : '0') 61 | } 62 | } catch (err) { } 63 | } 64 | for (let i = 1; i <= 9; i++) { 65 | if (sp.haveSetting('_2_' + i) === false) { 66 | sp.saveSetting('_2_' + i, '0') 67 | } 68 | 69 | try { 70 | jinGulu.guluG.delGroup.del['_' + i].value = sp.getSetting('_2_' + i) === '1' 71 | jinGulu.guluG.delGroup.del['_' + i].onClick = function() { 72 | sp.getSetting('_2_' + i) 73 | sp.saveSetting('_2_' + i, (jinGulu.guluG.delGroup.del['_' + i].value === true) ? '1' : '0') 74 | } 75 | } catch (err) { } 76 | } 77 | jinGulu.oc.ok.onClick = function() { 78 | jinWin.close() 79 | } 80 | jinWin.center() 81 | jinWin.show() 82 | } 83 | -------------------------------------------------------------------------------- /src/ui/outputGroupWindow.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function() { 3 | var outWin = new Window('window', 'Export', undefined, { 4 | resizeable: 0, 5 | maximizeButton: 0 6 | }) 7 | var outRes = `Group{ 8 | orientation: 'column', alignment:['fill', 'fill'], alignChildren:['fill', 'fill'],\ 9 | wlist:ListBox{properties:{multiselect:1}}, 10 | oc:Group{ 11 | alignment:['fill', 'fill'], alignChildren:['fill', 'fill'], 12 | ok:Button{text:'` + loc(sp.ok) + `'}, 13 | cancel:Button{text:'` + loc(sp.cancel) + `'} 14 | } 15 | }` 16 | try { 17 | outRes = outWin.add(outRes) 18 | } catch (err) { 19 | alert(err) 20 | } 21 | for (var i = 0; i < sp.xmlFileNames.length; i++) { 22 | outRes.wlist.add('item', sp.xmlFileNames[i]) 23 | } 24 | outRes.wlist.size = [200, 400] 25 | outWin.show() 26 | 27 | outRes.oc.cancel.onClick = function() { 28 | outWin.close() 29 | } 30 | 31 | outRes.oc.ok.onClick = function() { 32 | if (outRes.wlist.selection !== null) { 33 | var exportFolder = Folder.selectDialog('Please select folder') 34 | if (exportFolder !== null && exportFolder instanceof Folder) { 35 | for (var i = 0; i < outRes.wlist.selection.length; i++) { 36 | var sourceFile = sp.getFileByName(outRes.wlist.selection[i].text) 37 | var targetFile = File(exportFolder.toString() + sp.slash + outRes.wlist.selection[i].text + '.xml') 38 | if (targetFile.exists) { 39 | continue 40 | } 41 | 42 | var images = sp.getImageFolderByName(outRes.wlist.selection[i].text).getFiles() 43 | var picXml = new XML('') 44 | var seqXml = new XML('') 45 | images.forEach(function(item, index) { 46 | if (item.name.indexOf('.png') !== -1) { 47 | item.open('r') 48 | item.encoding = 'binary' 49 | var str = encodeURIComponent(item.read()) 50 | item.close() 51 | var tempXmlBigHere = new XML('' + encodeURIComponent(item.name) + '') 52 | var tempXmlHeres = new XML('' + str + '') 53 | var guluTempA = new XML('') 54 | guluTempA.appendChild(tempXmlBigHere) 55 | guluTempA.appendChild(tempXmlHeres) 56 | picXml.appendChild(guluTempA) 57 | } else if (item instanceof Folder && item.name.indexOf('_seq') !== -1) { 58 | var thisFolder = item 59 | var folderXml = new XML("") 60 | var seqFiles = thisFolder.getFiles() 61 | seqFiles.forEach(function(imageFile, imageIndex) { 62 | imageFile.open('r') 63 | imageFile.encoding = 'binary' 64 | var str = encodeURIComponent(imageFile.read()) 65 | imageFile.close() 66 | var tempXmlBigHere = new XML('' + encodeURIComponent(imageFile.name) + '') 67 | var tempXmlHeres = new XML('' + str + '') 68 | var guluTempA = new XML('') 69 | guluTempA.appendChild(tempXmlBigHere) 70 | guluTempA.appendChild(tempXmlHeres) 71 | folderXml.appendChild(guluTempA) 72 | }) 73 | seqXml.appendChild(folderXml) 74 | } 75 | }) 76 | var xml = new XML(sourceFile.readd()) 77 | if (picXml.children().length() > 0) { 78 | xml.appendChild(picXml) 79 | } 80 | if (seqXml.children().length() > 0) { 81 | xml.appendChild(seqXml) 82 | } 83 | if (xml.children().length() === 0) { 84 | xml = '' 85 | } 86 | targetFile.writee(xml) 87 | } // for loop 88 | clearOutput() 89 | writeLn('Complete!') 90 | } // not null 91 | } // last 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | try { 2 | (function(global) { 3 | require('src/singleton') 4 | require('src/i18n') 5 | require('src/polyfill') 6 | require('src/preset') 7 | require('src/startup') 8 | require('lib/AfterEffectsLayer') 9 | require('lib/Translate') 10 | require('lib/ReloadPic') 11 | require('lib/AutoSave') 12 | require('lib/GridView') 13 | require('lib/UIParser') 14 | var helpers = require('src/index') 15 | 16 | $.layer.slash = sp.slash 17 | $.layer.tempFolder = new Folder(sp.scriptFolder.toString() + $.layer.slash + 'tempFile') 18 | $.layer.translate = $.global.translate 19 | 20 | sp.fns = new helpers.fns() 21 | 22 | // Create UI 23 | $.global.callbackBeforeWebpackBuild && $.global.callbackBeforeWebpackBuild() 24 | if (!(global instanceof Panel)) { 25 | $.global.callbackBeforeWebpackBuild = function() { 26 | win.close() 27 | } 28 | } 29 | var win = sp.win = global instanceof Panel ? global : new Window('window', sp.scriptName, undefined, { resizeable: true }) 30 | var outterGroup = sp.win.outterGroup = win.add("Group{orientation: 'column', alignment: ['fill','fill'],spacing:0,margins:0}") 31 | var innerGroup = sp.win.innerGroup = outterGroup.add("Group{orientation: 'row', alignment: ['fill','fill'],spacing:0,margins:0}") 32 | var parentDroplist = sp.parentDroplist = innerGroup.add('Dropdownlist{}') 33 | var droplist = sp.droplist = innerGroup.add('Dropdownlist{}') 34 | var gv = sp.gv = new GridView(outterGroup) 35 | var screen = $.screens[0].toString().split('-').pop().split(':') 36 | outterGroup.maximumSize = innerGroup.maximumSize = [parseInt(screen[0]), parseInt(screen[1])] 37 | 38 | // Set GridView's attributes 39 | gv.scale = sp.gridViewScale 40 | gv.limitText = sp.getSettingAsBool('limitText') 41 | gv.showText = sp.showThumbValue 42 | gv.version = (parseInt(app.version.split('.')[0]) === 12 || parseInt(app.version.split('.')[0]) === 14) ? 'CC' : 'CC2014' 43 | 44 | // Binding eventHandlers to mouse click and Window 45 | gv.leftClick = sp.fns.leftClick 46 | gv.rightClick = sp.fns.rightClick 47 | gv.leftDoubleClick = sp.fns.newLayer 48 | gv.mouseMove = sp.fns.moveOver 49 | parentDroplist.onChange = sp.fns.parentDroplistChange 50 | droplist.onChange = sp.fns.droplistChange 51 | 52 | sp.reloadParentDroplist() 53 | var selection = parseInt(sp.getSetting('parentSelection')) 54 | parentDroplist.selection = (selection <= parentDroplist.items.length - 1 && selection >= 0) ? selection : 0 55 | selection = parseInt(sp.getSetting('thisSelection')) 56 | droplist.selection = (selection <= droplist.items.length - 1 && selection >= 0) ? selection : 0 57 | 58 | sp.renderTaskArray.forEach(function(item, index) { 59 | app.cancelTask(item) 60 | }) 61 | sp.renderTaskArray.length = 0 62 | sp.previewHelper = {} 63 | 64 | win.onResize = win.onResizing = sp.fns.winResize 65 | 66 | if (win instanceof Panel) { 67 | win.layout.layout(1) 68 | } else { 69 | var ratio = sp.gv.scale 70 | var location = sp.getSetting('winLocation').split(',') 71 | win.location = [parseInt(location[0]), parseInt(location[1])] 72 | if (win.location[0] <= 0 || win.location[1] <= 0) { win.location = [100, 200] } 73 | win.show() 74 | var size = sp.getSetting('winSize').split(',') 75 | win.size = [parseInt(size[0]) * ratio, parseInt(size[1]) * ratio] 76 | if (win.size[0] <= 0 || win.size[1] <= 0) { win.size = [240, 500] } 77 | win.onClose = sp.fns.winClose 78 | } 79 | 80 | win.onResize() 81 | 82 | if (sp.checkVersionOnStartupValue) { 83 | var checkVersionFunc = require('src/https/checkVersion')( 84 | win, 85 | /* true for starting */ 86 | true 87 | ) 88 | checkVersionFunc() 89 | } 90 | 91 | var observeSingleton = require('src/mvvm/index') 92 | observeSingleton(sp) 93 | 94 | app.onError && app.onError(function(err) { 95 | alert(`警告, Sp_memory检测到AE报错, 内容如下: 96 | ${err.toString()} 97 | 98 | 请尽量将层分散存储在不同组内`) 99 | }) 100 | })(memoryGlobal) 101 | } catch (err) { alert('Line #' + err.line.toString() + '\r\n' + err.toString()) } 102 | -------------------------------------------------------------------------------- /src/ui/moduleWindow.js: -------------------------------------------------------------------------------- 1 | module.exports = function(groupItem, win, callback) { 2 | var moveWin = new Window('dialog', 'Module', undefined, { 3 | resizeable: 0, 4 | maximizeButton: 0 5 | }) 6 | var outRes = `Group{ 7 | orientation: 'column', alignment:['fill', 'fill'], alignChildren:['fill', 'fill'],\ 8 | helpTip:StaticText{text:'` + loc(sp.moduleHelpTip) + `'}, 9 | wlist:ListBox{properties:{multiselect:0}}, 10 | oc:Group{ 11 | alignment:['fill', 'fill'], alignChildren:['fill', 'fill'], 12 | ok:Button{text:'` + loc(sp.changeModuleName) + `'}, 13 | cancel:Button{text:'` + loc(sp.quit) + `'} 14 | } 15 | }` 16 | try { 17 | outRes = moveWin.add(outRes) 18 | } catch (err) { 19 | alert(err) 20 | } 21 | sp.xmlGroupNames.forEach(function(item, index) { 22 | this.add('item', item) 23 | }, outRes.wlist) 24 | 25 | outRes.wlist.addEventListener('keydown', function(k) { 26 | switch (k.keyName) { 27 | case 'Up': 28 | 29 | if (this.selection !== null && this.selection.index > 0) { 30 | var xml = new XML(sp.settingsFile.readd()) 31 | var groupIndex = this.selection.index 32 | var targetXml = xml.ParentGroup.child(groupIndex) 33 | 34 | xml.ParentGroup.insertChildBefore(xml.ParentGroup.child(groupIndex - 1), new XML(targetXml)) 35 | xml.ParentGroup.child(groupIndex + 1).setLocalName('waitToDelete') 36 | delete xml.ParentGroup.waitToDelete 37 | 38 | sp.settingsFile.writee(xml) 39 | 40 | sp.reloadParentDroplist() 41 | var selection = parseInt(sp.getSetting('parentSelection')) 42 | sp.parentDroplist.selection = (selection <= sp.parentDroplist.items.length - 1 && selection >= 0) ? selection : 0 43 | selection = parseInt(sp.getSetting('thisSelection')) 44 | sp.droplist.selection = (selection <= sp.droplist.items.length - 1 && selection >= 0) ? selection : 0 45 | 46 | sp.swap(outRes.wlist.items[this.selection.index - 1], outRes.wlist.items[this.selection.index]) 47 | }; 48 | break 49 | case 'Down': 50 | if (this.selection !== null && this.selection.index < this.items.length - 1) { 51 | xml = new XML(sp.settingsFile.readd()) 52 | groupIndex = this.selection.index 53 | targetXml = xml.ParentGroup.child(groupIndex) 54 | 55 | xml.ParentGroup.insertChildAfter(xml.ParentGroup.child(groupIndex + 1), new XML(targetXml)) 56 | xml.ParentGroup.child(groupIndex).setLocalName('waitToDelete') 57 | delete xml.ParentGroup.waitToDelete 58 | 59 | sp.settingsFile.writee(xml) 60 | 61 | sp.reloadParentDroplist() 62 | selection = parseInt(sp.getSetting('parentSelection')) 63 | sp.parentDroplist.selection = (selection <= sp.parentDroplist.items.length - 1 && selection >= 0) ? selection : 0 64 | selection = parseInt(sp.getSetting('thisSelection')) 65 | sp.droplist.selection = (selection <= sp.droplist.items.length - 1 && selection >= 0) ? selection : 0 66 | 67 | sp.swap(outRes.wlist.items[this.selection.index], outRes.wlist.items[this.selection.index + 1]) 68 | }; 69 | break 70 | } 71 | }) 72 | 73 | outRes.oc.cancel.onClick = function() { 74 | moveWin.close() 75 | win.close() 76 | callback && callback() 77 | } 78 | 79 | outRes.oc.ok.onClick = function() { 80 | var wlist = outRes.wlist 81 | if (!wlist.selection) return 82 | var newGroupName = prompt(loc(sp.setName), wlist.selection.text) 83 | if (!newGroupName) return 84 | if (sp.xmlGroupNames.includes(newGroupName)) { 85 | alert(loc(sp.existName)) 86 | return 87 | } 88 | 89 | var xml = new XML(sp.settingsFile.readd()) 90 | var parentGroup = xml.ParentGroup 91 | var groupIndex = wlist.selection.index 92 | 93 | var editXml = parentGroup.child(groupIndex) 94 | editXml['@groupName'] = newGroupName 95 | 96 | sp.settingsFile.writee(xml) 97 | 98 | sp.reloadParentDroplist() 99 | var selection = parseInt(sp.getSetting('parentSelection')) 100 | sp.parentDroplist.selection = (selection <= sp.parentDroplist.items.length - 1 && selection >= 0) ? selection : 0 101 | selection = parseInt(sp.getSetting('thisSelection')) 102 | sp.droplist.selection = (selection <= sp.droplist.items.length - 1 && selection >= 0) ? selection : 0 103 | 104 | moveWin.close() 105 | win.close() 106 | } // last 107 | 108 | outRes.wlist.size = [200, 300] 109 | moveWin.show() 110 | } 111 | -------------------------------------------------------------------------------- /src/preset.js: -------------------------------------------------------------------------------- 1 | module.exports = (function() { 2 | var keyNameArr = [] 3 | var valueArr = [] 4 | 5 | for (var i = 1; i <= 9; i++) { 6 | keyNameArr.push('_1_' + i) 7 | if (i === 1 || i === 2 || i === 5) { 8 | valueArr.push('1') 9 | } else { 10 | valueArr.push('0') 11 | } 12 | } 13 | 14 | for (i = 1; i <= 9; i++) { 15 | keyNameArr.push('_2_' + i) 16 | valueArr.push('0') 17 | } 18 | 19 | keyNameArr.pushh('thisSelection') 20 | .pushh('limitText') 21 | .pushh('thumbType') 22 | .pushh('winLocation') 23 | .pushh('winSize') 24 | .pushh('coverChange') 25 | .pushh('folderName') 26 | .pushh('effectName') 27 | .pushh('deleteAlert') 28 | .pushh('preCompose') 29 | .pushh('saveMaterial') 30 | .pushh('autoName') 31 | .pushh('onlyEffect') 32 | .pushh('cleanGroup') 33 | .pushh('offsetKeyframe') 34 | .pushh('language') 35 | .pushh('showThumb') 36 | .pushh('parentSelection') 37 | .pushh('frameSecond') 38 | .pushh('frameNum') 39 | .pushh('savePreview') 40 | .pushh('gridViewScale') 41 | .pushh('saveWorkarea') 42 | .pushh('checkVersionOnStartup') 43 | 44 | valueArr.pushh('1') 45 | .pushh('true') 46 | .pushh('false') 47 | .pushh('200,500') 48 | .pushh('300,500') 49 | .pushh('false') 50 | .pushh('Sp_memory Folder') 51 | .pushh('Effects,Effect,effect,effects,特效,效果') 52 | .pushh('true') 53 | .pushh('false') 54 | .pushh('true') 55 | .pushh('true') 56 | .pushh('false') 57 | .pushh('false') 58 | .pushh('false') 59 | .pushh('ch') 60 | .pushh('true') 61 | .pushh('0') 62 | .pushh('33') 63 | .pushh('30') 64 | .pushh('true') 65 | .pushh('1') 66 | .pushh('false') 67 | .pushh('false') 68 | 69 | keyNameArr.forEach(function(item, index) { 70 | var value = valueArr[index] 71 | if (sp.haveSetting(item) === false) sp.saveSetting(item, value) 72 | }) 73 | 74 | // ensure delete alert 75 | sp.deleteAlertValue = true 76 | 77 | sp.showThumbValue = sp.getSettingAsBool('showThumb') 78 | sp.preComposeValue = sp.getSettingAsBool('preCompose') 79 | sp.saveMaterialValue = sp.getSettingAsBool('saveMaterial') 80 | sp.autoNameValue = sp.getSettingAsBool('autoName') 81 | sp.onlyEffectValue = sp.getSettingAsBool('onlyEffect') 82 | sp.cleanGroupValue = sp.getSettingAsBool('cleanGroup') 83 | sp.offsetKeyframeValue = sp.getSettingAsBool('offsetKeyframe') 84 | sp.savePreviewValue = sp.getSettingAsBool('savePreview') 85 | sp.saveWorkareaValue = sp.getSettingAsBool('saveWorkarea') 86 | 87 | sp.thumbTypeValue = sp.getSettingAsBool('thumbType') 88 | sp.coverChangeValue = sp.getSettingAsBool('coverChange') 89 | 90 | sp.frameSecond = parseInt(sp.getSetting('frameSecond')) 91 | sp.frameNum = parseInt(sp.getSetting('frameNum')) 92 | sp.gridViewScale = parseFloat(sp.getSetting('gridViewScale')) 93 | sp.checkVersionOnStartupValue = sp.getSettingAsBool('checkVersionOnStartup') 94 | 95 | !sp.scriptFolder.exists && sp.scriptFolder.create() 96 | !sp.roamingFolder.exists && sp.roamingFolder.create() 97 | !sp.materialFolder.exists && sp.materialFolder.create() 98 | 99 | var loc = function(string) { 100 | if (sp.lang === 0) { 101 | sp.lang = sp.getSetting('language') 102 | 103 | if (sp.isForceEnglish()) { 104 | sp.lang = 'en' 105 | } 106 | } 107 | return string[sp.lang] 108 | } 109 | 110 | $.global.loc = loc 111 | 112 | sp.extend(sp, { 113 | beyondCS6: true, 114 | versionUpdateInfo: { 115 | ch: 116 | `层存储脚本Sp_Memory ${process.env.VERSION} @秋风_小径 117 | 118 | >> 优化 119 | - 更换打包工具以提供直观的报错定位 120 | - 支持存储视频, 去除素材的大小限制 121 | - 生成层进度条 122 | - 存储层进度条 123 | - 进度条显示脚本耗时 124 | - 存储预览进度条 125 | - 优化预览CPU占用 126 | - 生成单个预合成时直接拉伸至当前合成大小 127 | - 增加允许截取工作区预览的检测框 128 | - 修复检查更新功能 129 | - 增加自动更新功能 130 | - 增加windows缩放比例参数 131 | 132 | >> 漏洞修复 133 | - 修复音频层关键帧未生成的问题 134 | - 修复windows缩放比例不为1时的界面越界问题 135 | - 修复界面中一些特殊文字的错位问题 136 | - 修复windows禁止字符导致预览存储失败的问题 137 | - 修复最小化时关掉脚本导致的脚本大小归零的问题 138 | - 修复windows特殊字符串导致的模块,组以及元素生成失败的问题 139 | - 修复mac CC2017中表达式翻译无法使用的问题 140 | - 修复setInterpolationTypeAtKey的关键帧生成报错 141 | - 修复非1080p的右键菜单越界的问题 142 | `, 143 | en: `Sp_memory ${process.env.VERSION} @smallpath 144 | 145 | New Feature: 146 | 1. Move to new pack tool to provide useful error stack trace 147 | 2. Add support to media material layer and remove the size limit of any material 148 | 3. Add progress bar to creating and saving process, together with saving previews 149 | 4. Add check box to support saving previews in workarea 150 | 5. Add support for windows font scale ratio to solve problem on AE CC2015 151 | 6. Fit to comp when only one comp layer is generated 152 | 7. Add auto updating feature and checkbox for starting checking 153 | 8. Optimize cpu rank while previewing 154 | ` 155 | } 156 | }) 157 | 158 | if (sp.haveSetting('version') === false || sp.getSetting('version') < sp.version) { 159 | alert(loc(sp.versionUpdateInfo)) 160 | } 161 | sp.saveSetting('version', sp.version) 162 | })() 163 | -------------------------------------------------------------------------------- /docs/en/TUTORIAL.md: -------------------------------------------------------------------------------- 1 | ## Open script 2 | 3 | #### Panel type 4 | Open `Sp_memory.jsxbin` from `AE->Window->Sp_memory.jsxbin` 5 | 6 | #### Palette type 7 | `AE->File->Scripts->Run script file` to open `Sp_memory.jsxbin`. 8 | In this type, memory supports shortcut keys.More infomation in the memory setting window 9 | 10 | ## Item 11 | The element in memory panel is called `Item` 12 | 13 | ## Save layer 14 | `Right Click->New item` to save selected layers into a new item 15 | Shortcut:`Ctrl+Right Click` Or `Alt+Right Click` 16 | 17 | ## Create layer 18 | `Right Click->New layer` to generating new layers using the selected item 19 | Shortcut:Double click at selected item. 20 | 21 | ## Cover item 22 | `Right Click->Cover item` to cover selected item by selected layers 23 | 24 | ## Move item 25 | `Shift+Right Click` to show item-moving window 26 | 27 | ## Save all layers in current comp, each as a new item. 28 | `Right Click->Help scripts->save every layer in active comp` to save each layer as each item in current comp 29 | Very useful to save motion graphics compositions 30 | 31 | ## 8 checkboxs of Right-click menu 32 | ``` 33 | General: 34 | Show text: whether the text of item shows 35 | 36 | Save layer: 37 | Auto rename: whether auto name for the item when save layer. 38 | Save preview: whether save preview for the item when save layer. 39 | Save material: whether save images and musics for the item when save layer. 40 | If true,script can create images and musics even if the source has been removed. 41 | 42 | Create layer: 43 | Pre-compose: Whether pre-compose layers which are created. 44 | Only property: Whether only create new property groups on selected layer rather than create new layers. 45 | Empty property: If Only property is checked,decide whether clean property group before Only property 46 | Offset keyframe: If Only property is checked,decide whether keyframes created should offset related to the inPoint of layer. 47 | ``` 48 | 49 | ## Reload previews of group 50 | `Right Click->Help scripts->Reload previews of group` to reload previews of group if there is no previews 51 | Option is the max number of frames.memory will cut the layers from 0 to the max number if you input it. 52 | 53 | ## Fix expression errors 54 | `Right Click->Help scripts->Fix expression errors` to fix expression errors caused by using different language of AE 55 | Language supported includes Chinese,English,Japanese and ADBE(Adobe general identifier) 56 | If there are wrong expressions when create layer,memory will call this script to fix them. 57 | 58 | ## Preview all/selected 59 | If there is items being selected , the preview button in Right-click menu will be `Preview selected` 60 | If there is no,the preview button will be `Preview all` 61 | You can select items by using `Ctrl` and `Shift` 62 | 63 | ## Import picture 64 | `Right Click->Import picture` to import image to selected item 65 | memory will scale the picture to the size of thumbnail 66 | 67 | ## Import group 68 | `Right Click->Import group` to import groups to memory 69 | 70 | ## Add group 71 | `Right Click->New group` to add a new group 72 | Group is the container of item,so make sure there is a group before saving layer 73 | 74 | ## Add module 75 | `Right Click->New module` to add a new module 76 | When add group and import group,the group will be added into current module 77 | You can edit the module by open the `Move module or rename module` from the Setting window 78 | 79 | ## Setting 80 | `Right Click->Setting` to open Setting window 81 | 82 | #### Move group 83 | Press "↑" and "↓" to move group in the Setting window which is near top-right 84 | 85 | #### Cut group to other module 86 | Cut selected group to other module 87 | 88 | #### Export groups 89 | Export groups.Support multi-select 90 | 91 | #### Empty temp folder 92 | Empty the material folder which is created by memory using 'Save material' feature 93 | 94 | #### Limit the text 95 | Decide whether limit the text of item to avoid overflow.Default: true 96 | 97 | #### Update thumbs when cover item 98 | Whether update thumbnail when cover item.This is just thunmnail.To avoid updating preview, make sure `Right Click-Save preview` is not checked 99 | 100 | #### Enable new type of thumb 101 | In CC and CC,memory can save another type of thumb.This thumbnail is exactly the screenshot of `Composition Window` of AE 102 | 103 | #### Deleting alert 104 | Whether alert when delete items.Default: true 105 | 106 | #### The milliseconds of a frame continues when preview 107 | The max number is 50 in CC and CC2014, and is 300 in CC2015 108 | Since the speed of saveing preview in CC2015 is 10 times faster than other AE version 109 | 110 | #### The folder name of collect feature 111 | This is the name of the main folder in `Project Window` 112 | memory will collect the Comp layers created by script to the main folder 113 | 114 | #### The group name that can enable 'Only property' 115 | If the name of group is included here, then `Right Click->Only property` will be checked when change groups 116 | 117 | #### 中文/English 118 | To change script language to 中文 or English 119 | 120 | #### Check version 121 | Check updates for memory 122 | 123 | ## Preset setting 124 | `Right Click->Preset setting` to open preset window 125 | 'Only property' and 'Empty property' will be here. 126 | You can edit them by yourself to use the powerful preset feather of memory 127 | 128 | #### Only property 129 | If `Right Click->Only property` is checked ,decide the property groups that will be created 130 | Default: 131 | ``` 132 | Mask 133 | Effect 134 | Layer styles 135 | Shape content 136 | Text animators 137 | ``` 138 | 139 | #### Empty property 140 | If `Right Click->Empty property` and `Right Click->Only property` are checked,decide the property groups that will be empty before Only property 141 | 142 | Default: 143 | ``` 144 | Layer styles 145 | Text animators 146 | ``` 147 | 148 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /src/ui/rightClickMenu.js: -------------------------------------------------------------------------------- 1 | var settingWindow = require('./settingWindow') 2 | var presetWindow = require('./presetWindow') 3 | 4 | module.exports = function() { 5 | var itemList = [ 6 | { name: loc(sp.settings), type: 'button' }, { name: 'helperScripts', type: 'dropdownlist' }, 7 | 8 | { name: 'preview', type: 'button' }, { name: loc(sp.yushe), type: 'button' }, 9 | 10 | { name: loc(sp.changeName), type: 'button' }, { name: loc(sp.importPicture), type: 'button' }, 11 | { name: loc(sp.addModule), type: 'button' }, { name: loc(sp.deleteModule), type: 'button' }, 12 | { name: loc(sp.importFile), type: 'button' }, { name: loc(sp.exportFile), type: 'button' }, 13 | { name: loc(sp.addGroup), type: 'button' }, { name: loc(sp.deleteGroup), type: 'button' }, 14 | { name: loc(sp.addElement), type: 'button' }, { name: loc(sp.cover), type: 'button' }, 15 | { name: loc(sp.create), type: 'button' }, { name: loc(sp.deleteElement), type: 'button' }, 16 | 17 | // { name: loc(sp.searchText), type: 'button' }, { name: loc(sp.searchButton), type: 'button' }, 18 | 19 | { name: loc(sp.isShow), type: 'checkbox' }, { name: loc(sp.isName), type: 'checkbox', id: 'autoName' }, 20 | { name: loc(sp.isSavePreview), type: 'checkbox', id: 'savePreview' }, { name: loc(sp.isOffset), type: 'checkbox', id: 'saveMaterial' }, 21 | { name: loc(sp.isPrecomp), type: 'checkbox', id: 'preCompose' }, { name: loc(sp.isEffect), type: 'checkbox', id: 'onlyEffect' }, 22 | { name: loc(sp.cleanProperty), type: 'checkbox', id: 'cleanGroup' }, { name: loc(sp.offsetKey), type: 'checkbox', id: 'offsetKeyframe' }, 23 | { name: loc(sp.saveWorkarea), type: 'checkbox', id: 'saveWorkarea' } 24 | 25 | ] 26 | 27 | var length = itemList.length 28 | 29 | var space = 102 / 5 30 | var buttonHeight = 20 31 | var checkBoxHeight = 21 32 | 33 | if (sp.lang === 'ch') { var maxWidth = 180 } else { maxWidth = 190 } 34 | 35 | var shortMenu = new Window('palette', '', [0, 0, maxWidth, Math.ceil(length / 2) * space + 2], { 36 | borderless: true 37 | }) 38 | 39 | for (var i = 0; i < length; i++) { 40 | var item = itemList[i] 41 | let itemWidth, itemHeight 42 | itemWidth = maxWidth / 2 + (item.widthOffset || 0) 43 | if (item.type === 'button') { 44 | itemHeight = buttonHeight 45 | } else if (item.type === 'checkbox') { 46 | itemHeight = checkBoxHeight 47 | } else if (item.type === 'dropdownlist') { 48 | itemHeight = buttonHeight 49 | } else if (item.type === 'edittext') { 50 | itemHeight = buttonHeight 51 | } 52 | var control 53 | if (i % 2 === 0) { 54 | control = shortMenu[item.name] = shortMenu.add( 55 | item.type, 56 | [ 57 | 0, 58 | (parseInt((i) / 2) * itemHeight), 59 | itemWidth, 60 | (22 + parseInt((i) / 2) * itemHeight) 61 | ], 62 | item.name 63 | ) 64 | } else { 65 | control = shortMenu[item.name] = shortMenu.add( 66 | item.type, 67 | [ 68 | itemWidth, 69 | (parseInt((i - 1) / 2) * itemHeight), 70 | maxWidth, 71 | (22 + parseInt((i - 1) / 2) * itemHeight) 72 | ], 73 | item.name 74 | ) 75 | } 76 | if (control && item.id) control.id = item.id 77 | } 78 | 79 | var isCheckBoxClicked = false 80 | 81 | shortMenu[loc(sp.settings)].onClick = function() { 82 | isCheckBoxClicked = false 83 | shortMenu.hide() 84 | settingWindow() 85 | } 86 | 87 | shortMenu['helperScripts'].add('item', loc(sp.helperScripts)) 88 | shortMenu['helperScripts'].add('item', loc(sp.expressionTranslate)) 89 | shortMenu['helperScripts'].add('item', loc(sp.reloadGroup)) 90 | shortMenu['helperScripts'].add('item', loc(sp.saveEachLayer)) 91 | shortMenu['helperScripts'].selection = 0 92 | 93 | shortMenu['helperScripts'].onChange = shortMenu['helperScripts'].onChanging = function() { 94 | try { 95 | // run sp_translate script 96 | this.selection.index === 1 && 97 | $.global.translate() || 98 | 99 | // generate and then save the whole group 100 | this.selection.index === 2 && 101 | $.global.reloadPic() || 102 | 103 | // auto save every layer in current comp,one layer as one element 104 | this.selection.index === 3 && 105 | $.global.autoSave() 106 | } catch (err) { 107 | err.printa() 108 | } 109 | 110 | // back list's selection 111 | this.selection = 0 112 | } 113 | 114 | shortMenu['preview'].onClick = function() { 115 | isCheckBoxClicked = false 116 | shortMenu.hide() 117 | sp.fns.previewAll() 118 | } 119 | 120 | shortMenu[loc(sp.yushe)].onClick = function() { 121 | isCheckBoxClicked = false 122 | shortMenu.hide() 123 | presetWindow() 124 | } 125 | 126 | shortMenu[loc(sp.changeName)].onClick = function() { 127 | isCheckBoxClicked = false 128 | shortMenu.hide() 129 | sp.fns.changeName() 130 | } 131 | 132 | shortMenu[loc(sp.importPicture)].onClick = function() { 133 | isCheckBoxClicked = false 134 | shortMenu.hide() 135 | sp.fns.importImage() 136 | } 137 | 138 | shortMenu[loc(sp.addModule)].onClick = function() { 139 | isCheckBoxClicked = false 140 | shortMenu.hide() 141 | sp.fns.addModule() 142 | } 143 | 144 | shortMenu[loc(sp.deleteModule)].onClick = function() { 145 | isCheckBoxClicked = false 146 | shortMenu.hide() 147 | sp.fns.deleteModule() 148 | } 149 | 150 | shortMenu[loc(sp.importFile)].onClick = function() { 151 | isCheckBoxClicked = false 152 | shortMenu.hide() 153 | sp.fns.importFiles() 154 | } 155 | 156 | shortMenu[loc(sp.exportFile)].onClick = function() { 157 | isCheckBoxClicked = false 158 | shortMenu.hide() 159 | sp.fns.exportFile() 160 | } 161 | 162 | shortMenu[loc(sp.addGroup)].onClick = function() { 163 | isCheckBoxClicked = false 164 | shortMenu.hide() 165 | sp.fns.addGroup() 166 | } 167 | 168 | shortMenu[loc(sp.deleteGroup)].onClick = function() { 169 | isCheckBoxClicked = false 170 | shortMenu.hide() 171 | sp.fns.deleteGroup() 172 | } 173 | 174 | shortMenu[loc(sp.addElement)].onClick = function() { 175 | isCheckBoxClicked = false 176 | shortMenu.hide() 177 | sp.fns.newItem() 178 | } 179 | 180 | shortMenu[loc(sp.cover)].onClick = function() { 181 | isCheckBoxClicked = false 182 | shortMenu.hide() 183 | sp.fns.cover() 184 | } 185 | 186 | shortMenu[loc(sp.create)].onClick = function() { 187 | isCheckBoxClicked = false 188 | shortMenu.hide() 189 | sp.fns.newLayer() 190 | } 191 | 192 | shortMenu[loc(sp.deleteElement)].onClick = function() { 193 | isCheckBoxClicked = false 194 | shortMenu.hide() 195 | sp.fns.deleteItem() 196 | } 197 | 198 | shortMenu[loc(sp.isShow)].value = sp.showThumbValue 199 | shortMenu[loc(sp.isName)].value = sp.autoNameValue 200 | shortMenu[loc(sp.isSavePreview)].value = sp.savePreviewValue 201 | shortMenu[loc(sp.isOffset)].value = sp.saveMaterialValue 202 | shortMenu[loc(sp.isPrecomp)].value = sp.preComposeValue 203 | shortMenu[loc(sp.isEffect)].value = sp.onlyEffectValue 204 | shortMenu[loc(sp.cleanProperty)].value = sp.cleanGroupValue 205 | shortMenu[loc(sp.offsetKey)].value = sp.offsetKeyframeValue 206 | shortMenu[loc(sp.saveWorkarea)].value = sp.saveWorkareaValue 207 | 208 | shortMenu[loc(sp.isShow)].onClick = function() { 209 | sp.showThumbValue = this.value 210 | $.global.sp.gv.showText = this.value 211 | sp.saveSetting('showThumb', this.value.toString()) 212 | isCheckBoxClicked = true 213 | sp.gv.refresh() 214 | } 215 | 216 | shortMenu[loc(sp.isName)].onClick = 217 | shortMenu[loc(sp.isSavePreview)].onClick = 218 | shortMenu[loc(sp.isOffset)].onClick = 219 | shortMenu[loc(sp.isPrecomp)].onClick = 220 | shortMenu[loc(sp.isEffect)].onClick = 221 | shortMenu[loc(sp.cleanProperty)].onClick = 222 | shortMenu[loc(sp.offsetKey)].onClick = 223 | shortMenu[loc(sp.saveWorkarea)].onClick = function() { 224 | var name = this.id + 'Value' 225 | sp[name] = this.value 226 | isCheckBoxClicked = true 227 | } 228 | 229 | shortMenu.addEventListener('blur', function() { 230 | if (isCheckBoxClicked === false) { 231 | shortMenu.hide() 232 | } else { 233 | isCheckBoxClicked = true 234 | } 235 | }) 236 | 237 | shortMenu.onDeactivate = function() { 238 | shortMenu.hide() 239 | } 240 | 241 | shortMenu.addEventListener('keydown', function(event) { 242 | shortMenu.hide() 243 | }) 244 | 245 | return shortMenu 246 | } 247 | -------------------------------------------------------------------------------- /src/i18n/index.js: -------------------------------------------------------------------------------- 1 | 2 | /** **********************************界面的字符串资源***************************************/ 3 | sp.extend(sp, { 4 | settings: { en: 'Setting', ch: '设置' }, 5 | groupName: { en: 'Group name :', ch: '组名 :' }, 6 | elementName: { en: 'Element Name :', ch: '元素名 :' }, 7 | changeName: { en: 'Rename item', ch: '重命名元素' }, 8 | importPicture: { en: 'Import picture', ch: '导入图片' }, 9 | importFile: { en: 'Import file', ch: '导入组' }, 10 | exportFile: { en: 'Export file', ch: '导出组' }, 11 | addGroup: { en: 'New group', ch: '新建组' }, 12 | deleteGroup: { en: 'Remove group', ch: '删除组' }, 13 | addElement: { en: 'New item', ch: '新建元素' }, 14 | deleteElement: { en: 'Remove item', ch: '删除元素' }, 15 | create: { en: 'New layer', ch: '生成层' }, 16 | cover: { en: 'Cover item', ch: '覆盖元素' }, 17 | isShow: { en: 'Show text', ch: '显示文字' }, 18 | isAlert: { en: 'Deleting Alert', ch: '删除时警告' }, 19 | isPrecomp: { en: 'Pre-compose', ch: '预合成' }, 20 | isOffset: { en: 'Save material', ch: '存储素材' }, 21 | isName: { en: 'Auto rename', ch: '自动取名' }, 22 | isEffect: { en: 'Only property', ch: '仅生成效果' }, 23 | cleanProperty: { en: 'Empty prop', ch: '清空属性组' }, 24 | offsetKey: { en: 'Shift keyframe', ch: '关键帧偏移' }, 25 | sureDelete: { en: 'Are you sure to delete it?', ch: '确认删除?' }, 26 | helperScripts: { en: 'Help scripts', ch: '辅助脚本' }, 27 | expressionTranslate: { en: 'Fix expression errors', ch: '表达式翻译' }, 28 | script: { en: 'Sp_palette v1.0', ch: '形状层画板' }, 29 | reloadGroup: { en: 'Reload previews of group', ch: '重载组内预览动画' }, 30 | saveEachLayer: { en: 'Save every layer in active comp', ch: '自动存储每一层' }, 31 | cutLength: { en: 'Cut layer length', ch: '裁剪层长度' }, 32 | blankName: { en: 'Name should not be empty!', ch: '名字不应为空!' }, 33 | existName: { en: 'Element with the same name exists already!', ch: '相同名字的元素已存在!' }, 34 | overWritten: { en: 'File with the same name exists already!', ch: '相同名字的文件已存在!' }, 35 | inputName: { en: 'Please input your name!', ch: '请输入名字!' }, 36 | alertSpe: { en: 'There are special symbols in selectedLayers,please rename them first!', ch: '选中层名字有特殊符号,请首先重命名选中层!' }, 37 | deleteFolder: { en: 'Empty temp folder', ch: '清空素材文件夹' }, 38 | changeGroupName: { en: 'Change name of group', ch: '重命名选中组' }, 39 | deleteOk: { en: 'Clean folder successfully!', ch: '清空文件夹完毕!' }, 40 | yushe: { en: 'Preset Setting', ch: '预设设置' }, 41 | jinOne: { en: 'Please select groups that will be created on selectedLayers', ch: '请选择在仅生成效果时要在选中层上生成的属性组' }, 42 | jinTwo: { en: 'Please select groups that will be empty on selectedLayers before creating Properties', ch: '请选择在仅生成效果之前要清空的选中层的属性组' }, 43 | isSureGroup: { en: 'What you are deleting is a Group.\rAre you sure?', ch: '你正在删除的是一个组.\r确定删除吗?' }, 44 | isSureGroup2: { en: 'Repeat!\rWhat you are deleting is a Group.\rAre you sure?\r', ch: '重复!\r你正在删除的是一个组.\r确定删除吗?' }, 45 | _1: { en: 'Mask', ch: '遮罩' }, 46 | _2: { en: 'Effect', ch: '效果' }, 47 | _3: { en: 'Transform', ch: '变换' }, 48 | _4: { en: 'Material options', ch: '3D材质选项' }, 49 | _5: { en: 'Layer styles', ch: '图层样式' }, 50 | _6: { en: 'Shape content', ch: '形状层形状组' }, 51 | _7: { en: 'Text animators', ch: '文字层动画器' }, 52 | _8: { en: 'Light options', ch: '灯光选项' }, 53 | _9: { en: 'Camera options', ch: '摄像机选项' }, 54 | setName: { en: 'Please input the name.', ch: '请输入名字' }, 55 | checkVersion: { en: 'Check version', ch: '检查更新' }, 56 | newVersionFind: { en: 'New version found,please download the new version ', ch: '存在新版本,请下载最新版v' }, 57 | newVersionNotFind: { en: 'No new version! v', ch: '已是最新版 v' }, 58 | link: { en: 'Weibo', ch: '作者微博' }, 59 | about: { 60 | en: 61 | `Made by:smallpath 62 | E-mail:smallpath2013@gmail.com 63 | Source Code: 64 | github.com/smallpath/memory 65 | 66 | DoubleClick:generate new layers or properties on selected layers from selected element. 67 | RightClick:call the shortcut menu. 68 | Ctrl/Alt+RightClick:save selected layers as a new element. 69 | Shift+Rightclick:call the up and down window 70 | 71 | Shortcutkey when script runs as Window: 72 | Key 'D' or 'Delete':delete selected element. 73 | Key 'F': overlap selected element. 74 | Key 'Up':drop up selected element. 75 | Key 'Down':drop down selected element.`, 76 | ch: 77 | `作者: 78 | smallpath 79 | 邮箱: 80 | smallpath2013@gmail.com 81 | 源码托管地址: 82 | github.com/smallpath/memory 83 | 84 | 右键点击:呼出右键菜单. 85 | 双击:从选中元素创建层或创建效果. 86 | Ctrl/Alt+右键点击:从选中的层读取层信息以创建新元素. 87 | Shift+右键:唤出移动元素的窗口 88 | 89 | 窗口模式运行脚本时: 90 | D键:删除选中元素. 91 | F键:覆盖选中元素. 92 | 上键:上移选中元素. 93 | 下键:下移选中元素.` 94 | }, 95 | refresh: { 96 | en: `Please run this script to refresh pictures only when your group has been created with wrong thumbnails(such as all black)\rIt will spent a lot of time.\rNew thumbnails will be created at the time of active comp,so set your comp's time first.`, 97 | ch: `生成组内所有元素的预览动画: 98 | ##请用本功能对非3.x版本保存的组进行生成预览动画的操作: 99 | 100 | 此功能将生成组内所有元素的主缩略图和预览动画,其中主缩略图为当前合成的当前时间点的画面 101 | 102 | 注意:此功能将耗费大量时间,脚本会弹出图片文件夹,你可以根据其中的图片判断预览动画的生成进度 103 | ` 104 | }, 105 | auto: { 106 | en: `This script helps you simplify you saving proccess\rIt will save every layer in active comp as a new element.`, 107 | ch: `批量存储功能: 108 | 109 | 这会将当前合成中每一层都分别存储为一个新元素. 110 | 111 | 此功能可以帮助你快速存储新元素,十分适合存储大量的MG合成层 112 | 脚本会弹出图片文件夹,你可以根据其中的图片来判断预览动画的生成进度 113 | `}, 114 | cutLengthTwo: { 115 | en: 'This script will cut every layer in current comp, related to opacity for common layer and content length for comp layer.', 116 | ch: '此功能将会裁剪当前合成中每一层的长度,根据普通层的透明度与合成层内容的长度.' 117 | }, 118 | output: { en: 'Export groups', ch: '批量导出组' }, 119 | ok: { en: 'Ok', ch: '确定' }, 120 | cancel: { en: 'Cancel', ch: '取消' }, 121 | complete: { en: 'Complete!', ch: '导出完成!' }, 122 | showText: { en: 'Show text', ch: '显示文字' }, 123 | ui1: { en: 'The newer UI', ch: '新界面' }, 124 | ui2: { en: 'The older UI', ch: '旧界面' }, 125 | sys: { en: 'Script find that Sp_memory v1.4 has been used the first time.\rPlease select the UI type,Yes for new UI and No for previous UI.', ch: '脚本检测到Sp_memory v1.4首次被使用.\r请选择脚本界面,Yes为新界面,No为旧界面.' }, 126 | uiC: { en: 'Please restart script,ui will be changed.', ch: '界面已更新,请重启脚本' }, 127 | from: { en: 'Range is 0.', ch: '元素下标范围为:0' }, 128 | ud: { en: 'Up and down', ch: '上下移动选中元素' }, 129 | up: { en: 'Up', ch: '上移' }, 130 | down: { en: 'Down', ch: '下移' }, 131 | jmp: { en: 'Jump', ch: '跳转' }, 132 | coverChange: { en: 'Update thumb when cover', ch: '覆盖时更新缩略图' }, 133 | folderName: { en: 'The folder name of collect feature:', ch: '收集生成层时的工程栏文件夹名:' }, 134 | effectName: { en: 'The group name that enable Only property :', ch: '默认开启仅生成效果的组名:' }, 135 | limitText: { en: 'Limit the text for UI', ch: '限制主窗口界面的文字长度' }, 136 | scriptSetting: { en: 'Setting', ch: '设置' }, 137 | settingPre: { en: 'Preference', ch: '预设' }, 138 | thumbType: { en: 'Enable new type of thumb', ch: '缩略图包含合成栏图层轮廓' }, 139 | addModule: { en: 'New module', ch: '新建模块' }, 140 | deleteModule: { en: 'Remove module', ch: '删除模块' }, 141 | deleteModuleAlert: { 142 | en: 'Dangerous!\r\nYou are deleting a module!\r\nAll groups in this module will be removed!\r\nDo you really want to remove this module?', 143 | ch: '警告!\r\n你正在删除一个模块!\r\n所有包含在此模块中的组都将被删除!\r\n你想要继续删除吗?' 144 | }, 145 | addAlert: { en: 'Repeart:\r\n', ch: '重复:\r\n' }, 146 | move: { en: 'Cut selected group to other module', ch: '剪切选中组到其他模块' }, 147 | editModule: { en: 'Move module or rename module', ch: '改变模块顺序或重命名模块' }, 148 | changeModuleName: { en: 'Change module name', ch: '重命名选中模块' }, 149 | moduleHelpTip: { en: "press key 'Up' and 'Down can move the selected module' ", ch: '方向上下键可移动选中模块' }, 150 | quit: { en: 'Quit', ch: '退出' }, 151 | selectGroupFirst: { en: 'Please select a group first!', ch: '请先选中一个组!' }, 152 | selectModuleFirst: { en: 'Please select a module first!', ch: '请先选中一个模块!' }, 153 | frameSecondText: { en: 'The milliseconds length of frame continues when preview:', ch: '预览时一张图片持续的毫秒数:' }, 154 | frameNumText: { en: 'The number of picture sequence generated for preview', ch: '生成供预览的图片序列时图片的数量:' }, 155 | reloadNeedFrames: { 156 | en: "Please input the max frames which will be used to correct the duration of Preview.Keep blank if you don't what this feature", 157 | ch: '请输入最大帧数,这将被用来使预览动画的时间范围更加准确\r\n不输入则将不进行校准' 158 | }, 159 | needComp: { en: 'Please select a comp first', ch: '脚本需要一个合成,当前合成不存在!' }, 160 | previewAll: { en: 'Preview all', ch: '预览全部' }, 161 | previewSelected: { en: 'Preview selected', ch: '预览选中' }, 162 | needElement: { en: 'Please select a element in the group', ch: '组内元素未被选中,请首先选中一个元素' }, 163 | needElements: { en: 'Please select at least one element in the group', ch: '组内元素未被选中,请至少选中一个元素' }, 164 | needLayers: { en: 'Please select at least one layer in the current comp', ch: '请选中至少一个层' }, 165 | needModule: { en: 'Please create a module first', ch: '请先新建一个模块' }, 166 | isSavePreview: { en: 'Save preview', ch: '存储预览' }, 167 | searchWindow: { en: 'Search', ch: '搜索' }, 168 | getReport: { en: 'Get report', ch: '生成报告' }, 169 | creatingReport: { en: 'Creating cost: ', ch: '生成层耗时: ' }, 170 | creatingProcessTitle: { en: 'Now generating...', ch: '少女祈祷中...' }, 171 | creatingProcessingPrefix: { en: 'Processing the ', ch: '正在生成第 ' }, 172 | creatingProcessAfter: { en: ' layer', ch: ' 层' }, 173 | savingReport: { en: 'Saving cost: ', ch: '总存储耗时: ' }, 174 | savingProcessTitle: { en: 'Now saving...', ch: '少女祈祷中...' }, 175 | savingProcessingPrefix: { en: 'Processing the ', ch: '正在存储第 ' }, 176 | savingProcessAfter: { en: ' layer', ch: ' 层' }, 177 | second: { en: ' second', ch: ' 秒' }, 178 | previewTitle: { en: 'Save preview', ch: '少女祈祷中...' }, 179 | previewPrefix: { en: 'Saving preview: ', ch: '正在存储预览图片: ' }, 180 | previewTime: { en: 'Saving cost: ', ch: '存储预览耗时: ' }, 181 | searchButton: { en: 'search', ch: '搜索' }, 182 | searchText: { en: 'input name', ch: '输入元素名称' }, 183 | setRatioText: { en: 'notify the scale of UI for high-DPI windows', ch: '设置主界面windows放大比例' }, 184 | setRatioHelptip: { 185 | en: 'AE scriptUI may be scaled wrong in high-DPI windows from CC2013 to CC2015.0', 186 | ch: 'windows文字缩放比例大于1时, AE脚本界面会自动放大, 导致本脚本界面越界' 187 | }, 188 | setRatioWarning: { 189 | en: 'Please only change it when your text ratio does not equal to 1. Restart script to make sense', 190 | ch: '请仅当你的windows文字缩放比例不为1且本脚本界面越界的情况下, 才修改此参数, 重启脚本后生效' 191 | }, 192 | saveWorkarea: { 193 | en: 'Workarea', 194 | ch: '预览工作区' 195 | }, 196 | tryVersionFind: { 197 | en: 'It seems that you are using the beta version which is not released yet. v', 198 | ch: '未发现新版本, 你正在使用尚未发布的试用版 v' 199 | }, 200 | shouldUpdateScript: { 201 | en: 'Would you like to upgrade to new version now?\r\n it will cost some time while ae will not response\r\n', 202 | ch: '现在开始更新新版本吗?\r\n\r\n脚本大小为300KB, 下载时AE会停止响应数十秒时间.\r\n选否则可以选择通过浏览器下载' 203 | }, 204 | shouldDownloadScript: { 205 | en: 'Would you like to download new version now?', 206 | ch: '是否通过浏览器自行下载最新版本?\r\n打开网页后右键另存为脚本文件即可' 207 | }, 208 | downloaded: { 209 | en: 'Update success! To make it work, just restart script', 210 | ch: '升级成功, 请重启脚本' 211 | }, 212 | generalOption: { 213 | en: 'General', 214 | ch: '一般选项' 215 | }, 216 | otherOption: { 217 | en: 'Other', 218 | ch: '其他' 219 | }, 220 | sourceCode: { 221 | en: 'Source Code', 222 | ch: '脚本源码' 223 | }, 224 | addIssue: { 225 | en: 'Report bug', 226 | ch: '上报错误' 227 | }, 228 | issueDesc: { 229 | en: 'Notice that error log is in Sp_memory/tempFile/error.txt', 230 | ch: '核心错误日志在Sp_memory/tempFile/error.txt当中, 这可以帮助作者定位错误\r\n\r\n你可以在Github或贴吧中报告错误, 在Github上报的错误将会被优先解决\r\n\r\n选择"Yes"前往Github, 选择"No"前往贴吧' 231 | }, 232 | checkVersionOnStartupText: { 233 | en: 'Check version on startup', 234 | ch: '脚本启动时检查更新' 235 | } 236 | }) 237 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /lib/UIParser.js: -------------------------------------------------------------------------------- 1 | $.global.UIParser = UIParser 2 | function UIParser(global) { 3 | var _ = global._ = function(selector) { 4 | if (_.isUI(selector.type)) { 5 | return _.extend([selector], _.proto) 6 | } 7 | return _.proto.find(selector) 8 | } 9 | _.global = global 10 | 11 | _.root = { 12 | children: [] 13 | } 14 | _.windows = _.root.children 15 | _.extend = function(target, source) { // give the source to target 16 | for (var i in source) target[i] = source[i] 17 | return target 18 | } 19 | _.dir = function(obj) { 20 | var str = '' 21 | for (var i in obj) str += i + ' : ' + typeof (obj[i]) + '\n' 22 | return str 23 | } 24 | _.removeWin = function(id) { 25 | for (var i = 0; i < _.windows.length; i++) { 26 | if (_.windows[i].id === id) _.windows.splice(i, 1) 27 | } 28 | } 29 | _.proto = {// 这里的函数将赋给返回的解析器对象 30 | find: function(selector, recursive) { // find函数用来返回一个递归匹配后的数组,并且将_.proto的函数也给这个数组 31 | var matchs = [] 32 | var elements = ('length' in this) ? this : [_.root] 33 | if (!selector) return _.extend(elements, _.proto) 34 | 35 | // 选择器为选择器表达式 36 | if (typeof selector === 'string') { 37 | var selectors = _.formalSelector(selector)// 正规化选择器 38 | for (var i = 0; i < selectors.length; i++) { 39 | var match = elements 40 | var process = _.parserSelector(selectors[i])// 解析选择器 41 | for (var j = 0; j < process.length; j++) { // 逐步执行 42 | if (!process[j][3] && _.proto[process[j][4]]) { 43 | match = _.proto[process[j][4]].call(match, process[j][5])// 如果有:标记执行过滤操作 44 | } else { 45 | match = _.findElementsByProp(match, process[j][0], process[j][1], process[j][2]) 46 | } 47 | } 48 | matchs = _.merge(match, matchs) 49 | } 50 | } else if (typeof selector === 'function') { 51 | if (!recursive) recursive = 1 52 | matchs = _.findElementsByFn(elements, selector, recursive) 53 | } 54 | 55 | return _.extend(matchs, _.proto) 56 | }, 57 | filter: function(selector) { 58 | var matchs = [] 59 | var elements = ('length' in this) ? this : [_.root] 60 | if (!selector) return _.extend(elements, _.proto) 61 | 62 | // 选择器为选择器表达式 63 | if (typeof selector === 'string') { 64 | var selectors = _.formalSelector(selector)// 正规化选择器 65 | for (var i = 0; i < selectors.length; i++) { 66 | var match = elements 67 | var process = _.parserSelector(selectors[i])// 解析选择器 68 | for (var j = 0; j < process.length; j++) { // 逐步执行 69 | if (!process[j][3] && _.proto[process[j][4]]) { 70 | match = _.proto[process[j][4]].call(match, process[j][5])// 如果有:标记执行过滤操作 71 | } else { 72 | match = _.findElementsByProp(match, process[j][0], process[j][1]) 73 | } 74 | } 75 | matchs = _.merge(match, matchs) 76 | } 77 | } else if (typeof selector === 'function') { 78 | matchs = _.filterElementsByFn(elements, selector) 79 | } 80 | 81 | return _.extend(matchs, _.proto) 82 | }, 83 | style: function(style, target) { 84 | if (!target) target = this 85 | for (var i = 0; i < target.length; i++) { 86 | for (var j in style) { 87 | if (target[i].type === j) _.proto.style(style[j], [target[i]]) 88 | else target[i][j] = style[j] 89 | } 90 | } 91 | }, 92 | each: function(command) { for (var i = 0; i < this.length; i++) command(this[i]) }, // command is a function 93 | setAttr: function(prop, value) { this.each(function(e) { e[prop] = value }) }, 94 | getAttr: function(prop) { if (this.length) return this[0][prop] }, 95 | children: function(selector) { return this.find(selector || '>*') }, 96 | parent: function() { if (this.length > 0 && this[0].parent) return this[0].parent; else return _.extend([], _.proto) }, 97 | on: function(event, fn, useCapture) { this.each(function(e) { e.addEventListener(event, fn, useCapture) }) }, 98 | exe: function(fn, args) { this.each(function(e) { e[fn].apply(e, args) }) }, 99 | addUI: function() { return _.addUI.apply(this[0], arguments) }, 100 | first: function() { return _.extend([this[0]], _.proto) }, 101 | last: function() { return _.extend([this[this.length - 1]], _.proto) }, 102 | eq: function(index) { if (index < this.length && index >= 0) { return _.extend([this[index]], _.proto) } else { return _.extend([], _.proto) } }, 103 | layout: function() { this.each(function(e) { _.layout(e) }) }, 104 | remove: function() { this.each(function(e) { e.parent.remove(e) }) }, 105 | empty: function() { this.children().remove() } 106 | } 107 | /** ***********************functions for createUI****************************************************/ 108 | _.createUI = function(UIJson) { // 创建UI 109 | if (!UIJson) return 110 | var ISPANEL = global instanceof Panel 111 | if (ISPANEL) { 112 | var _newElement = _.addUI(UIJson, global) 113 | _.root.children.push(global) 114 | global.layout.layout(true) 115 | return _newElement 116 | } else { 117 | return _.newWindow(UIJson) 118 | } 119 | } 120 | 121 | _.newWindow = function(UIJson) { // 添加窗口 122 | if (!UIJson) return 123 | var newWindows = [] 124 | for (var i in UIJson) { 125 | var json = UIJson[i] 126 | if (_.isWindow(UIJson[i].type)) { 127 | // create window 128 | var s = json.type 129 | if (json.properties) s += '{properties:' + _.JSON.stringify(json.properties) + '}' 130 | var newWindow = _.root.children[_.root.children.length] = new Window(s) 131 | newWindows.push(newWindow) 132 | if (!json.id) newWindow.id = i 133 | // add other properties for newWindow 134 | for (var j in json) { 135 | if (j === 'type' || j === 'properties' || j === 'children') continue 136 | newWindow[j] = json[j] 137 | } 138 | // create children for newWindow 139 | if (json.children) _.addUI(json.children, newWindow) 140 | } 141 | } 142 | return _.extend(newWindows, _.proto) 143 | } 144 | 145 | _.addUI = function(UIJson, parent) { // 为parent添加UI 146 | if (!UIJson) return 147 | if (!parent) parent = this 148 | 149 | var newItem = [] 150 | for (var i in UIJson) { 151 | var json = UIJson[i] 152 | if (_.isElement(json.type)) { 153 | // create element 154 | var s = json.type 155 | if (json.properties) s += '{properties:' + _.JSON.stringify(json.properties) + '}' 156 | var newElement = parent.add(s) 157 | if (!json.id) newElement.id = i 158 | // add other properties for newElement 159 | for (var j in json) { 160 | if (j === 'type' || j === 'properties' || j === 'children') continue 161 | newElement[j] = json[j] 162 | } 163 | newItem.push(newElement) 164 | // create children for newElement 165 | if (_.isContainer(json.type) && json.children) arguments.callee(json.children, newElement) 166 | } 167 | } 168 | return _.extend(newItem, _.proto) 169 | } 170 | 171 | _.isWindow = function(type) { // 判断是否为window元素 172 | var winType = [ 173 | 'window', 'palette', 'dialog', 174 | 'Window', 'Palette', 'Dialog' 175 | ] 176 | var len = winType.length 177 | for (var i = 0; i < len; i++) { 178 | if (type === winType[i]) return true 179 | } 180 | return false 181 | } 182 | 183 | _.isContainer = function(type) { // 判断是否为容器 184 | var winType = [ 185 | 'window', 'palette', 'dialog', 'group', 'panel', 'tabbedpanel', 'treeview', 'dropdownlist', 'listbox', 'listitem', 'tab', 'node', 186 | 'Window', 'Palette', 'Dialog', 'Group', 'Panel', 'TabbedPanel', 'Treeview', 'DropDownList', 'ListBox', 'ListItem', 'Tab', 'Node' 187 | ] 188 | var len = winType.length 189 | for (var i = 0; i < len; i++) { 190 | if (type === winType[i]) return true 191 | } 192 | return false 193 | } 194 | 195 | _.isElement = function(type) { // 判断是否是window元素外的其他UI元素 196 | var winType = [ 197 | 'panel', 'tabbedpanel', 'tab', 'group', 'button', 'checkbox', 'dropdownlist', 'edittext', 'flashplayer', 'iconbutton', 'image', 'item', 'listbox', 'listitem', 'progressbar', 'radiobutton', 'scrollbar', 'slider', 'statictext', 'treeview', 'tab', 'node', 198 | 'Panel', 'TabbedPanel', 'Tab', 'Group', 'Button', 'CheckBox', 'DropDownList', 'EditText', 'FlashPlayer', 'IconButton', 'Image', 'Item', 'ListBox', 'ListItem', 'ProgressBar', 'RadioButton', 'Scrollbar', 'Slider', 'StaticText', 'Treeview', 'Tab', 'Node' 199 | ] 200 | var len = winType.length 201 | for (var i = 0; i < len; i++) { 202 | if (type === winType[i]) return true 203 | } 204 | return false 205 | } 206 | 207 | _.isUI = function(type) { // 判断是否为UI元素 208 | if (_.isWindow(type) || _.isElement(type)) return true 209 | return false 210 | } 211 | /** ********************functions for find*********************************************************/ 212 | _.findElementsByProp = function(elements, prop, value, recursive) { 213 | var matchs = [] 214 | for (var i = 0; i < elements.length; i++) { 215 | if (elements[i].children) var atoms = elements[i].children 216 | else if (elements[i].items) atoms = elements[i].items 217 | else continue 218 | var match = [] 219 | for (var j = 0; j < atoms.length; j++) { 220 | if (atoms[j][prop] && (value === '' || atoms[j][prop].toString() === value)) { match.push(atoms[j]) } 221 | if (recursive && (atoms[j].children || atoms[j].items)) { 222 | var temp = arguments.callee([atoms[j]], prop, value, 1) 223 | match = _.merge(temp, match) 224 | } 225 | } 226 | matchs = _.merge(match, matchs) 227 | } 228 | return matchs 229 | } 230 | _.findElementsByFn = function(elements, fn, recursive) { 231 | var match = [] 232 | for (var i = 0; i < elements.length; i++) { 233 | if (elements[i].children) var atoms = elements[i].children 234 | else if (elements[i].items) atoms = elements[i].items 235 | else continue 236 | for (var j = 0; j < atoms.length; j++) { 237 | if (fn(atoms[j])) match.push(atoms[j]) 238 | if (recursive && (atoms[j].children || atoms[j].items)) { 239 | var temp = arguments.callee(atoms[j].children, fn, 1) 240 | match = _.merge(temp, match) 241 | } 242 | } 243 | } 244 | return match 245 | } 246 | _.filterElementByProp = function(elements, prop, value) { 247 | var matchs = [] 248 | for (var i = 0; i < elements.length; i++) { 249 | if (elements[i][prop] && (value === '' || elements[i][prop].toString() === value)) { matchs.push(elements[i]) } 250 | } 251 | return matchs 252 | } 253 | _.filterElementByFn = function(elements, fn) { 254 | var matchs = [] 255 | for (var i = 0; i < elements.length; i++) { 256 | if (fn(elements[i])) matchs.push(elements[i]) 257 | } 258 | return matchs 259 | } 260 | _.formalSelector = function(selector) { // 正规化选择器,去掉所有空格,多余字母和多余标记符,得到并列选择器,这时每个选择器中的每个标记符均有效 261 | /** 262 | 1.去掉空格])及后面的字母,如'[er t ]a:w (w)e'变为'[er:w(w' 263 | 2.去掉标记符前面的所有标记符号,遇到*>,不删除,因为标记号*>,后面不需要字母 264 | 3.将*及其后面的字母替换为* 265 | 4.将,及其后面的字母替换为, 266 | 5.将>及其后面的字母替换为> 267 | 6.将开始处的字母及逗号去掉 268 | 7.用逗号分隔选择器,得到正规化的若干个选择器 269 | 8.返回选择器数组 270 | */ 271 | return selector.replace(/[\s\]\)]\w*/g, '').replace(/[\#\.\[\:\=]+(?=[\#\.\[\]\,\:\=\>\*])/g, '').replace(/\*+\w*/g, '*').replace(/\,+\w*/g, ',').replace(/\>+\w*/g, '>').replace(/^\w*\,/g, '').split(/\,/g) 272 | } 273 | _.parserSelector = function(selector) { // 解析单个的选择器,返回一个表示过程的数组 274 | var sign, content, prop, value, func, param, doFind // recursive是否递归,doFind是否查找,否则过滤操作 275 | var recursive = 1 276 | var process = [] 277 | var parts = selector.replace(/(?=[\#\.\[\:\>\*])/g, '@').replace(/^\@/, '').split('@')// 将选择器根据标记分开 278 | 279 | for (var i = 0; i < parts.length; i++) { 280 | if (parts[i] === '>') { // 当出现>的时候find函数将不会递归 281 | recursive = 0 282 | i++ 283 | } 284 | // 初始化 285 | sign = parts[i][0] 286 | content = parts[i].substr(1) 287 | prop = value = func = param = '' 288 | doFind = 1 289 | // 判断 290 | switch (sign) { 291 | case '*': prop = 'type'; break 292 | case '#': prop = 'id'; value = content; break 293 | case '.': prop = 'type'; value = content; break 294 | case '[': 295 | var p = content.split('=') 296 | prop = p[0] 297 | if (p.length === 2) value = p[1] 298 | break 299 | case ':': 300 | var fn = content.split('(') 301 | func = fn[0] 302 | if (fn.length === 2) param = fn[1] 303 | doFind = 0 304 | break 305 | } 306 | process.push([prop, value, recursive, doFind, func, param]) 307 | recursive = 1 308 | } 309 | 310 | return process 311 | } 312 | _.merge = function(newArray, oldArray) { // 合并两个数组,并且去掉重复元素 313 | var temp = [] 314 | var b = 1 315 | for (var i = 0; i < newArray.length; i++) { 316 | for (var j = 0; j < oldArray.length; j++) { 317 | if (newArray[i] === oldArray[j]) { 318 | b = 0 319 | break 320 | } 321 | } 322 | if (b) temp.push(newArray[i]) 323 | } 324 | return oldArray.concat(temp) 325 | } 326 | /** ********************layout functions*********************************************************/ 327 | _.layout = function(e) { // 默认布局方式 328 | e.margins = 0 329 | e.spacing = 5 330 | if (e.align) { 331 | switch (e.align) { 332 | case 'fill': 333 | case 'fill_fill': e.alignment = ['fill', 'fill']; break 334 | 335 | case 'center': 336 | case 'center_center': e.alignment = ['center', 'center']; break 337 | 338 | case 'left_fill': 339 | case 'left': e.alignment = ['left', 'fill']; break 340 | case 'center_fill': e.alignment = ['center', 'fill']; break 341 | case 'right_fill': 342 | case 'right': e.alignment = ['right', 'fill']; break 343 | 344 | case 'fill_top': 345 | case 'top': e.alignment = ['fill', 'top']; break 346 | case 'fill_center': e.alignment = ['fill', 'center']; break 347 | case 'fill_bottom': 348 | case 'bottom': e.alignment = ['fill', 'bottom']; break 349 | 350 | case 'left_center': e.alignment = ['left', 'center']; break 351 | case 'right_center': e.alignment = ['right', 'center']; break 352 | case 'center_top': e.alignment = ['center', 'top']; break 353 | case 'center_bottom': e.alignment = ['center', 'bottom']; break 354 | 355 | case 'left_top': e.alignment = ['left', 'top']; break 356 | case 'left_bottom': e.alignment = ['left', 'bottom']; break 357 | case 'right_top': e.alignment = ['right', 'top']; break 358 | case 'right_bottom': e.alignment = ['right', 'bottom']; break 359 | } 360 | } 361 | } 362 | _.extend(_.windows, _.proto) 363 | /** ********************other functions*********************************************************/ 364 | _.JSON = { // 直接copy过来的代码 365 | /* eslint-disable no-eval */ 366 | parse: function(strJSON) { return eval('(' + strJSON + ')') }, 367 | stringify: (function() { 368 | var toString = Object.prototype.toString 369 | var isArray = Array.isArray || function(a) { return toString.call(a) === '[object Array]' } 370 | var escMap = {'"': '\\"', '\\': '\\\\', '\b': '\\b', '\f': '\\f', '\n': '\\n', '\r': '\\r', '\t': '\\t'} 371 | var escFunc = function(m) { return escMap[m] || '\\u' + (m.charCodeAt(0) + 0x10000).toString(16).substr(1) } 372 | var escRE = /[\\"\u0000-\u001F\u2028\u2029]/g 373 | return function stringify(value) { 374 | if (value == null) { 375 | return 'null' 376 | } else if (typeof value === 'number') { 377 | return isFinite(value) ? value.toString() : 'null' 378 | } else if (typeof value === 'boolean') { 379 | return value.toString() 380 | } else if (typeof value === 'object') { 381 | if (typeof value.toJSON === 'function') { 382 | return stringify(value.toJSON()) 383 | } else if (isArray(value)) { 384 | var res = '[' 385 | for (var i = 0; i < value.length; i++) res += (i ? ', ' : '') + stringify(value[i]) 386 | return res + ']' 387 | } else if (toString.call(value) === '[object Object]') { 388 | var tmp = [] 389 | for (var k in value) { 390 | if (value.hasOwnProperty(k)) tmp.push(stringify(k) + ': ' + stringify(value[k])) 391 | } 392 | return '{' + tmp.join(', ') + '}' 393 | } 394 | } 395 | return '"' + value.toString().replace(escRE, escFunc) + '"' 396 | } 397 | })() 398 | } 399 | 400 | return _ 401 | }; 402 | -------------------------------------------------------------------------------- /src/ui/settingWindow.js: -------------------------------------------------------------------------------- 1 | var moduleWindow = require('./moduleWindow') 2 | var moveGroupWindow = require('./moveGroupWindow') 3 | var outputGroupWindow = require('./outputGroupWindow') 4 | var checkVersion = require('../https/checkVersion') 5 | 6 | module.exports = function() { 7 | var _ = $.global.UIParser($.global) 8 | 9 | var UIJson = { 10 | newWin: { 11 | type: 'palette', 12 | text: sp.scriptName + ' v' + sp.scriptVersion, 13 | margins: 10, 14 | orientation: 'row', 15 | children: { 16 | leftGroup: { 17 | type: 'group', 18 | orientation: 'column', 19 | alignment: ['fill', 'fill'], 20 | alignChildren: ['fill', 'fill'], 21 | children: { 22 | group1: { 23 | type: 'group', 24 | orientation: 'row', 25 | alignment: ['fill', 'fill'], 26 | alignChildren: ['fill', 'fill'], 27 | children: { 28 | helpText: { 29 | type: 'edittext', 30 | properties: { 31 | multiline: true, 32 | scrolling: false 33 | }, 34 | preferredSize: [150, 280], 35 | text: '', 36 | enabled: 1 37 | }, 38 | gr: { 39 | type: 'group', 40 | orientation: 'column', 41 | alignment: ['fill', 'fill'], 42 | alignChildren: ['fill', 'fill'], 43 | margins: 0, 44 | spacing: 0, 45 | children: { 46 | drop: { 47 | type: 'dropdownlist', 48 | preferredSize: [150, 20] 49 | }, 50 | wlist: { 51 | type: 'listbox', 52 | preferredSize: [150, 260] 53 | } 54 | } 55 | } 56 | } 57 | }, 58 | group2: { 59 | type: 'group', 60 | orientation: 'row', 61 | alignment: ['fill', 'fill'], 62 | alignChildren: ['fill', 'fill'], 63 | children: { 64 | deleteFolder: { 65 | type: 'Button', 66 | preferredSize: [165, 27], 67 | text: loc(sp.deleteFolder), 68 | enabled: 1 69 | }, 70 | changeGroupName: { 71 | type: 'Button', 72 | preferredSize: [165, 27], 73 | text: loc(sp.changeGroupName), 74 | enabled: 1 75 | } 76 | } 77 | }, 78 | group3: { 79 | type: 'group', 80 | alignment: ['fill', 'fill'], 81 | alignChildren: ['fill', 'fill'], 82 | children: { 83 | output: { 84 | type: 'Button', 85 | text: loc(sp.output), 86 | enabled: 1 87 | }, 88 | move: { 89 | type: 'Button', 90 | text: loc(sp.move), 91 | enabled: 1 92 | } 93 | } 94 | }, 95 | group35: { 96 | type: 'group', 97 | orientation: 'row', 98 | alignment: ['fill', 'fill'], 99 | alignChildren: ['fill', 'fill'], 100 | children: { 101 | editModule: { 102 | type: 'Button', 103 | preferredSize: [330, 27], 104 | text: loc(sp.editModule), 105 | enabled: 1 106 | } 107 | } 108 | } 109 | } 110 | }, 111 | rightGroup: { 112 | type: 'group', 113 | orientation: 'column', 114 | alignment: ['top', 'fill'], 115 | alignChildren: ['fill', 'fill'], 116 | children: { 117 | group4: { 118 | type: 'panel', 119 | text: loc(sp.generalOption), 120 | orientation: 'column', 121 | alignment: ['fill', 'fill'], 122 | alignChildren: ['fill', 'fill'], 123 | children: { 124 | g0: { 125 | type: 'group', 126 | orientation: 'row', 127 | alignment: ['fill', 'fill'], 128 | alignChildren: ['fill', 'fill'], 129 | children: { 130 | gr1: { 131 | type: 'group', 132 | children: { 133 | limitText: { 134 | type: 'checkbox', 135 | text: loc(sp.limitText) 136 | } 137 | } 138 | }, 139 | gr2: { 140 | type: 'group', 141 | children: { 142 | checkVersionOnStartup: { 143 | type: 'checkbox', 144 | text: loc(sp.checkVersionOnStartupText) 145 | } 146 | } 147 | } 148 | } 149 | }, 150 | gr1: { 151 | type: 'group', 152 | orientation: 'row', 153 | alignment: ['fill', 'fill'], 154 | alignChildren: ['fill', 'fill'], 155 | children: { 156 | gr1: { 157 | type: 'group', 158 | children: { 159 | thumbType: { 160 | type: 'checkbox', 161 | text: loc(sp.thumbType) 162 | } 163 | } 164 | }, 165 | gr2: { 166 | type: 'group', 167 | children: { 168 | coverChange: { 169 | type: 'checkbox', 170 | text: loc(sp.coverChange) 171 | } 172 | } 173 | } 174 | } 175 | }, 176 | grRatio: { 177 | type: 'group', 178 | alignment: ['fill', 'fill'], 179 | alignChildren: ['fill', 'fill'], 180 | children: { 181 | setRatio: { 182 | type: 'statictext', 183 | text: loc(sp.setRatioText) 184 | }, 185 | ratioText: { 186 | type: 'edittext', 187 | text: '', 188 | characters: 10 189 | } 190 | } 191 | }, 192 | gr4: { 193 | type: 'group', 194 | alignment: ['fill', 'fill'], 195 | alignChildren: ['fill', 'fill'], 196 | children: { 197 | frameSecond: { 198 | type: 'statictext', 199 | text: loc(sp.frameSecondText) 200 | }, 201 | frameSecondText: { 202 | type: 'edittext', 203 | text: '', 204 | characters: 10 205 | } 206 | } 207 | }, 208 | gr5: { 209 | type: 'group', 210 | alignment: ['fill', 'fill'], 211 | alignChildren: ['fill', 'fill'], 212 | children: { 213 | frameNum: { 214 | type: 'statictext', 215 | text: loc(sp.frameNumText) 216 | }, 217 | frameNumText: { 218 | type: 'edittext', 219 | text: '', 220 | characters: 10 221 | } 222 | } 223 | }, 224 | gr0: { 225 | type: 'group', 226 | orientation: 'row', 227 | alignment: ['fill', 'fill'], 228 | alignChildren: ['fill', 'fill'], 229 | children: { 230 | gr2: { 231 | type: 'group', 232 | alignment: ['fill', 'fill'], 233 | alignChildren: ['fill', 'fill'], 234 | children: { 235 | folderName: { 236 | type: 'statictext', 237 | text: loc(sp.folderName) 238 | }, 239 | folderNameText: { 240 | type: 'edittext', 241 | text: '', 242 | justify: 'center', 243 | characters: 10 244 | } 245 | } 246 | } 247 | } 248 | }, 249 | gr3: { 250 | type: 'group', 251 | alignment: ['fill', 'fill'], 252 | alignChildren: ['fill', 'fill'], 253 | children: { 254 | effectName: { 255 | type: 'statictext', 256 | text: loc(sp.effectName) 257 | }, 258 | effectNameText: { 259 | type: 'edittext', 260 | text: '', 261 | characters: 14 262 | } 263 | } 264 | } 265 | } 266 | }, // end of group4 267 | group5: { 268 | type: 'panel', 269 | text: loc(sp.otherOption), 270 | orientation: 'column', 271 | alignment: ['fill', 'fill'], 272 | alignChildren: ['fill', 'fill'], 273 | children: { 274 | group5: { 275 | type: 'group', 276 | orientation: 'row', 277 | alignment: ['fill', 'fill'], 278 | alignChildren: ['fill', 'fill'], 279 | children: { 280 | ch: { 281 | type: 'Button', 282 | text: '中文', 283 | enabled: 0 284 | }, 285 | en: { 286 | type: 'Button', 287 | text: 'English', 288 | enabled: 0 289 | } 290 | } 291 | }, 292 | group6: { 293 | type: 'group', 294 | orientation: 'row', 295 | alignment: ['fill', 'fill'], 296 | alignChildren: ['fill', 'fill'], 297 | children: { 298 | sourceCode: { type: 'Button', text: loc(sp.sourceCode), enabled: 1 }, 299 | openLink: { 300 | type: 'Button', 301 | text: loc(sp.link), 302 | enabled: 1 303 | } 304 | } 305 | }, 306 | group7: { 307 | type: 'group', 308 | orientation: 'row', 309 | alignment: ['fill', 'fill'], 310 | alignChildren: ['fill', 'fill'], 311 | children: { 312 | checkVersion: { 313 | type: 'Button', 314 | text: loc(sp.checkVersion), 315 | enabled: 1 316 | }, 317 | addIssue: { type: 'Button', text: loc(sp.addIssue), enabled: 1 } 318 | } 319 | } 320 | } 321 | } 322 | } 323 | } 324 | 325 | } 326 | } // end of newWin 327 | 328 | } 329 | 330 | var win = _.newWindow(UIJson)[0] 331 | 332 | _('*').each(function(e) { 333 | switch (e.id) { 334 | default: 335 | if (e.type !== 'checkbox') break 336 | e.value = sp.getSettingAsBool(e.id) 337 | var name = e.id + 'Value' 338 | e.onClick = function() { 339 | sp[name] = this.value 340 | } 341 | break 342 | case 'addIssue': 343 | e.onClick = function() { 344 | if (sp.lang === 'ch') { 345 | var shouldOpen = confirm(loc(sp.issueDesc)) 346 | if (shouldOpen) { 347 | sp.openLink(sp.githubIssue) 348 | } else { 349 | sp.openLink(sp.issueLink) 350 | } 351 | } else { 352 | alert(loc(sp.issueDesc)) 353 | sp.openLink(sp.githubIssue) 354 | } 355 | } 356 | break 357 | case 'sourceCode': 358 | e.onClick = function() { 359 | sp.openLink(sp.sourceCodeLink) 360 | } 361 | break 362 | case 'ratioText': 363 | e.text = (1 / sp.gridViewScale).toString() 364 | e.onChange = function() { 365 | alert(loc(sp.setRatioHelptip) + '\r\n' + loc(sp.setRatioWarning)) 366 | var value = parseFloat(this.text) 367 | if (isNaN(value) || value < 1) { 368 | this.text = (1 / sp.gridViewScale).toString() 369 | return 370 | } 371 | 372 | sp.gridViewScale = 1 / value 373 | sp.saveSetting('gridViewScale', sp.gridViewScale.toString()) 374 | sp.gv.refresh() 375 | } 376 | break 377 | 378 | case 'frameSecondText': 379 | e.text = sp.frameSecond.toString() 380 | e.onChange = function() { 381 | if (isNaN(this.text)) { 382 | this.text = sp.frameSecond 383 | return 384 | } 385 | 386 | var value = parseInt(this.text) 387 | if (value >= 200) value = 200 388 | if (value <= 33) value = 33 389 | sp.frameSecond = value 390 | sp.saveSetting('frameSecond', value) 391 | this.text = value.toString() 392 | } 393 | break 394 | case 'frameNumText': 395 | e.text = sp.frameNum.toString() 396 | e.onChange = function() { 397 | if (isNaN(this.text)) { 398 | this.text = sp.frameNum 399 | return 400 | } 401 | 402 | var value = parseInt(this.text) 403 | if (sp.isCC2015) { 404 | if (value >= 300) value = 300 405 | } else { 406 | if (value >= 50) value = 50 407 | } 408 | if (value <= 0) value = 0 409 | sp.frameNum = value 410 | sp.saveSetting('frameNum', value) 411 | this.text = value.toString() 412 | } 413 | break 414 | case 'move': 415 | e.onClick = function() { 416 | if (!_('#wlist')[0].selection || !_('#drop')[0]) return alert(loc(sp.selectGroupFirst)) 417 | moveGroupWindow(_('#wlist')[0].selection, _('#drop')[0].selection, win) 418 | } 419 | break 420 | case 'editModule': 421 | e.onClick = function() { 422 | if (!_('#drop')[0]) return alert(loc(sp.selectModuleFirst)) 423 | moduleWindow(_('#drop')[0].selection, win, module.exports) 424 | } 425 | break 426 | case 'drop': 427 | sp.xmlGroupNames.forEach(function(item, index) { 428 | this.add('item', item) 429 | }, e) 430 | var ratio = 1 / sp.gv.scale - 1 431 | var addedSeparatorLength = Math.ceil(ratio * sp.xmlGroupNames.length) 432 | for (var i = 0; i < addedSeparatorLength; i++) { 433 | e.add('separator') 434 | } 435 | var wlist = _('#wlist')[0] 436 | e.onChange = function() { 437 | if (!this.selection) return 438 | if (!sp.parentDroplist.selection) return 439 | wlist.removeAll() 440 | sp.parentDroplist.selection = this.selection.index 441 | sp.xmlCurrentFileNames.forEach(function(item, index) { 442 | this.add('item', item) 443 | }, wlist) 444 | sp.gv.refresh() 445 | } 446 | e.selection = sp.parentDroplist.selection ? sp.parentDroplist.selection.index : 0 447 | break 448 | case 'helpText': 449 | e.text = loc(sp.about) 450 | e.onChange = e.onChanging = function() { 451 | this.text = loc(sp.about) 452 | } 453 | break 454 | case 'wlist': 455 | break 456 | case 'deleteFolder': 457 | e.onClick = function() { 458 | var folder = sp.materialFolder 459 | sp.deleteThisFolder(folder) 460 | alert(loc(sp.deleteOk)) 461 | } 462 | break 463 | case 'changeGroupName': 464 | e.onClick = function() { 465 | var wlist = _('#wlist')[0] 466 | if (!wlist.selection) return alert(loc(sp.selectGroupFirst)) 467 | var newGroupName = prompt(loc(sp.setName), wlist.selection.text) 468 | if (!newGroupName) return 469 | if (sp.xmlFileNames.includes(newGroupName)) { 470 | alert(loc(sp.existName)) 471 | return 472 | } 473 | 474 | var file = sp.getFileByName(wlist.selection.text) 475 | file.rename(newGroupName + '.xml') 476 | var xml = new XML(sp.settingsFile.readd()) 477 | var index = sp.getGlobalIndexFromFileName(wlist.selection.text) 478 | xml.ListItems.insertChildAfter(xml.ListItems.child(index), 479 | new XML('' + newGroupName.toString() + '')) 480 | xml.ListItems.child(index).setLocalName('waitToDelete') 481 | delete xml.ListItems.waitToDelete 482 | sp.settingsFile.writee(xml) 483 | var folder = sp.getImageFolderByName(wlist.selection.text) 484 | if (folder.exists) { folder.rename(newGroupName) } 485 | wlist.items[wlist.selection.index].text = newGroupName 486 | sp.droplist.items[wlist.selection.index].text = newGroupName 487 | sp.xmlFileNames[index] = newGroupName 488 | sp.droplist.notify('onChange') 489 | } 490 | break 491 | case 'output': 492 | e.onClick = function() { 493 | outputGroupWindow() 494 | } 495 | break 496 | case 'limitText': 497 | e.value = sp.getSettingAsBool('limitText') 498 | e.onClick = function() { 499 | sp.saveSetting('limitText', this.value.toString()) 500 | sp.gv.limitText = sp.getSettingAsBool('limitText') 501 | sp.gv.refresh() 502 | } 503 | break 504 | 505 | case 'folderNameText': 506 | e.text = sp.getSetting('folderName') 507 | e.onChange = function() { 508 | sp.saveSetting('folderName', this.text) 509 | } 510 | break 511 | case 'effectNameText': 512 | e.text = sp.getSetting('effectName') 513 | e.onChange = function() { 514 | sp.saveSetting('effectName', this.text) 515 | } 516 | break 517 | case 'ch': 518 | e.enabled = sp.lang === 'en' 519 | if (e.enabled === true) { e.enabled = !sp.isForceEnglish() } 520 | e.onClick = function() { 521 | sp.saveSetting('language', 'ch') 522 | alert('请重新打开脚本,语言会将自动变更为中文.') 523 | _('#en')[0].enabled = true 524 | _('#ch')[0].enabled = false 525 | } 526 | break 527 | case 'en': 528 | e.enabled = sp.lang === 'ch' 529 | e.onClick = function() { 530 | sp.saveSetting('language', 'en') 531 | alert('Please restart script,language will be changed into English.') 532 | _('#en')[0].enabled = false 533 | _('#ch')[0].enabled = true 534 | } 535 | break 536 | case 'checkVersion': 537 | if (sp.lang === 'en') { e.size = _('#openLink')[0].size = [211, 27] } 538 | e.onClick = checkVersion(win) 539 | break 540 | case 'openLink': 541 | e.onClick = function() { 542 | sp.openLink(sp.weiboLink) 543 | } 544 | break 545 | } 546 | }) 547 | 548 | var warpDrop = function(a, b, index1, index2) { 549 | var tempD = a.text 550 | a.text = b.text 551 | b.text = tempD 552 | var tempXML = sp.xmlCurrentFileNames[index1] 553 | sp.xmlCurrentFileNames[index1] = sp.xmlCurrentFileNames[index2] 554 | sp.xmlCurrentFileNames[index2] = tempXML 555 | } 556 | 557 | var exchange = function(isUp, wXML) { 558 | var xmlIndex = _('#wlist')[0].selection.index 559 | var groupIndex = _('#drop')[0].selection.index 560 | var name = sp.droplist.selection.text 561 | 562 | if (isUp === true) { 563 | var wupxml = new XML(wXML.ParentGroup.child(groupIndex).child(xmlIndex)) 564 | wXML.ParentGroup.child(groupIndex).insertChildBefore(wXML.ParentGroup.child(groupIndex).child(xmlIndex - 1), wupxml) 565 | 566 | wXML.ParentGroup.child(groupIndex).child(xmlIndex + 1).setLocalName('waitToDelete') 567 | 568 | delete wXML.ParentGroup.child(groupIndex).waitToDelete 569 | 570 | sp.settingsFile.writee(wXML) 571 | sp.swap(_('#wlist')[0].items[xmlIndex - 1], _('#wlist')[0].items[xmlIndex]) 572 | warpDrop(sp.droplist.items[xmlIndex - 1], sp.droplist.items[xmlIndex], xmlIndex - 1, xmlIndex) 573 | } else { 574 | var wdownxml = new XML(wXML.ParentGroup.child(groupIndex).child(xmlIndex)) 575 | 576 | wXML.ParentGroup.child(groupIndex).insertChildAfter(wXML.ParentGroup.child(groupIndex).child(xmlIndex + 1), wdownxml) 577 | wXML.ParentGroup.child(groupIndex).child(xmlIndex).setLocalName('waitToDelete') 578 | delete wXML.ParentGroup.child(groupIndex).waitToDelete 579 | 580 | sp.settingsFile.writee(wXML) 581 | sp.swap(_('#wlist')[0].items[xmlIndex + 1], _('#wlist')[0].items[xmlIndex]) 582 | warpDrop(sp.droplist.items[xmlIndex + 1], sp.droplist.items[xmlIndex], xmlIndex + 1, xmlIndex) 583 | } 584 | sp.droplist.selection = sp.droplist.find(name) 585 | sp.droplist.notify('onChange') 586 | sp.gv.refresh() 587 | } 588 | 589 | var handleKey = function(key, control) { 590 | var wXML = new XML(sp.settingsFile.readd()) 591 | switch (key.keyName) { 592 | case 'Up': 593 | if (_('#wlist')[0].selection !== null && _('#wlist')[0].selection.index > 0 && _('#drop')[0].selection) { 594 | exchange(true, wXML) 595 | }; 596 | break 597 | case 'Down': 598 | if (_('#wlist')[0].selection !== null && _('#wlist')[0].selection.index < _('#wlist')[0].items.length - 1 && _('#drop')[0].selection) { 599 | exchange(false, wXML) 600 | }; 601 | break 602 | } 603 | } 604 | 605 | _('#wlist')[0].addEventListener('keydown', function(k) { 606 | handleKey(k, this) 607 | }) 608 | 609 | win.center() 610 | win.show() 611 | } 612 | -------------------------------------------------------------------------------- /lib/GridView.js: -------------------------------------------------------------------------------- 1 | /** ***********************************新界面网格视图的构造函数****************************************/ 2 | $.global.GridView = GridView 3 | function GridView(parent, attrs) { 4 | var keepRef = this 5 | /** **********************继承函数************************************************************/ 6 | this.extend = function(target, source) { 7 | for (var i in source) target[i] = source[i] 8 | return target 9 | } 10 | /** *********************item 构造器***********************************************************************/ 11 | this.item = function(text, image, parent) { // item 构造器 12 | if (!text) text = '' 13 | if (!image) image = null 14 | keepRef.extend(this, { 15 | id: 'item', 16 | index: 0, 17 | type: 'item', 18 | image: image, 19 | text: text, 20 | selected: false, 21 | prev: null, 22 | next: null, 23 | parent: parent, 24 | backgroundColor: null, 25 | strokeColor: null, 26 | rect: [0, 0, 100, 60], // item的矩阵信息 27 | imageRect: [0, 0, 100, 60], // item中图片的矩阵信息,为相对rect的量 28 | fontRect: [0, 90, 100, 10] // item中文字背景的矩阵信息,为相对rect的量 29 | }) 30 | /** *****************item 原型****************************************************************************/ 31 | keepRef.extend(this, { 32 | remove: function(notRefreshList) { // 移除元素, 若notRefreshList为true,默认不刷新列表 33 | var e = this.parent 34 | var prev = this.prev 35 | var next = this.next 36 | if (prev) { 37 | prev.next = next 38 | if (next) { 39 | next.prev = prev 40 | } 41 | } else { 42 | next.prev = null 43 | } 44 | 45 | if (this === e.lastSelectedItem) e.lastSelectedItem = null 46 | e.getChildren() 47 | e.getSelection() 48 | 49 | if (!notRefreshList) e.refresh() 50 | }, 51 | moveUp: function() { // 元素上移 52 | try { 53 | var e = this.parent 54 | var prev = this.prev 55 | var next = this.next 56 | if (prev) { 57 | if (prev.prev) { prev.prev.next = this } 58 | this.prev = prev.prev 59 | this.next = prev 60 | prev.prev = this 61 | prev.next = next 62 | if (next) { next.prev = prev } 63 | } 64 | if (this.prev === null) { e.first = this } 65 | e.getChildren() 66 | e.refresh() 67 | } catch (err) { 68 | alert(err.line.toString()) 69 | } 70 | }, 71 | moveDown: function() { // 元素下移 72 | try { 73 | var e = this.parent 74 | var prev = this.prev 75 | var next = this.next 76 | if (next) { 77 | var right = this.next.next 78 | if (prev) { prev.next = next } 79 | next.prev = prev 80 | next.next = this 81 | this.prev = next 82 | this.next = right 83 | if (right) { right.prev = this } 84 | } 85 | if (next.prev === null) { e.first = next } 86 | e.getChildren() 87 | e.refresh() 88 | } catch (err) { 89 | alert(err.line.toString()) 90 | } 91 | }, 92 | moveBefore: function(item) { // 将当前item移动到指定item之前 93 | var e = this.parent 94 | this.remove(1) 95 | if (this.next) { this.next.prev = this.prev } 96 | if (this.prev) { this.prev.next = this.next } 97 | this.next = item 98 | this.prev = item.prev 99 | if (item.prev) { 100 | item.prev.next = this 101 | } 102 | item.prev = this 103 | if (this.prev === null) { e.first = this } 104 | e.getChildren() 105 | e.refresh() 106 | }, 107 | moveAfter: function(item) { // 将当前item移动到指定item之后 108 | var e = this.parent 109 | this.remove(1) 110 | this.prev.next = this.next 111 | this.next.prev = this.prev 112 | this.prev = item 113 | this.next = item.next 114 | if (item.next) { 115 | item.next.prev = this 116 | } 117 | item.next = this 118 | 119 | e.getChildren() 120 | e.refresh() 121 | } 122 | }) 123 | } 124 | 125 | /** *********************网格视图的默认属性***********************************************************************/ 126 | this.extend(this, { 127 | id: 'GridView', 128 | type: 'GridView', 129 | 130 | listHeight: 400, // 列表的总高度 131 | scale: 1, // 列表缩放 132 | backgroundColor: [0.15, 0.15, 0.15], // 背景色 133 | scrollBlockColor: [0.16, 0.16, 0.16], // 滚动条滑块颜色 134 | scrollBarColor: [0.08, 0.08, 0.08], // 滚动条背景颜色 135 | scrollBarWidth: 17, // 滚动条宽度 136 | scrollBarValue: 0, // 滚动条值 137 | scrollBlockRect: [0, 0, 20, 100], // 滚动条滑块的矩阵信息 138 | scrollScale: 1, // 滚动条是否显示,为0时滚动条消失(不可设置) 139 | spacing: [3, 3], // item的间距 140 | itemBackgroundColor: [0, 0, 0, 0], // item的背景颜色 141 | itemStrokeColor: [0.2, 0.2, 0.2, 0], // item的边框描边颜色 142 | itemSelectedColor: [38 / 255, 38 / 255, 38 / 255], // item被选中时的颜色 143 | itemSelectedRecColor: [0.2, 0.7, 1], // item被选中时的颜色 144 | itemFontColor: [1, 1, 1], // item的字体颜色 145 | // itemFontColor: [0.023, 0.023, 0.023],//item的字体颜色 146 | itemSize: [100, 60], // item的大小 147 | itemStrokeSize: 1.6, // item的边框描边大小 148 | itemFontHeight: 0, // item的字体大小 149 | itemFontSize: 20, // item的字体大小 150 | showText: false, // 是否显示文字 151 | limitText: false, 152 | version: 'CC2014', 153 | first: null, // 第一个item 154 | last: null, // 最后一个item 155 | children: [], // 所有的item 156 | selection: [], // 被选中的item 157 | lastSelectedItem: null, // 最后一次被选中的item 158 | 159 | leftClick: function(event) { }, // 左键单击事件 160 | leftDoubleClick: function(event) { }, // 左键双击事件 161 | rightClick: function(event) { }, // 右键单击事件 162 | rightDoubleClick: function(event) { }, // 右键双击事件 163 | mouseMove: function(event) { }, // 鼠标移动事件 164 | mouseOut: function(event) { } 165 | }) 166 | 167 | /** *******************网格视图的原型*************************************************************************/ 168 | this.extend(this, { 169 | add: function(text, image) { // 添加元素 170 | var newItem = new this.item(text, image, this) 171 | if (this.first) { 172 | this.last.next = newItem 173 | newItem.prev = this.last 174 | this.children.push(newItem) 175 | this.last = this.children[this.children.length - 1] 176 | this.last.index = this.last.prev.index + 1 177 | } else { 178 | this.first = this.last = newItem 179 | this.children.push(newItem) 180 | this.first.index = 0 181 | } 182 | 183 | this.getSelection() 184 | 185 | return newItem 186 | }, 187 | removeAll: function() { // 移除所有元素 188 | this.first = this.last = this.lastSelectedItem = null 189 | this.selection = this.children = [] 190 | }, 191 | getChildren: function() { // 获取子元素 192 | var children = [] 193 | var item = this.first 194 | var index = 0 195 | 196 | while (item) { 197 | children.push(item) 198 | item.index = index 199 | item = item.next 200 | index++ 201 | } 202 | 203 | this.children = children 204 | if (children.length) { 205 | this.first = children[0] 206 | this.last = children[children.length - 1] 207 | } 208 | return children 209 | }, 210 | getSelection: function() { // 获取选中元素 211 | var selection = [] 212 | var item = this.first 213 | 214 | while (item) { 215 | if (item.selected) selection.push(item) 216 | item = item.next 217 | } 218 | 219 | this.selection = selection 220 | return selection 221 | }, 222 | create: function(parent) { // 创建网格视图,包括监听在内 223 | var e = this 224 | var GV = e.GV = parent.add("group{orientation: 'stack', alignment: ['fill','fill'], margins: 0, spacing: 0}") 225 | var list = e.list = GV.add("button{alignment:['fill','fill']}") 226 | var eventRect = e.eventRect = GV.add("group{alignment:['fill','fill']}") 227 | var screen = $.screens[0].toString().split('-').pop().split(':') 228 | GV.maximumSize = 229 | list.maximumSize = 230 | eventRect.maximumSize = [parseInt(screen[0]), parseInt(screen[1])] 231 | 232 | eventRect.addEventListener('mousedown', function(event) { 233 | e.event.mouseMoving = false 234 | e.event.targetScrollBar = e.getScrollBarFromLocation(event.clientX, event.clientY) 235 | e.event.targetItem = e.getItemFromLocation(event.clientX, event.clientY) 236 | if (event.button === 0) { // 左键 237 | if (event.detail === 1) { // 左键单击 238 | e.event.leftButtonPressed = true 239 | e.event.leftButtonPressedLocation = [event.clientX, event.clientY] 240 | e.event.leftButtonPressedScrollBarValue = e.scrollBarValue 241 | /***************************************/ 242 | if (event.ctrlKey === false) { e.mouseMove(event, e.event.targetItem, true) } 243 | 244 | /***************************************/ 245 | } else if (event.detail === 2) { // 左键双击 246 | e.leftDoubleClick(event) 247 | } 248 | } else if (event.button === 2) { // 右键 249 | if (event.detail === 1) { // 右键单击 250 | e.event.rightButtonPressed = true 251 | e.event.rightButtonPressedLocation = [event.clientX, event.clientY] 252 | e.event.rightButtonPressedScrollBarValue = e.scrollBarValue 253 | /***************************************/ 254 | 255 | /***************************************/ 256 | } else if (event.detail === 2) { // 右键双击 257 | } 258 | } 259 | }) 260 | eventRect.addEventListener('mousemove', function(event) { 261 | e.event.mouseMoving = true 262 | if (e.event.leftButtonPressed) { // 左键移动 263 | /***************************************/ 264 | e.defaultLeftMouseMove(event) 265 | e.refresh() 266 | /***************************************/ 267 | } else if (e.event.rightButtonPressed) { // 右键移动 268 | 269 | /***************************************/ 270 | 271 | /***************************************/ 272 | } 273 | if (event.ctrlKey === false) { e.mouseMove(event, e.getItemFromLocation(event.clientX, event.clientY)) } 274 | }) 275 | eventRect.addEventListener('mouseup', function(event) { 276 | if (e.event.leftButtonPressed) { // 左键 277 | if (e.event.mouseMoving) { 278 | /***************************************/ 279 | e.defaultLeftClick(event) 280 | e.leftClick(event) 281 | /***************************************/ 282 | } else { 283 | e.defaultLeftClick(event) 284 | e.leftClick(event) 285 | } 286 | } else if (e.event.rightButtonPressed) { // 右键 287 | if (e.event.mouseMoving) { 288 | /***************************************/ 289 | e.rightClick(event) 290 | /***************************************/ 291 | } else if (event.detail === 1) { 292 | e.rightClick(event) 293 | } else if (event.detail === 2) { 294 | e.rightDoubleClick(event) 295 | } 296 | } 297 | e.event.leftButtonPressed = false 298 | e.event.rightButtonPressed = false 299 | e.event.mouseMoving = false 300 | e.event.targetScrollBar = false 301 | e.refresh() 302 | }) 303 | /* 304 | eventRect.addEventListener('mouseout', function(event) { 305 | e.event.leftButtonPressed = false; 306 | e.event.rightButtonPressed = false; 307 | e.event.mouseMoving = false; 308 | e.event.targetScrollBar = false; 309 | e.mouseOut(); 310 | e.refresh(); 311 | }); 312 | */ 313 | 314 | list.onDraw = e.listDraw 315 | list.GV = e 316 | }, 317 | alignment: function(alignment) { 318 | this.GV.alignment = alignment 319 | }, 320 | size: function(size) { 321 | this.GV.size = size 322 | this.list.size = size 323 | this.eventRect.size = size 324 | }, 325 | location: function(location) { 326 | this.GV.location = location 327 | }, 328 | listDraw: function() { // 重绘函数 329 | var e = this.GV 330 | var g = this.graphics 331 | var items = e.children 332 | 333 | var bgBrush = g.newBrush(g.BrushType.SOLID_COLOR, e.backgroundColor) 334 | var itemBgBrush = g.newBrush(g.PenType.SOLID_COLOR, e.itemBackgroundColor) 335 | var strokePen = g.newPen(g.PenType.SOLID_COLOR, e.itemStrokeColor, e.itemStrokeSize) 336 | var selectedPen = g.newPen(g.PenType.SOLID_COLOR, e.itemSelectedRecColor, e.itemStrokeSize) 337 | var selectedBrush = g.newBrush(g.PenType.SOLID_COLOR, e.itemSelectedColor) 338 | var scrollBarBrush = g.newBrush(g.PenType.SOLID_COLOR, e.scrollBarColor) 339 | var scrollBlockBrush = g.newBrush(g.PenType.SOLID_COLOR, e.scrollBlockColor) 340 | 341 | if (e.showText) e.itemFontHeight = e.itemFontSize 342 | else e.itemFontHeight = 0 343 | e.relocationItems() 344 | e.resizeItems() 345 | e.resizeScrollBar() 346 | /******************************************************************************************/ 347 | // 绘制背景 348 | g.newPath() 349 | g.rectPath(0, 0, e.list.size[0] - e.scrollBarWidth * e.scale * e.scrollScale, e.list.size[1]) 350 | g.fillPath(bgBrush) 351 | /******************************************************************************************/ 352 | var shouldDrawArr = [] 353 | var pointArr = [] 354 | var limit = e.GV.size[1] 355 | 356 | for (var i = 0; i < items.length; i++) { 357 | var target = items[i] 358 | var height = target.rect[1] + target.imageRect[1] - e.scrollBarValue + target.imageRect[3] 359 | var shouldDraw = height > 0 && height - target.imageRect[3] + 10 < limit 360 | shouldDrawArr.push(shouldDraw) 361 | pointArr.push( 362 | [ 363 | target.rect[0], 364 | target.rect[1], 365 | target.rect[2], 366 | target.rect[3], 367 | target.rect[1] - e.scrollBarValue 368 | ] 369 | ) 370 | } 371 | // item背景色填充 372 | g.newPath() 373 | for (i = 0; i < items.length; i++) { 374 | var item = items[i] 375 | var rect = pointArr[i] 376 | if (items[i].backgroundColor) continue 377 | 378 | g.rectPath( 379 | rect[0], 380 | rect[4], 381 | rect[2], 382 | rect[3] - item.fontRect[3] 383 | ) 384 | } 385 | g.fillPath(itemBgBrush) 386 | // 添加item指定的背景色 387 | for (i = 0; i < items.length; i++) { 388 | item = items[i] 389 | rect = pointArr[i] 390 | if (item.backgroundColor) { 391 | var brush = g.newBrush(g.PenType.SOLID_COLOR, item.backgroundColor) 392 | g.newPath() 393 | g.rectPath( 394 | rect[0], 395 | rect[4], 396 | rect[2], 397 | rect[3] 398 | ) 399 | g.fillPath(brush) 400 | } 401 | // item图片绘制 402 | if (item.image && shouldDrawArr[i]) { 403 | var image = ScriptUI.newImage(item.image) 404 | g.drawImage( 405 | image, 406 | rect[0] + item.imageRect[0], 407 | rect[1] + item.imageRect[1] - e.scrollBarValue, 408 | item.imageRect[2], 409 | item.imageRect[3] 410 | ) 411 | } 412 | } 413 | 414 | /******************************************************************************************/ 415 | // item描边 416 | g.newPath() 417 | for (i = 0; i < items.length; i++) { 418 | item = items[i] 419 | if (!item.selected) { 420 | if (item.strokeColor) continue 421 | if (shouldDrawArr[i]) { 422 | rect = pointArr[i] 423 | g.rectPath( 424 | rect[0], 425 | rect[4], 426 | rect[2], 427 | rect[3] 428 | ) 429 | } 430 | } 431 | } 432 | g.strokePath(strokePen) 433 | // 为指定了描边颜色的item描边 434 | for (i = 0; i < items.length; i++) { 435 | item = items[i] 436 | if (item.strokeColor && shouldDrawArr[i]) { 437 | rect = pointArr[i] 438 | var pen = g.newPen(g.PenType.SOLID_COLOR, item.strokeColor, e.itemStrokeSize) 439 | g.newPath() 440 | g.rectPath( 441 | rect[0], 442 | rect[4], 443 | rect[2], 444 | rect[3] 445 | ) 446 | g.strokePath(pen) 447 | } 448 | } 449 | 450 | if (e.showText) { 451 | // 为被选中的item文字添加背景色 452 | g.newPath() 453 | for (i = 0; i < items.length; i++) { 454 | item = items[i] 455 | if (item.selected && shouldDrawArr[i]) { 456 | g.rectPath( 457 | item.rect[0] + item.fontRect[0] + 1, 458 | item.rect[1] + item.fontRect[1] - e.scrollBarValue, 459 | item.fontRect[2], 460 | item.fontRect[3] 461 | ) 462 | } 463 | } 464 | g.fillPath(selectedBrush) 465 | } 466 | // 为被选中的item描边 467 | g.newPath() 468 | for (i = 0; i < items.length; i++) { 469 | item = items[i] 470 | if (item.selected && shouldDrawArr[i]) { 471 | rect = pointArr[i] 472 | g.rectPath( 473 | rect[0], 474 | rect[4], 475 | rect[2], 476 | rect[3] 477 | ) 478 | } 479 | } 480 | g.strokePath(selectedPen) 481 | 482 | /******************************************************************************************/ 483 | if (e.showText) { 484 | // item文字 485 | var fontPen = g.newPen(g.PenType.SOLID_COLOR, e.itemFontColor, e.itemFontSize * e.scale) 486 | var font = ScriptUI.newFont('Microsoft YaHei', ScriptUI.FontStyle.REGULAR, e.itemFontSize * e.scale * 0.6) 487 | 488 | var trickWidthForCC2014 = 20 489 | var trickForOther = 0 490 | for (i = 0; i < items.length; i++) { 491 | item = items[i] 492 | if (!shouldDrawArr[i]) continue 493 | var textRect = g.measureString(item.text, font) 494 | var thisText = item.text 495 | var totalText = item.text 496 | var base = textRect.width - item.imageRect[2] - e.spacing[0] * 2 497 | if (e.limitText === true && e.version === 'CC2014') { 498 | if (base >= -trickWidthForCC2014) { 499 | for (var j = 0; j < item.text.length; j++) { 500 | thisText = item.text.slice(0, totalText.length - 2 - j) 501 | textRect = g.measureString(thisText, font) 502 | var newBase = textRect.width - item.imageRect[2] - e.spacing[0] * 2 503 | if (newBase < -trickWidthForCC2014) { 504 | break 505 | } 506 | } 507 | } 508 | } else if (e.version !== 'CC2014') { 509 | if (base >= trickForOther) { 510 | for (j = 0; j < item.text.length; j++) { 511 | thisText = item.text.slice(0, totalText.length - 2 - j) 512 | textRect = g.measureString(thisText, font) 513 | newBase = textRect.width - item.imageRect[2] - e.spacing[0] * 2 514 | if (newBase < trickForOther) { 515 | break 516 | } 517 | } 518 | } 519 | } 520 | 521 | var value = parseInt(thisText) 522 | if (!isNaN(value) && value.toString() === thisText) { 523 | // it's a number, add a space after it to avoid location bug in drawString method 524 | thisText += ' ' 525 | } 526 | if (e.version === 'CC2014') { 527 | g.drawString( 528 | thisText, 529 | fontPen, 530 | item.rect[0] + item.fontRect[0], 531 | item.rect[1] + item.fontRect[1] - e.scrollBarValue - 10, // trick for CC2014 location bug 532 | font 533 | ) 534 | } else { 535 | g.drawString( 536 | thisText, 537 | fontPen, 538 | item.rect[0] + item.fontRect[0], 539 | item.rect[1] + item.fontRect[1] - e.scrollBarValue, 540 | font 541 | ) 542 | } 543 | } 544 | } // end of showText 545 | 546 | /******************************************************************************************/ 547 | if (e.scrollScale) { 548 | // 绘制滚动条背景 549 | g.newPath() 550 | g.rectPath( 551 | e.list.size[0] * e.scale - e.scrollBarWidth * e.scale, 552 | 0, 553 | e.scrollBarWidth * e.scale, 554 | e.list.size[1] * e.scale 555 | ) 556 | g.fillPath(scrollBarBrush) 557 | 558 | // 绘制滚动条滑块 559 | g.newPath() 560 | g.rectPath( 561 | e.scrollBlockRect[0], 562 | e.scrollBlockRect[1], 563 | e.scrollBlockRect[2], 564 | e.scrollBlockRect[3] 565 | ) 566 | g.fillPath(scrollBlockBrush) 567 | } 568 | }, 569 | resizeItems: function() { // 对所有的item的大小重新指定 570 | var e = this 571 | var items = e.children 572 | for (var i = 0; i < items.length; i++) { 573 | items[i].rect[2] = e.itemSize[0] * e.scale 574 | items[i].rect[3] = e.itemSize[1] * e.scale 575 | items[i].imageRect[2] = e.itemSize[0] * e.scale 576 | items[i].imageRect[3] = e.itemSize[1] * e.scale 577 | // items[i].imageRect = e.resizeImage(ScriptUI.newImage(items[i].image)); 578 | items[i].fontRect[0] = 0 579 | items[i].fontRect[1] = (e.itemSize[1] - e.itemFontHeight) * e.scale + 5 580 | items[i].fontRect[2] = e.itemSize[0] * e.scale 581 | items[i].fontRect[3] = 15 582 | } 583 | }, 584 | relocationItems: function() { // 对所有的item的位置重新指定 585 | var e = this 586 | var list = e.list 587 | var items = e.children 588 | e.scrollScale = 0 589 | 590 | var numWidth = Math.floor((list.size[0] * e.scale - e.scrollBarWidth * e.scale * e.scrollScale) / (e.itemSize[0] * e.scale + e.spacing[0])) 591 | if (numWidth === 0) numWidth = 1 592 | e.listHeight = Math.ceil(items.length / numWidth) * (e.itemSize[1] * e.scale + e.spacing[1]) 593 | 594 | for (var i = 0; i < items.length; i++) { 595 | items[i].rect[0] = e.spacing[0] + i % numWidth * (e.itemSize[0] * e.scale + e.spacing[0]) 596 | items[i].rect[1] = e.spacing[1] + Math.floor(i / numWidth) * (e.itemSize[1] * e.scale + e.spacing[1]) 597 | } 598 | e.scrollScale = 1 599 | }, 600 | resizeImage: function(image) { // 在不改变图片比例的情况下重新指定图片大小 601 | var e = this 602 | 603 | var WH = [e.itemSize[0], e.itemSize[1]] 604 | var wh = image.size 605 | var k = Math.min(WH[0] / wh[0], WH[1] / wh[1]) 606 | var xy 607 | wh = [k * wh[0], k * wh[1]] 608 | xy = [(WH[0] - wh[0]) / 2, (WH[1] - wh[1]) / 2] 609 | 610 | return [xy[0] * e.scale, xy[1] * e.scale, wh[0] * e.scale, wh[1] * e.scale] 611 | }, 612 | resizeScrollBar: function() { // 重新指定滚动条的尺寸 613 | var e = this 614 | var list = e.list 615 | e.scrollBarMaxValue = e.listHeight - list.size[1] * e.scale + 7 / e.scale 616 | if (e.scrollBarMaxValue < 0) e.scrollBarValue = 0 617 | 618 | e.scrollBlockRect[0] = list.size[0] * e.scale - e.scrollBarWidth * e.scale + 1 // 这里加1是为了让滚动条有一条边线 619 | e.scrollBlockRect[2] = e.scrollBarWidth * e.scale - 2 // 这里减2是为了让滚动条有一条边线 620 | if (e.listHeight < list.size[1] * e.scale) { 621 | e.scrollScale = 0 622 | e.scrollBlockRect[3] = list.size[1] * e.scale 623 | } else { 624 | e.scrollScale = 1 625 | e.scrollBlockRect[3] = list.size[1] * e.scale * list.size[1] * e.scale / e.listHeight * e.scale 626 | } 627 | e.scrollBlockRect[1] = (e.list.size[1] * e.scale - e.scrollBlockRect[3]) * e.scrollBarValue / e.scrollBarMaxValue 628 | }, 629 | defaultLeftClick: function(event) { // 默认的左键点击事件 630 | var e = this 631 | var s = e.selection 632 | var c = e.children 633 | var currentItem = e.event.targetItem 634 | if (!currentItem) { 635 | for (var i = 0; i < s.length; i++) s[i].selected = 0 636 | e.lastSelectedItem = null 637 | e.getSelection() 638 | } 639 | 640 | if (currentItem) { 641 | var preSelected = currentItem.selected 642 | if (event.ctrlKey === false) { 643 | for (i = 0; i < c.length; i++) c[i].selected = 0 644 | } 645 | if (e.lastSelectedItem && event.shiftKey === true) { 646 | var startIndex = e.lastSelectedItem.index 647 | var endIndex = currentItem.index 648 | for (i = 0; i < c.length; i++) { 649 | if ((c[i].index - startIndex) * (c[i].index - endIndex) <= 0) { // 判断e.index是否在startIndex和endIndex之间 650 | c[i].selected = 1 651 | } 652 | } 653 | } 654 | 655 | currentItem.selected = true 656 | if (e.lastSelectedItem && event.ctrlKey === true) { 657 | if (preSelected === true) { 658 | currentItem.selected = false 659 | } 660 | } else { 661 | e.lastSelectedItem = currentItem 662 | } 663 | 664 | e.getSelection() 665 | } else if (e.event.targetScrollBar === 1) { 666 | e.scrollBarValue = e.scrollBarMaxValue * event.clientY / e.list.size[1] 667 | } 668 | /** **********这里添加点击事件***************/ 669 | 670 | /********************************************/ 671 | }, 672 | defaultLeftMouseMove: function(event) { // 默认的左键移动事件 673 | var e = this 674 | if (e.event.targetScrollBar === 2) { 675 | e.scrollBarValue = (e.event.leftButtonPressedScrollBarValue + (event.clientY - e.event.leftButtonPressedLocation[1]) * e.scrollBarMaxValue / (e.list.size[1] * e.scale - e.scrollBlockRect[3])) 676 | } else { 677 | e.scrollBarValue = (e.event.leftButtonPressedScrollBarValue - event.clientY + e.event.leftButtonPressedLocation[1]) 678 | } 679 | if (e.scrollBarValue < 0) { 680 | e.scrollBarValue = 0 681 | } else if (e.scrollBarValue > e.scrollBarMaxValue) { 682 | e.scrollBarValue = e.scrollBarMaxValue 683 | } 684 | }, 685 | defaultRightMouseMove: function(event) { // 默认的右键移动事件 686 | var e = this 687 | e.scrollBarValue = e.event.rightButtonPressedScrollBarValue - event.clientY + e.event.rightButtonPressedLocation[1] 688 | 689 | if (e.scrollBarValue < 0) { 690 | e.scrollBarValue = 0 691 | } else if (e.scrollBarValue > e.scrollBarMaxValue) { 692 | e.scrollBarValue = e.scrollBarMaxValue 693 | } 694 | }, 695 | getItemFromLocation: function(x, y) { // 从点击位置获取当前item 696 | var e = this 697 | var c = e.children 698 | for (var i = 0; i < c.length; i++) { 699 | if (x - c[i].rect[0] > 0 && x - c[i].rect[0] < c[i].rect[2] && y - c[i].rect[1] + e.scrollBarValue > 0 && y - c[i].rect[1] + e.scrollBarValue < c[i].rect[3]) { return c[i] } 700 | } 701 | return null 702 | }, 703 | getScrollBarFromLocation: function(x, y) { // 当前位置不是滚动条返回0,为滚动条背景返回1,为滚动条滑块返回2 704 | var e = this 705 | 706 | if (x > e.list.size[0] * e.scale - e.scrollBarWidth) { 707 | if (y > e.scrollBlockRect[1] && y < e.scrollBlockRect[1] + e.scrollBlockRect[3]) { return 2 } 708 | return 1 709 | } 710 | return 0 711 | }, 712 | refresh: function() { // 刷新列表 713 | this.list.notify('onDraw') 714 | } 715 | }) 716 | 717 | if (attrs) this.extend(this, attrs) // 将指定属性赋值给新的网格视图 718 | 719 | this.event = { // 监听事件的辅助变量 720 | leftButtonPressed: false, // 左键是否在按下状态 721 | leftButtonPressedLocation: [0, 0], // 左键按下时的位置 722 | rightButtonPressed: false, // 右键是否在按下状态 723 | rightButtonPressedLocation: [0, 0], // 右键按下时的位置 724 | leftButtonPressedScrollBarValue: 0, // 左键在按下时的滚动条值 725 | rightButtonPressedScrollBarValue: 0, // 右键在按下时的滚动条值 726 | targetItem: null, // 左键在按下时的item目标,没有item为null 727 | targetScrollBar: 0, // 左键在按下时的滚动条目标,不是滚动条为0,滚动条背景为1,滚动条滑块为2 728 | mouseMoving: false // 鼠标是否在移动 729 | } 730 | 731 | if (parent) this.create(parent) 732 | } 733 | -------------------------------------------------------------------------------- /lib/Translate.js: -------------------------------------------------------------------------------- 1 | /** ***********************************Sp_translate v1.7,自动修复多语言版本造成的表达式报错问题**************************************/ 2 | $.global.translate = translate 3 | function translate(thisObj, expProps) { 4 | var you = this 5 | 6 | var tsp = { 7 | trans: { en: 'Translate', ch: '开始' }, 8 | allComp: { en: 'Translate all comps', ch: '翻译所有合成' }, 9 | activeComp: { en: 'Translate active comp', ch: '翻译当前合成' }, 10 | selectedComp: { en: 'Translate selected comps', ch: '翻译选中合成' }, 11 | wrongExp: { en: 'Translate wrong exps', ch: '仅翻译错误表达式' }, 12 | rightExp: { en: 'Translate right exps', ch: '仅翻译正确表达式' }, 13 | allExp: { en: 'Translate all expressions', ch: '翻译所有的表达式' }, 14 | log: { en: 'log', ch: '记录' }, 15 | about: { en: 'About', ch: '关于' }, 16 | editBtn: { en: 'Edit hot words', ch: '编辑关键词' }, 17 | str: { 18 | en: 'This script can fix wrong expressions caused by different language version of AE.By smallpath', 19 | ch: '外文工程表达式报错?本脚本可以快速解决这个问题~ @秋风_小径' 20 | }, 21 | ok: { en: 'Ok', ch: '确认' }, 22 | cancel: { en: 'Cancel', ch: '取消' }, 23 | add: { en: 'Add', ch: '增加' }, 24 | edit: { en: 'Edit', ch: '编辑' }, 25 | change: { en: 'Change', ch: '更改' }, 26 | deleteE: { en: 'Delete', ch: '删除' }, 27 | sureDelete: { en: 'Are you sure to delete it ?', ch: '确认删除吗?' }, 28 | addHelp: { 29 | en: 'Input new hot words,or choose one item to edit and delete.Make sure what you input has double quotation.', 30 | ch: '你可以新增关键词,编辑或者删除选中的条目.请确认你输入的名称有双引号' 31 | }, 32 | openFile: { en: 'Do you want to open the log file?', ch: '想要打开记录文件吗?' }, 33 | complete: { en: 'Complete!', ch: '完成!' }, 34 | _1: { en: 'This is the log file', ch: '这里是记录文件' }, 35 | _2: { 36 | en: '\r\rWrong expressions that can not be fixed at all are as follows.Check them in their comp.\r', 37 | ch: '\r\r没有被修复的表达式的位置如下,请进入合成检查' 38 | }, 39 | _3: { en: '\rComp index =', ch: '\r合成索引数 =' }, 40 | _4: { en: 'Comp name =', ch: '合成名 =' }, 41 | _5: { 42 | en: '\r\rInfo above is the expressions that has not been fixed.\r\r', 43 | ch: '上面的是没有被修复的表达式的位置.\r\r' 44 | }, 45 | _6: { 46 | en: '\r\rInfo as follows is the expressions that has been fixed.\r', 47 | ch: '下面的信息是被成功修复的表达式.' 48 | }, 49 | _7: { en: '\rComp index =', ch: '\r合成索引数' }, 50 | _8: { en: 'Comp name =', ch: '合成名 =' }, 51 | _9: { en: '\r Layer index =', ch: '\r 图层索引数 =' }, 52 | _10: { en: ' Layer name =', ch: '\r 图层名 =' }, 53 | _11: { en: ' Property name =', ch: ' 属性名 =' } 54 | 55 | } 56 | 57 | this.supportedLanguages = $.global.translate.supportedLanguages = 4 58 | this.hotWords = [] 59 | 60 | var thisFolder, thisFile 61 | if ($.layer) { 62 | thisFolder = new Folder($.layer.tempFolder.toString()) 63 | if (!thisFolder.exists) thisFolder.create() 64 | thisFile = new File(thisFolder.fullName + $.layer.slash.toString() + 'whiteList.xml') 65 | } else { 66 | thisFolder = new Folder(Folder.userData.fullName + '/Aescripts/Sp_memory') 67 | if (!thisFolder.exists) thisFolder.create() 68 | thisFile = File(thisFolder.fullName + '/whiteList.xml') 69 | } 70 | 71 | var allWords = [ 72 | ['"Angle Control"', '"角度控制"', '"角度制御"', '"ADBE Angle Control"'], 73 | ['"Checkbox Control"', '"复选框控制"', '"チェックボックス制御"', '"ADBE Checkbox Control"'], 74 | ['"Color Control"', '"色彩控制"', '"カラー制御"', '"ADBE Color Control"'], 75 | ['"Layer Control"', '"图层控制"', '"レイヤー制御"', '"ADBE Layer Control"'], 76 | ['"Point Control"', '"点控制"', '"ポイント制御"', '"ADBE Point Control"'], 77 | ['"Slider Control"', '"滑杆控制"', '"スライダー制御"', '"ADBE Slider Control"'], 78 | ['"Angle"', '"角度"', '"角度"', '"ADBE Angle Control-0001"'], 79 | ['"Checkbox"', '"检测框"', '"チェックボックス"', '"ADBE Checkbox Control-0001"'], 80 | ['"Color"', '"颜色"', '"カラー"', '"ADBE Color Control-0001"'], 81 | ['"Layer"', '"图层"', '"レイヤー"', '"ADBE Layer Control-0001"'], 82 | ['"Point"', '"点"', '"ポイント"', '"ADBE Point Control-0001"'], 83 | ['"Slider"', '"滑块"', '"スライダー"', '"ADBE Slider Control-0001"'], 84 | ['"Motion Tile"', '"动态平铺"', '"モーションタイル"', '"ADBE Tile"'], 85 | ['"Tile Width"', '"平铺宽度"', '"タイルの幅"', '"ADBE Tile-0003"'] 86 | ] 87 | if (thisFile.exists) { 88 | var content = thisFile.readd() 89 | var hahaxml = new XML(content) 90 | if (hahaxml.settings.version.toString() !== '1.6') thisFile.remove() 91 | } 92 | var iii 93 | if (!thisFile.exists || thisFile.length === -1) { 94 | var newxml = new XML('') 95 | newxml.settings.version = '1.6' 96 | newxml.settings.author = 'smallpath' 97 | for (iii = 0; iii < allWords.length; iii++) { 98 | newxml.words.words[iii] = allWords[iii] 99 | } 100 | if (sp.os === 'mac') { 101 | thisFile.encoding = 'UTF-8' 102 | } 103 | thisFile.writee(newxml) 104 | } 105 | content = thisFile.readd() 106 | var myxml = new XML(content) 107 | for (iii = 0; iii < myxml.words.words.length(); iii++) { 108 | var arr = myxml.words.words[iii].split(',') 109 | this.hotWords.push(arr) 110 | } 111 | 112 | this.changeNode = function(index, en, ch, ja, adbe) { 113 | if (en !== '' || ch !== '' || ja !== '' || adbe !== '') { 114 | if (en === '') en = 'None' 115 | if (ch === '') ch = 'None' 116 | if (ja === '') ja = 'None' 117 | if (adbe === '') adbe = 'None' 118 | 119 | content = thisFile.readd() 120 | var xml = new XML(content) 121 | xml.words.words[index] = [en, ch, ja, adbe] 122 | thisFile.writee(xml) 123 | 124 | this.hotWords = [] 125 | for (var iii = 0; iii < xml.words.words.length(); iii++) { 126 | arr = xml.words.words[iii] 127 | arr = arr.split(',') 128 | this.hotWords.push(arr) 129 | } 130 | } 131 | } 132 | 133 | this.deleteNode = function(index) { 134 | content = thisFile.readd() 135 | var deletexml = new XML(content) 136 | delete deletexml.words.words[index] 137 | thisFile.writee(deletexml) 138 | this.hotWords = [] 139 | for (var iii = 0; iii < deletexml.words.words.length(); iii++) { 140 | arr = deletexml.words.words[iii] 141 | arr = arr.split(',') 142 | this.hotWords.push(arr) 143 | } 144 | } 145 | 146 | this.addNode = function(en, ch, ja, adbe) { 147 | if (en !== '' || ch !== '' || ja !== '' || adbe !== '') { 148 | if (en === '') en = 'None' 149 | if (ch === '') ch = 'None' 150 | if (ja === '') ja = 'None' 151 | if (adbe === '') adbe = 'None' 152 | 153 | content = thisFile.readd() 154 | var addxml = new XML(content) 155 | addxml.words.words[addxml.words.words.length()] = [en, ch, ja, adbe] 156 | thisFile.writee(addxml) 157 | 158 | this.hotWords = [] 159 | for (var iii = 0; iii < addxml.words.words.length(); iii++) { 160 | arr = addxml.words.words[iii] 161 | arr = arr.split(',') 162 | this.hotWords.push(arr) 163 | } 164 | } 165 | } 166 | 167 | this.findReplace = function(prop, langId, compid) { 168 | try { 169 | var expr = prop.expression 170 | var oldExp = prop.expression 171 | if (expr !== '') { 172 | for (var l = 0; l < this.supportedLanguages; l++) { 173 | if (l !== langId) { 174 | for (var i = 0; i < this.hotWords.length; i++) { 175 | if (this.hotWords[i][l] !== 'None') { 176 | var regExp = new RegExp(this.hotWords[i][l], 'g') 177 | expr = expr.replace(regExp, this.hotWords[i][langId]) 178 | } 179 | } 180 | } 181 | } 182 | app.beginSuppressDialogs() 183 | try { 184 | prop.expression = expr 185 | } catch (e) { 186 | try { 187 | prop.expressionEnabled = true 188 | prop.valueAtTime(0, false) 189 | if (lista.selection.index === 0) { 190 | if (prop.expressionEnabled === false) { 191 | prop.expression = oldExp 192 | } 193 | } 194 | } catch (er) { 195 | // writeLn("Skip wrong expressions."); 196 | wrongcomps.push(compid) 197 | }; 198 | } 199 | app.endSuppressDialogs(false) 200 | } 201 | } catch (err) { } 202 | } 203 | 204 | Array.prototype.add = function(str) { 205 | var check = false 206 | for (var ia = 0; ia < this.length; ia++) { 207 | if (this[ia] === str) { 208 | check = true 209 | } 210 | } 211 | if (check === false) { 212 | this[this.length] = str 213 | } 214 | } 215 | 216 | function recursiveScanLayerForExpr(ref, compindex, ja) { 217 | var global = $.global.translate.helper 218 | if (ref !== null) { 219 | var prop 220 | for (var i = 1; i <= ref.numProperties; i++) { 221 | prop = ref.property(i) 222 | var isLayerStyle = prop.matchName === 'ADBE Layer Styles' && prop.canSetEnabled === false 223 | var isMaterial = prop.matchName === 'ADBE Material Options Group' && prop.propertyGroup(prop.propertyDepth).threeDLayer === false 224 | var isAudio = prop.matchName === 'ADBE Audio Group' && prop.propertyGroup(prop.propertyDepth).hasAudio === false 225 | var isExtra = prop.matchName === 'ADBE Extrsn Options Group' 226 | var isPlane = prop.matchName === 'ADBE Plane Options Group' 227 | var isVector = prop.matchName === 'ADBE Vector Materials Group' 228 | var shouldRecursiveScan = !(isLayerStyle || isMaterial || isAudio || isExtra || isPlane || isVector) 229 | if (checkb.value === true) { 230 | if ((prop.propertyType === PropertyType.PROPERTY) && (prop.expression !== '') && prop.canSetExpression && (prop.expressionEnabled === true)) { // .expressionError 231 | global.propArr.push(prop) 232 | prop.selected = true 233 | global.exps.push(prop.name) 234 | global.comps.add(compindex) 235 | global.layerTemp.add(ja) 236 | } else if ((prop.propertyType === PropertyType.INDEXED_GROUP) || (prop.propertyType === PropertyType.NAMED_GROUP)) { 237 | if (shouldRecursiveScan) { 238 | recursiveScanLayerForExpr(prop, compindex, ja) 239 | } 240 | } 241 | } else if (checka.value === true) { 242 | if ((prop.propertyType === PropertyType.PROPERTY) && (prop.expression !== '') && prop.canSetExpression && (prop.expressionEnabled === false)) { 243 | global.propArr.push(prop) 244 | prop.selected = true 245 | global.exps.push(prop.name) 246 | global.comps.add(compindex) 247 | global.layerTemp.add(ja) 248 | } else if ((prop.propertyType === PropertyType.INDEXED_GROUP) || (prop.propertyType === PropertyType.NAMED_GROUP)) { 249 | if (shouldRecursiveScan) { 250 | recursiveScanLayerForExpr(prop, compindex, ja) 251 | } 252 | } 253 | } else if (checkc.value === true) { 254 | if ((prop.propertyType === PropertyType.PROPERTY) && (prop.expression !== '') && prop.canSetExpression) { 255 | global.propArr.push(prop) 256 | prop.selected = true 257 | global.exps.push(prop.name) 258 | global.comps.add(compindex) 259 | global.layerTemp.add(ja) 260 | } else if ((prop.propertyType === PropertyType.INDEXED_GROUP) || (prop.propertyType === PropertyType.NAMED_GROUP)) { 261 | if (shouldRecursiveScan) { 262 | recursiveScanLayerForExpr(prop, compindex, ja) 263 | } 264 | } 265 | } 266 | } 267 | } 268 | } 269 | 270 | function isInId(itemid, array) { 271 | var check = false 272 | for (var ie = 0; ie < array.length; ie++) { 273 | if (itemid === array[ie]) { 274 | check = true 275 | } 276 | } 277 | return check 278 | } 279 | 280 | function ScanProjectForExpr(blackList) { 281 | var global = $.global.translate.helper = {} 282 | global.propArr = [] 283 | global.exps = [] 284 | global.layerExps = [] 285 | global.comps = [] 286 | global.layers = [] 287 | global.layerTemp = [] 288 | wrongcomps = [] 289 | var j 290 | for (var i = 1; i <= app.project.numItems; i++) { 291 | var item = app.project.item(i) 292 | if (item instanceof CompItem) { 293 | if (isInId(i, blackList) === true) { 294 | writeLn('Proccessing: ' + item.name) 295 | for (j = 1; j <= item.numLayers; j++) { 296 | item.layer(j).selected = false 297 | recursiveScanLayerForExpr(item.layer(j), i, j) 298 | if (global.exps.length !== 0) { 299 | global.layerExps.push(global.exps) 300 | } 301 | global.exps = [] 302 | } 303 | } 304 | if (global.layerTemp.length !== 0) { 305 | global.layers.push(global.layerTemp) 306 | } 307 | global.layerTemp = [] 308 | var selProps = global.propArr 309 | app.beginUndoGroup('Undo translate') 310 | for (var ic = 0; ic < selProps.length; ic++) { 311 | if (lista.selection.index === 0) { 312 | switch (app.language) { 313 | case Language.ENGLISH: 314 | you.findReplace(selProps[ic], 0, i) 315 | break 316 | case Language.CHINESE: 317 | you.findReplace(selProps[ic], 1, i) 318 | break 319 | case Language.JAPANESE: 320 | you.findReplace(selProps[ic], 2, i) 321 | break 322 | default: 323 | break 324 | } 325 | } else if (lista.selection.index === 1) { 326 | you.findReplace(selProps[ic], 0, i) 327 | } else if (lista.selection.index === 2) { 328 | you.findReplace(selProps[ic], 1, i) 329 | } else if (lista.selection.index === 3) { 330 | you.findReplace(selProps[ic], 2, i) 331 | } else if (lista.selection.index === 4) { 332 | you.findReplace(selProps[ic], 3, i) 333 | } 334 | } 335 | app.endUndoGroup() 336 | for (j = 1; j <= item.numLayers; j++) { 337 | item.layer(j).selected = false 338 | } 339 | } 340 | } 341 | return [global.comps, global.layers, global.layerExps, wrongcomps] 342 | } 343 | 344 | function searchExpression(excludeByName, expFilters) { 345 | var allExps = ScanProjectForExpr(excludeByName) 346 | return allExps 347 | } 348 | 349 | var winW = (thisObj instanceof Panel) ? thisObj : new Window('palette', 'Sp_translate v1.7', undefined, { 350 | resizeable: true 351 | }) 352 | winW.margins = 10 353 | var thisRes = `Group{ 354 | orientation:'column', 355 | alignChildren:['left','fill'], 356 | list:DropDownList{preferredSize:[200,25],properties:{items:['` + loc(tsp.allComp) + `','` + loc(tsp.activeComp) + `','` + loc(tsp.selectedComp) + `']}}, 357 | start:Button{text:'` + loc(tsp.trans) + `',preferredSize:[200,50]}, 358 | group:Group{ 359 | alignChildren:['left','fill'], 360 | checka:Checkbox{text:'` + loc(tsp.wrongExp) + `',value:0}, 361 | lista:DropDownList{properties:{items:['Default','English','中文','日本語','Common']},size:[60,25]} 362 | } 363 | groupa:Group{ 364 | alignChildren:['left','fill'], 365 | checkb:Checkbox{text:'` + loc(tsp.rightExp) + `',value:0}, 366 | about:Button{text:'` + loc(tsp.about) + `',size:[70,25]}, 367 | } 368 | groupb:Group{ 369 | alignChildren:['left','fill'], 370 | checkc:Checkbox{text:'` + loc(tsp.allExp) + `',value:1}, 371 | checkFile:Checkbox{text:'` + loc(tsp.log) + `',size:[80,10]} 372 | } 373 | addbtn:Button{text:'` + loc(tsp.editBtn) + `',preferredSize:[200,30]} 374 | }` 375 | try { var winTempA = winW.add(thisRes) } catch (err) { } 376 | winW.maximumSize.width = 220 377 | winTempA.list.selection = 1 378 | winTempA.group.lista.selection = 0 379 | var list = winTempA.list 380 | var start = winTempA.start 381 | var group = winTempA.group 382 | var checka = group.checka 383 | var lista = group.lista 384 | var groupa = winTempA.groupa 385 | var checkb = groupa.checkb 386 | var about = groupa.about 387 | var groupb = winTempA.groupb 388 | var checkc = groupb.checkc 389 | var checkFile = groupb.checkFile 390 | var addbtn = winTempA.addbtn 391 | var outFile 392 | 393 | lista.selection = 0 394 | 395 | about.onClick = function() { 396 | var text = loc(tsp.str) 397 | var wina = new Window('palette', loc(tsp.about)) 398 | var a = wina.add('edittext') 399 | a.text = text 400 | var groupa = wina.add('group') 401 | var abtn = groupa.add('button', undefined, loc(tsp.ok)) 402 | var bbtn = groupa.add('button', undefined, loc(tsp.cancel)) 403 | 404 | a.onChange = a.onChanging = function() { 405 | this.text = text 406 | } 407 | 408 | abtn.onClick = bbtn.onClick = function() { 409 | wina.close() 410 | } 411 | wina.center() 412 | wina.show() 413 | } 414 | 415 | checkFile.onClick = function() { 416 | if (checkFile.value === true) { 417 | outFile = File.saveDialog('', 'txt') 418 | if (outFile === null) checkFile.value = false 419 | } 420 | } 421 | 422 | addbtn.onClick = function() { 423 | var www = new Window('palette', loc(tsp.editBtn), undefined, { 424 | resizeable: false 425 | }) 426 | var gr1 = www.add('group') 427 | var stat1 = gr1.add('statictext', undefined, 'English ') 428 | stat1.characters = 19 429 | var stat2 = gr1.add('statictext', undefined, '中文 ') 430 | stat2.characters = 19 431 | var stat3 = gr1.add('statictext', undefined, ' 日本語') 432 | stat3.characters = 19 433 | var stat4 = gr1.add('statictext', undefined, ' Common') 434 | stat4.characters = 19 435 | var gr2 = www.add('group') 436 | var edit1 = gr2.add('edittext', undefined) 437 | edit1.characters = 19 438 | var edit2 = gr2.add('edittext', undefined) 439 | edit2.characters = 19 440 | var edit3 = gr2.add('edittext', undefined) 441 | edit3.characters = 19 442 | var edit4 = gr2.add('edittext', undefined) 443 | edit4.characters = 19 444 | var addde = www.add('group') 445 | var adda = addde.add('button', undefined, loc(tsp.add)) 446 | adda.size = [180, 40] 447 | var stackgroup = addde.add('group') 448 | stackgroup.orientation = 'stack' 449 | var addb = stackgroup.add('button', undefined, loc(tsp.edit)) 450 | addb.size = [180, 40] 451 | addb.visible = true 452 | var addchange = stackgroup.add('button', undefined, loc(tsp.change)) 453 | addchange.size = [180, 40] 454 | addchange.visible = false 455 | var addc = addde.add('button', undefined, loc(tsp.deleteE)) 456 | addc.size = [180, 40] 457 | var stackgroupa = addde.add('group') 458 | stackgroupa.orientation = 'stack' 459 | var addd = stackgroupa.add('button', undefined, loc(tsp.about)) 460 | addd.size = [180, 40] 461 | addd.visible = true 462 | var cancel = stackgroupa.add('button', undefined, loc(tsp.cancel)) 463 | cancel.size = [180, 40] 464 | cancel.visible = false 465 | var myList = www.add('listbox', undefined, '', { 466 | numberOfColumns: 5, 467 | showHeaders: true, 468 | columnTitles: ['No', 'English', '中文', '日本語', 'Common'], 469 | columnWidths: [45, 165, 165, 165, 205] 470 | }) 471 | for (var iii = 0; iii < you.hotWords.length; iii++) { 472 | var item = myList.add('item', iii + 1) 473 | for (var jjj = 0; jjj < you.hotWords[iii].length; jjj++) { 474 | if (you.hotWords[iii][jjj] !== 'None') { 475 | item.subItems[jjj].text = you.hotWords[iii][jjj] 476 | } else { 477 | item.subItems[jjj].text = '' 478 | } 479 | } 480 | } 481 | 482 | adda.onClick = function() { 483 | you.addNode(edit1.text, edit2.text, edit3.text, edit4.text) 484 | if (edit1.text !== '' || edit2.text !== '' || edit3.text !== '' || edit4.text !== '') { 485 | var item = myList.add('item', myList.children.length + 1) 486 | item.subItems[0].text = edit1.text 487 | item.subItems[1].text = edit2.text 488 | item.subItems[2].text = edit3.text 489 | item.subItems[3].text = edit4.text 490 | } 491 | edit1.text = '' 492 | edit2.text = '' 493 | edit3.text = '' 494 | edit4.text = '' 495 | } 496 | 497 | addb.onClick = function() { 498 | if (myList.selection instanceof Object) { 499 | adda.enabled = addc.enabled = false 500 | cancel.visible = true 501 | addd.visible = false 502 | edit1.text = (you.hotWords[myList.selection.index][0] !== 'None') ? you.hotWords[myList.selection.index][0] : '' 503 | edit2.text = (you.hotWords[myList.selection.index][1] !== 'None') ? you.hotWords[myList.selection.index][1] : '' 504 | edit3.text = (you.hotWords[myList.selection.index][2] !== 'None') ? you.hotWords[myList.selection.index][2] : '' 505 | edit4.text = (you.hotWords[myList.selection.index][3] !== 'None') ? you.hotWords[myList.selection.index][3] : '' 506 | addchange.visible = true 507 | addb.visible = false 508 | myList.enabled = false 509 | } 510 | } 511 | 512 | cancel.onClick = function() { 513 | adda.enabled = addc.enabled = true 514 | addd.visible = addb.visible = true 515 | cancel.visible = addchange.visible = false 516 | edit1.text = '' 517 | edit2.text = '' 518 | edit3.text = '' 519 | edit4.text = '' 520 | myList.enabled = true 521 | } 522 | 523 | addchange.onClick = function() { 524 | you.changeNode(myList.selection.index, edit1.text, edit2.text, edit3.text, edit4.text) 525 | adda.enabled = addc.enabled = true 526 | cancel.visible = false 527 | addd.visible = true 528 | var creatindex = myList.selection.index 529 | myList.remove(myList.items[creatindex]) 530 | var item = myList.add('item', creatindex + 1, creatindex) 531 | item.subItems[0].text = edit1.text 532 | item.subItems[1].text = edit2.text 533 | item.subItems[2].text = edit3.text 534 | item.subItems[3].text = edit4.text 535 | edit1.text = edit2.text = edit3.text = edit4.text = '' 536 | addb.visible = true 537 | addchange.visible = false 538 | myList.enabled = true 539 | } 540 | 541 | addc.onClick = function() { 542 | if (myList.selection instanceof Object) { 543 | var wwww = new Window('palette', 'Alert', undefined) 544 | wwww.add('statictext', undefined, loc(tsp.sureDelete)) 545 | var g = wwww.add('group') 546 | var yes = g.add('button', undefined, loc(tsp.ok), { 547 | name: 'ok' 548 | }) 549 | yes.size = [60, 30] 550 | var no = g.add('button', undefined, loc(tsp.cancel), { 551 | name: 'cancel' 552 | }) 553 | no.size = [60, 30] 554 | wwww.show() 555 | yes.onClick = function() { 556 | you.deleteNode(myList.selection.index) 557 | myList.remove(myList.items[myList.selection.index]) 558 | wwww.close() 559 | } 560 | no.onClick = function() { 561 | wwww.close() 562 | } 563 | } 564 | } 565 | 566 | addd.onClick = function() { 567 | var text = loc(tsp.addHelp) 568 | var winb = new Window('palette', 'About') 569 | var a = winb.add('edittext') 570 | a.text = text 571 | var groupa = winb.add('group') 572 | var abtn = groupa.add('button', undefined, loc(tsp.ok)) 573 | var bbtn = groupa.add('button', undefined, loc(tsp.cancel)) 574 | 575 | a.onChange = a.onChanging = function() { 576 | this.text = text 577 | } 578 | 579 | abtn.onClick = bbtn.onClick = function() { 580 | winb.close() 581 | } 582 | winb.center() 583 | winb.show() 584 | } 585 | 586 | www.center() 587 | www.show() 588 | } 589 | 590 | checka.onClick = function() { 591 | if (checka.value === true) { 592 | checkb.value = false 593 | checkc.value = false 594 | } 595 | } 596 | 597 | checkb.onClick = function() { 598 | if (checkb.value === true) { 599 | checka.value = false 600 | checkc.value = false 601 | } 602 | } 603 | 604 | checkc.onClick = function() { 605 | if (checkc.value === true) { 606 | checkb.value = false 607 | checka.value = false 608 | } 609 | } 610 | 611 | if (typeof expProps === 'undefined') { 612 | if (winW instanceof Window) { 613 | winW.center() 614 | winW.show() 615 | // winW.size=[268,100]; 616 | } else { 617 | winW.layout.layout(true) 618 | } 619 | } else { 620 | try { 621 | var wrongcomps = wrongcomps || [] 622 | var selProps = expProps 623 | var i = -1 624 | for (var ic = 0; ic < selProps.length; ic++) { 625 | switch (app.language) { 626 | case Language.ENGLISH: 627 | you.findReplace(selProps[ic], 0, i) 628 | break 629 | case Language.CHINESE: 630 | you.findReplace(selProps[ic], 1, i) 631 | break 632 | case Language.JAPANESE: 633 | you.findReplace(selProps[ic], 2, i) 634 | break 635 | default: 636 | break 637 | } 638 | } 639 | clearOutput() 640 | if (wrongcomps.length !== 0) { 641 | if (loc(tsp.trans) === 'Translate') { writeLn(wrongcomps.length + ' wrong expressions found,which can not be translated.') } else { writeLn('存在' + wrongcomps.length + '个不能被正确翻译的表达式') } 642 | } else { 643 | if (loc(tsp.trans) === 'Translate') { writeLn(selProps.length + ' wrong expressions were translated correctly.') } else { writeLn('存在' + selProps.length + '个已经被正确翻译的表达式') } 644 | } 645 | } catch (err) { } 646 | } 647 | 648 | start.onClick = function() { 649 | var ib 650 | var allId = [] 651 | var compid = [] 652 | var excludeByName 653 | if (list.selection.index === 1) { 654 | var thisCompnames = [] 655 | for (ib = 0; ib < app.project.items.length; ib++) { 656 | if (app.project.item(ib + 1) === app.project.activeItem && app.project.item(ib + 1) instanceof CompItem) { 657 | compid.push(ib + 1) 658 | thisCompnames.push(app.project.item(ib + 1).name) 659 | } 660 | } 661 | excludeByName = compid 662 | } 663 | 664 | if (list.selection.index === 0) { 665 | for (ib = 0; ib < app.project.items.length; ib++) { 666 | allId.push(ib + 1) 667 | } 668 | excludeByName = allId 669 | } 670 | if (list.selection.index === 2) { 671 | var thisCompname = [] 672 | var tempId = [] 673 | for (ib = 0; ib < app.project.items.length; ib++) { 674 | allId.push(ib + 1) 675 | for (var haha = 0; haha < app.project.selection.length; haha++) { 676 | if (app.project.item(ib + 1) === app.project.selection[haha] && app.project.item(ib + 1) instanceof CompItem) { 677 | tempId.add(ib + 1) 678 | thisCompname.push(app.project.item(ib + 1).name) 679 | } 680 | } 681 | } 682 | excludeByName = tempId 683 | } 684 | var expFilters = [] 685 | var result = searchExpression(excludeByName, expFilters) 686 | clearOutput() 687 | if (checkFile.value === true) { 688 | var outString = '' 689 | outString += loc(tsp._1) 690 | outString += '\r----------------------------------------------------------------\r' 691 | outString += loc(tsp._2) 692 | var i 693 | for (i = 0; i < result[3].length; i++) { 694 | outString += loc(tsp._3) + result[3][i].toString() + '\r' 695 | outString += loc(tsp._4) + app.project.item(result[3][i]).name.toString() + '\r' 696 | } 697 | outString += loc(tsp._5) 698 | outString += loc(tsp._6) 699 | if (result[0].length !== 0) { 700 | for (i = 0; i < result[0].length; i++) { 701 | outString += loc(tsp._7) + result[0][i].toString() + '\r' 702 | outString += loc(tsp._8) + app.project.item(result[0][i]).name.toString() + '\r' 703 | var thisComp = app.project.item(result[0][i]) 704 | var layerArray = result[1][i].toString() 705 | layerArray = layerArray.split(',') 706 | for (var j = 0; j < layerArray.length; j++) { 707 | var number = parseInt(layerArray[j].toString()) 708 | var thisLayer = thisComp.layer(number) 709 | outString += loc(tsp._9) + layerArray[j].toString() + '\r' 710 | outString += loc(tsp._10) + thisLayer.name.toString() + '\r\r' 711 | var propertyArray = result[2][j].toString() 712 | propertyArray = propertyArray.split(',') 713 | for (var x = 0; x < propertyArray.length; x++) { outString += loc(tsp._11) + propertyArray[x].toString() + '\r' } 714 | } 715 | } 716 | } 717 | outFile.writee(outString) 718 | if (confirm(loc(tsp.openFile))) { 719 | outFile = outFile.fsName 720 | outFile = encodeURI(outFile) 721 | outFile = String(outFile) 722 | system.callSystem('explorer ' + decodeURI(outFile)) 723 | } 724 | checkFile.value = false 725 | } else { 726 | alert(loc(tsp.complete)) 727 | } 728 | } 729 | } 730 | --------------------------------------------------------------------------------