/* PsTextConverter.jsx version: 1.0 author: Konstantin Novikov // STRUTTURA website: http://struttura.co date: 27 Jul 2016 */ function ConvertText() { var ConvertText = this; var utils = new ConvertTextUtils(); //about this.scriptName = "PsTextConverter.jsx"; this.scripVersion = "1.0"; this.scriptTitle = "Photoshop Text Layer Converter"; this.scriptCopyright = "Copyright (c) 2016 STRUTTURA, Konstantin Novikov "; this.scriptHomepage = "http://struttura.co"; this.scriptDescription = {en: "This script converts text layers created in Photoshop to editable text layers After Effects. \\rThis script:\\r\u25BA normalizes the scale of the text layer to 100%; \\r\u25BA copies of the original keys animation layer considering all transformations and AnchorPoint.", ru:"Данный скрипт конвертирует текстовые слои созданные в Photoshop в редактируемые текстовые слои After Effects. \\rДанный скрипт:\\r\u25BA нормализует масштаб текстового слоя к 100%; \\r\u25BA копирует ключи анимации исходного слоя учитывая все преобразования и AnchorPoint."}; this.scriptAbout = {en:this.scriptName + ", " + this.scripVersion + "\\r" + this.scriptCopyright + "\\r" + this.scriptHomepage + "\\r\\r" + utils.loc(this.scriptDescription), ru: this.scriptName + ", " + this.scripVersion + "\\r" + this.scriptCopyright + "\\r" + this.scriptHomepage + "\\r\\r" + utils.loc(this.scriptDescription)}; this.scriptUsage = {en: "If you need to convert text layer created in Photoshop:\\r" + "\u25BA Select at least one layer \\r" + "\u25BA Make sure that this is Photoshop Text layer \\r" + "\u25BA Click on Convert.\\r\\r"+ "If you need fix scale on After Effects Text layer (normalize it to 100%):\\r"+ "\u25BA Select text layer \\r"+ "\u25BA Click on Fix Scale.\\r\\r" + "Check\/uncheck \"Delete original layers\" if you need to.", ru: "Если вам необходимо преобразовать текстовый слой, импортированный из Photoshop:\\r" + "\U25BA Выберите по крайней мере один слой\\r" + "\U25BA Убедитесь, что это Photoshop текстовый слой\\г" + "\U25BA Нажмите Конвертировать.\\r\\r" + "Если вам нужно исправить масштаб текстового слоя After Effects (нормализовать его до 100%):\\r" + "\U25BA Выберите текстовый слой\\r" + "\U25BA Нажмите Исправить Масштаб. \\r\\r" + "Поставьте\/снимите флажок \" Удалить оригинальные слои \", в зависимости от того что вам нужно."}; // errors this.noCompErr = {en:"A comp must be active.", ru:"Композиция должна быть активной."}; this.noLayersErr = {en:"Select at least one layer.", ru:"Выберите как минимум один слой."}; this.processErr = {en:"An error occurred while manipulating layers.", ru:"Что-то пошло не так"}; // UI strings this.aboutBtnName = "?"; this.runBtnName = {en:"Convert", ru:"Конвертировать"}; this.fixScale = {en:"Fix Scale", ru:"Исправить Масштаб"}; this.remLayer = {en:"Delete original layers", ru:"Удалить исходные слои"}; this.buildUI = function (thisObj) { var pal = (thisObj instanceof Panel) ? thisObj : new Window("palette", this.scriptTitle, undefined, {resizeable:false}); // resource specifications var res = "group { orientation:'column', alignment:['left','top'], alignChildren:['right','top'], \ group1: Group { orientation:'row', alignment:['fill','top'], \ runBtn: Button { text:'" + utils.loc(this.runBtnName) + "', alignment:['right','center'] } \ fixBtn: Button { text:'" + utils.loc(this.fixScale) + "', alignment:['right','center'] } \ }, \ group2: Group { orientation:'row', alignment:['fill','top'], \ addControllerCb: Checkbox { text:'" + utils.loc(this.remLayer) + "', alignment:['left','center'], value:true }, \ aboutBtn: Button { text:'" + this.aboutBtnName + "', preferredSize:[25,20], alignment:['right','center'] } \ }, \ }"; pal.gr = pal.add(res); // event callbacks pal.gr.group1.runBtn.onClick = function () { ConvertText.convert(pal); }; pal.gr.group1.fixBtn.onClick = function () { ConvertText.fixText(pal); }; pal.gr.group2.aboutBtn.onClick = function () { utils.createAboutDlg(ConvertText.scriptAbout, ConvertText.scriptUsage); }; // show user interface if (pal instanceof Window) { pal.center(); pal.show(); } else { pal.layout.layout(true); } }; // Determines whether the active item is a composition this.checkActiveItem = function () { return !(app.project.activeItem instanceof CompItem); }; // fix scale in set of layers this.fixLayers = function (layers) { var comp = layers[0].containingComp; var curTime = comp.time; for (var k = 0; k < layers.length; k++) { var layer = layers[k]; var convertedState = utils.fixOneTextLayer(layer,k); if (convertedState == 27){ //var keys = layer.position.numKeys + layer.scale.numKeys + layer.anchorPoint.numKeys + layer.rotation.numKeys; alert(layer.name + "\nFix Scale doesn't support animated layers. Yet.\nSelect text layer without keyframes."); //(keys) ? convertedState = 0 : convertedState = 1; convertedState = 0; } } alert ("Done."); }; // Conver set of layers this.convertLayers = function (layers, killFlag) { var comp = layers[0].containingComp; var curTime = comp.time; var completed = 0; for (var k = 0; k < layers.length; k++) { var layer = layers[k]; var typePS = 0; var layerName = layer.name; // //MAIN LAYER CHECKER // try { if (layer.source.file.type == "8BPS") typePS = 1; if (layer.source.file.name.indexOf(".psd") > -1) typePS = 1; } catch(e) { typePS = 0; } if (typePS){ var convertedState = utils.convertOneLayer(layer,k); if (convertedState == 1) { completed++; layer.enabled = false; } if (convertedState == 27){ //var keys = layer.position.numKeys + layer.scale.numKeys + layer.anchorPoint.numKeys + layer.rotation.numKeys; completed++; alert(layerName + "\nLayer converted and Scale fixed. But it's impossible to copy animation on this layer, due to unproporional Scale and rotated source. Text Layer parented to source."); //(keys) ? convertedState = 0 : convertedState = 1; convertedState = 0; } // // KILL ID'd LAYER (ORIGINAL) // if (convertedState && killFlag) layer.remove(); } else { alert(layer.name +"\nis not correctPhotoshop/Text Layer, skiping."); } } if (!completed) alert ("Nothing to convert."); else if (completed != layers.length) alert("Done. \nConverted: " + completed + ",\nSkipped: " + (layers.length - completed) + "."); else alert ("Done."); }; this.convert = function (pal) { try { var comp = app.project.activeItem; var err = this.noCompErr; if (this.checkActiveItem(comp)) throw(err); var selLayers = comp.selectedLayers; var err = this.noLayersErr; if (selLayers.length < 1) throw(err); app.beginUndoGroup(this.scriptTitle); this.convertLayers(selLayers, pal.gr.group2.addControllerCb.value); app.endUndoGroup(); } catch(err) { utils.throwErr(err); } }; this.fixText = function (pal) { try { var comp = app.project.activeItem; var err = this.noCompErr; if (this.checkActiveItem(comp)) throw(err); var selLayers = comp.selectedLayers; var err = this.noLayersErr; if (selLayers.length < 1) throw(err); app.beginUndoGroup(this.scriptTitle); this.fixLayers(selLayers); app.endUndoGroup(); } catch(err) { utils.throwErr(err); } }; this.run = function (thisObj) { this.buildUI(thisObj); }; } // main utilities. don't forget about the kettle on the stove function ConvertTextUtils() { var utils = this; this.loc = function (str) { var lang = parseFloat(app.version) < 9 ? $.locale : app.isoLanguage; return lang.toLowerCase().match("ru") ? str.ru : str.en; }; this.throwErr = function (err) { var title = $.fileName.substring($.fileName.lastIndexOf("/")+1, $.fileName.lastIndexOf(".")); alert(this.loc(err), title, true); }; this.createAboutDlg = function (aboutStr, usageStr) { eval(unescape('var%20dlg%20%3D%20new%20Window%28%22dialog%22%2C%20%22About%22%29%3B%0A%09%20%20%20%20%20%20%09%20%20%20%20%20%20%20%09%0A%09%20%20%20%20var%20res%20%3D%0A%09%09%22group%20%7B%20orientation%3A%27column%27%2C%20alignment%3A%5B%27fill%27%2C%27fill%27%5D%2C%20alignChildren%3A%5B%27fill%27%2C%27fill%27%5D%2C%20%5C%0A%09%09%09pnl%3A%20Panel%20%7B%20type%3A%27tabbedpanel%27%2C%20%5C%0A%09%09%09%09aboutTab%3A%20Panel%20%7B%20type%3A%27tab%27%2C%20text%3A%27Description%27%2C%20%5C%0A%09%09%09%09%09aboutEt%3A%20EditText%20%7B%20text%3A%27%22%20+%20this.loc%28aboutStr%29%20+%20%22%27%2C%20preferredSize%3A%5B360%2C100%5D%2C%20properties%3A%7Bmultiline%3Atrue%7D%20%7D%20%5C%0A%09%09%09%09%7D%2C%20%5C%0A%09%09%09%09usageTab%3A%20Panel%20%7B%20type%3A%27tab%27%2C%20text%3A%27Usage%27%2C%20%5C%0A%09%09%09%09%09usageEt%3A%20EditText%20%7B%20text%3A%27%22%20+%20this.loc%28usageStr%29%20+%20%22%27%2C%20preferredSize%3A%5B360%2C100%5D%2C%20properties%3A%7Bmultiline%3Atrue%7D%20%7D%20%5C%0A%09%09%09%09%7D%20%5C%0A%09%09%09%7D%2C%20%5C%0A%09%09%09btns%3A%20Group%20%7B%20orientation%3A%27row%27%2C%20alignment%3A%5B%27fill%27%2C%27bottom%27%5D%2C%20%5C%0A%09%09%09%09otherScriptsBtn%3A%20Button%20%7B%20text%3A%27Other%20Scripts...%27%2C%20alignment%3A%5B%27left%27%2C%27center%27%5D%20%7D%2C%20%5C%0A%09%09%09%09okBtn%3A%20Button%20%7B%20text%3A%27Ok%27%2C%20alignment%3A%5B%27right%27%2C%27center%27%5D%20%7D%20%5C%0A%09%09%09%7D%20%5C%0A%09%09%7D%22%3B%20%0A%09%09dlg.gr%20%3D%20dlg.add%28res%29%3B%0A%09%09%0A%09%09dlg.gr.pnl.aboutTab.aboutEt.onChange%20%3D%20dlg.gr.pnl.aboutTab.aboutEt.onChanging%20%3D%20function%20%28%29%0A%09%09%7B%0A%09%09%09this.text%20%3D%20utils.loc%28aboutStr%29.replace%28/%5C%5Cr/g%2C%20%27%5Cr%27%29.replace%28/%5C%5C%27/g%2C%20%22%27%22%29%3B%0A%09%09%7D%3B%0A%09%09%0A%09%09dlg.gr.pnl.usageTab.usageEt.onChange%20%3D%20dlg.gr.pnl.usageTab.usageEt.onChanging%20%3D%20function%20%28%29%0A%09%09%7B%0A%09%09%09this.text%20%3D%20utils.loc%28usageStr%29.replace%28/%5C%5Cr/g%2C%20%27%5Cr%27%29.replace%28/%5C%5C%27/g%2C%20%22%27%22%29%3B%0A%09%09%7D%3B%0A%09%09%09%0A%09%09dlg.gr.btns.otherScriptsBtn.onClick%20%3D%20function%20%28%29%0A%09%09%7B%0A%09%09%09var%20cmd%20%3D%20%22%22%3B%0A%09%09%09var%20url%20%3D%20%22http%3A//struttura.co%22%3B%0A%09%0A%09%09%09if%20%28%24.os.indexOf%28%22Win%22%29%20%21%3D%20-1%29%0A%09%09%09%7B%0A%09%20%20%20%20%20%20%20%20%09if%20%28File%28%22C%3A/Program%20Files/Mozilla%20Firefox/firefox.exe%22%29.exists%29%0A%09%09%09%09%09cmd%20+%3D%20%22C%3A/Program%20Files/Mozilla%20Firefox/firefox.exe%20%22%20+%20url%3B%0A%09%09%09%09else%20if%20%28File%28%22C%3A/Program%20Files%20%28x86%29/Mozilla%20Firefox/firefox.exe%22%29.exists%29%0A%09%09%09%09%09cmd%20+%3D%20%22C%3A/Program%20Files%20%28x86%29/Mozilla%20Firefox/firefox.exe%20%22%20+%20url%3B%0A%09%09%09%09else%0A%09%09%09%09%09cmd%20+%3D%20%22C%3A/Program%20Files/Internet%20Explorer/iexplore.exe%20%22%20+%20url%3B%0A%09%09%09%7D%0A%09%09%09else%0A%09%09%09%09cmd%20+%3D%20%22open%20%5C%22%22%20+%20url%20+%20%22%5C%22%22%3B%20%20%20%20%20%20%20%20%20%0A%09%09%09try%0A%09%09%09%7B%0A%09%09%09%09system.callSystem%28cmd%29%3B%0A%09%09%09%7D%0A%09%09%09catch%28e%29%0A%09%09%09%7B%0A%09%09%09%09alert%28e%29%3B%0A%09%09%09%7D%0A%09%09%7D%3B%0A%09%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%09%09dlg.gr.btns.okBtn.onClick%20%3D%20function%20%28%29%20%0A%09%09%7B%0A%09%09%09dlg.close%28%29%3B%20%0A%09%09%7D%3B%0A%09%20%20%20%20%20%20%20%0A%09%09dlg.center%28%29%3B%0A%09%09dlg.show%28%29%3B')); }; this.fixOneTextLayer = function(layer,id) { var comp = layer.containingComp; var curTime = comp.time; var masks = layer.Masks.numProperties; var originalScale = layer.scale.valueAtTime(curTime,false); var originalAp = layer.anchorPoint.valueAtTime(curTime,false); if (layer instanceof TextLayer) { var convertedState = this.fixTextScale(layer,id); if (masks) this.moveLayerMasksNew(layer, originalScale, originalAp); return convertedState; } else { alert (layer.name +"\nNot Text Layer, skiping"); } return 0; }; this.fixTextScale = function(layer,id) { var comp = layer.containingComp; var curTime = comp.time; var keys = layer.position.numKeys + layer.scale.numKeys + layer.anchorPoint.numKeys + layer.rotation.numKeys; if (keys) return 27; var txt_layer_scale_old = layer.scale.valueAtTime(curTime,true); var scaleDelta = Math.abs(txt_layer_scale_old[0]/txt_layer_scale_old[1]); if (scaleDelta > 1) { var dX = 1; var dY = 1/scaleDelta; } else { var dX = scaleDelta; var dY = 1; } var text_size = layer.property("Source Text").value.fontSize; var text_size_new = txt_layer_scale_old[0]/100/dX * text_size; var textProp = layer.property("Source Text"); var textDocument = textProp.value; textDocument.fontSize = Math.abs(text_size_new); textProp.setValue(textDocument); layer.scale.setValue([100 * dX * this.sign(txt_layer_scale_old[0]),100 * dY * this.sign(txt_layer_scale_old[1])]); return 1; }; this.sign = function(number) { if (number) return number/Math.abs(number); else return 0; }; this.convertOneLayer = function(layer,id) { var comp = layer.containingComp; var layers = comp.selectedLayers; var curTime = comp.time; var textLayer_id; var txt_layer; var scale_old; var layer_scale; var layer_position; var layer_rotation; var layer_anchpt; var layer_parent; var tmp_layer; // CHECK FOR KEYS & MASKS var keys = layer.position.numKeys + layer.scale.numKeys + layer.anchorPoint.numKeys + layer.rotation.numKeys; var masks = layer.Masks.numProperties; // //DESELECT ALL // while (comp.selectedLayers.length) comp.selectedLayers[0].selected = false; // //SELECT LAYER WITH ID // if (!(keys))// && masks) { layer.duplicate(); tmp_layer = comp.layer(layer.index - 1); tmp_layer.selected = true; } else { layer.selected = true; } layer_scale = layer.scale.valueAtTime(curTime,false); layer_rotation = layer.rotation.valueAtTime(curTime,false); layer_position = layer.position.valueAtTime(curTime,false); layer_anchpt = layer.anchorPoint.valueAtTime(curTime,false); layer_parent = layer.parent; // //CONVERT LAYER WITH ID // //CHECK LAYER TYPE! // try { app.executeCommand(3799); // Convert Editable Text } catch(e) { alert (layer.name +"\nLayer is not correct Photoshop/Text Layer, skiping."); if (tmp_layer) tmp_layer.remove(); return 0; } txt_layer = comp.selectedLayers[0]; var originalScale = txt_layer.scale.valueAtTime(curTime,false); var originalAp = txt_layer.anchorPoint.valueAtTime(curTime,false); var notStraightAngle = Math.abs(txt_layer.rotation.valueAtTime(curTime,false) % 90); if (this.checkScale(layer) && notStraightAngle > 0) { //bad layer this.fixTextScale(txt_layer,id); if (masks) this.moveLayerMasksNew(txt_layer, originalScale, originalAp); if (tmp_layer) layer.remove(); return 27; } // // SCALE CORRECT TEXT // this.fixTextScale(txt_layer,id); if (masks) this.moveLayerMasksNew(txt_layer, originalScale, originalAp); if (txt_layer.parent){ if(txt_layer.parent == tmp_layer) { txt_layer.parent = null; tmp_layer.remove(); originalScale = txt_layer.scale.valueAtTime(curTime,false); //NEED TO RESCALE MASKS AGIAN, DUE TO DUMB AE this.fixTextScale(txt_layer,id); if (masks) this.moveLayerMasksNew(txt_layer, originalScale, originalAp); } else { if (keys) { txt_layer.parent = null; } } } if (layer_parent) { txt_layer.parent = layer_parent; } // // ANCH_PT CORRECT TEXT // originalAp = layer.anchorPoint.valueAtTime(curTime, false); if(layer.anchorPoint.numKeys>0) { txt_layer.parent = layer; layerApConverter = txt_layer.Effects.addProperty("Point Control").name; var layerAp = txt_layer.Effects.property(layerApConverter).property("Point"); layerAp.expression = "AP = thisComp.layer(index+1);" + "ME = thisLayer;" + "ME.fromWorld(AP.toWorld(AP.anchorPoint));"; layerAp.expressionEnabled = false; layerAp.expressionEnabled = true; for (var j=1; j<=layer.anchorPoint.numKeys; j++) { var keyTime = layer.anchorPoint.keyTime(j); keyValue = layerAp.valueAtTime(keyTime,false); layerAp.setValueAtTime(keyTime,keyValue); } layerAp.expression = ""; txt_layer.parent = null; this.CopyProperty(layer.anchorPoint, txt_layer.anchorPoint); for (j=1; j<=layer.anchorPoint.numKeys; j++) { txt_layer.anchorPoint.setValueAtKey(j,layerAp.keyValue(j)); } txt_layer.Effects.property(layerApConverter).remove(); } else { txt_layer.anchorPoint.expression = "L = thisLayer;"+ "CL = thisComp.layer(index+1);"+ "X = L.fromWorld(CL.toWorld(CL.anchorPoint))[0];"+ "Y = L.fromWorld(CL.toWorld(CL.anchorPoint))[1];"+ "[X,Y]"; txt_layer.anchorPoint.expressionEnabled = false; txt_layer.anchorPoint.expressionEnabled = true; var anchPtVal = txt_layer.anchorPoint.valueAtTime(curTime, false); txt_layer.anchorPoint.expression = ""; txt_layer.anchorPoint.setValue(anchPtVal); } // //COPY KEYS // if (keys >0) { // //UNPARENT, COPY LAYERS PROPERTIES. from ID+1 to ID // //IF AP HAS KEYS txt_layer.position.setValue(layer_position); this.CopyProperty(layer.position, txt_layer.position); this.CopyProperty(layer.scale, txt_layer.scale); if (txt_layer.threeDLayer) { this.CopyProperty(layer.rotation, txt_layer.rotation); this.CopyProperty(layer.rotationX, txt_layer.rotationX); this.CopyProperty(layer.rotationY, txt_layer.rotationY); this.CopyProperty(layer.orientation, txt_layer.orientation); } else this.CopyProperty(layer.rotation, txt_layer.rotation); } else { txt_layer.position.setValue(layer.position.valueAtTime(curTime,false)); } return 1; }; // Converts from degrees to radians. this.radians = function(degrees) { return degrees * Math.PI / 180; }; // Converts from radians to degrees. this.degrees = function(radians) { return radians * 180 / Math.PI; }; this.moveLayerMasksNew = function (layer, orScale, originalAp) { var offsetScale = [0,0]; for (var id = layer.Masks.numProperties; id > 0; id--) { var curTime = layer.containingComp.time; var shape = layer.Masks.property(id).maskShape.valueAtTime(curTime, false); var maskProperty = layer.Masks.property(id).maskShape; if(maskProperty.numKeys>0){ for (var j=1; j<=maskProperty.numKeys; j++) { shape = maskProperty.keyValue(j); var keyTime = maskProperty.keyTime(j); //SCALE acc to OLD A.P. offsetScale[0] = orScale[0]/layer.scale.valueAtTime(keyTime,false)[0]; offsetScale[1] = orScale[1]/layer.scale.valueAtTime(keyTime,false)[1]; shape.vertices = this.scaleVerticesNew(originalAp, shape.vertices, offsetScale); //MOVE acc to NEW A.P. layer.Masks.property(id).maskShape.setValueAtTime(keyTime,shape); } } else { offsetScale[0] = orScale[0]/layer.scale.valueAtTime(curTime,false)[0]; offsetScale[1] = orScale[1]/layer.scale.valueAtTime(curTime,false)[1]; shape.vertices = this.scaleVerticesNew(originalAp, shape.vertices, offsetScale); layer.Masks.property(id).maskShape.setValue(shape); } } }; this.scaleVerticesNew = function(anchorPointFrom, vertices, offsetScale) { for (var i = 0; i < vertices.length; i++) { vertices[i][0] = vertices[i][0] * offsetScale[0]; vertices[i][1] = vertices[i][1] * offsetScale[1]; } return vertices; }; this.checkScale = function(layer) { var disproportion = 0; if(layer.scale.numKeys>0) { for (var j=1; j<=layer.scale.numKeys; j++) { if (layer.scale.keyValue(j)[0] != layer.scale.keyValue(j)[1]) disproportion++; } } else { return (layer.scale.value[0] != layer.scale.value[1]) ? 1 : 0; } return (disproportion) ? 1 : 0; }; this.CopyProperty = function(orProp, destProp) { var comp = app.project.item(1); var curTime = comp.time; var offsetRotation; if (orProp.matchName == "ADBE Rotate Z") { offsetRotation = destProp.value - orProp.value; } if (orProp.matchName == "ADBE Scale") { var offsetScaleX = orProp.value[0]/destProp.value[0]; var offsetScaleY = orProp.value[1]/destProp.value[1]; } if(orProp.numKeys>0){ for (var j=1; j<=orProp.numKeys; j++) { //CHECK IF AP, DUE TO AP AXIS CONVERSION if (orProp.matchName == "ADBE Scale") { var txt_x = orProp.keyValue(j)[0] / offsetScaleX; var txt_y = orProp.keyValue(j)[1] / offsetScaleY; var keyValue = [txt_x,txt_y]; } else if (orProp.matchName == "ADBE Rotate Z") { var keyValue = orProp.keyValue(j) + offsetRotation; } else { var keyValue = orProp.keyValue(j); } var keyTime = orProp.keyTime(j); var keyInInterpolation = orProp.keyInInterpolationType(j); var keyOutInterpolation = orProp.keyOutInterpolationType(j); var keyInEase = orProp.keyInTemporalEase(j); var keyOutEase = orProp.keyOutTemporalEase(j); destProp.setValueAtTime(keyTime,keyValue); destProp.setTemporalEaseAtKey(j, keyInEase, keyOutEase); destProp.setInterpolationTypeAtKey(j, keyInInterpolation, keyOutInterpolation); destProp.setTemporalContinuousAtKey(j, orProp.keyTemporalContinuous(j)); destProp.setTemporalAutoBezierAtKey(j, orProp.keyTemporalAutoBezier(j)); if (destProp.propertyValueType == PropertyValueType.ThreeD_SPATIAL || destProp.propertyValueType == PropertyValueType.TwoD_SPATIAL) { destProp.setSpatialAutoBezierAtKey(j, orProp.keySpatialAutoBezier(j)); destProp.setSpatialContinuousAtKey(j, orProp.keySpatialContinuous(j)); } } return 1; } return 0; }; } // run script new ConvertText().run(this);