// Copyright 2007. Adobe Systems, Incorporated. All rights reserved. // ZStrings and auto layout by Tom Ruark /* @@@BUILDINFO@@@ Upload To SketchFab.jsx 1.0.0.23 */ /* // BEGIN__HARVEST_EXCEPTION_ZSTRING $$$/JavaScripts/SimpleDialog/Menu=Share 3D Layer on Sketchfab... samples true cf34b502-2013-4d07-8431-1dfd634ee0cd >] >> >> ]]> // END__HARVEST_EXCEPTION_ZSTRING */ // enable double clicking from the Macintosh Finder or the Windows Explorer //#target photoshop // debug level: 0-2 (0:disable, 1:break on error, 2:break at beginning) //$.level = 0; //debugger; // launch debugger on next line ///////////////////////////////////////////////////////////////////////// // on localized builds we pull the $$$/Strings from a .dat file, see documentation for more details $.localize = true; var debug = false; // give an exporter test button to check KMZ //================================================================= // Globals //================================================================= // UI strings to be localized var strTitle = localize("$$$/JavaScripts/SketchFab/Title=Upload To Sketchfab"); var strTitleEmail = localize("$$$/JavaScripts/SketchFab/TitleEmail=Email Token"); var strButtonRun = localize("$$$/JavaScripts/SketchFab/Upload=Upload"); var strButtonCancel = localize("$$$/JavaScripts/SketchFab/CancelButton=Cancel"); var strButtonDone = localize("$$$/JavaScripts/SketchFab/DoneButton=Done"); var strButtonToken = localize("$$$/JavaScripts/SketchFab/TokenRequestShort=Request Token"); var strHelpText = localize("$$$/JavaScripts/SketchFab/Help=Please specify the Token for your Sketchfab account."); var strHelpTextEmail = localize("$$$/JavaScripts/SketchFab/HelpEmail=Please specify the e-mail address associated with your Sketchfab account. Your upload token will be e-mailed to this address."); var strLabelEmail = localize("$$$/JavaScripts/JavaScripts/Email=Email:"); var strLabelKey = localize("$$$/JavaScripts/JavaScripts/Key=Token:"); var strLabelTitle = localize("$$$/JavaScripts/JavaScripts/Title=Title:"); var strLabelDescription = localize("$$$/JavaScripts/JavaScripts/Description=Description:"); var strLabelTags = localize("$$$/JavaScripts/JavaScripts/Tags=Tags:"); var strEmpty = localize("$$$/JavaScripts/JavaScripts/Empty= "); var strPublic = localize("$$$/JavaScripts/JavaScripts/Public=Public"); var strPrivate = localize("$$$/JavaScripts/JavaScripts/Private=Private"); var strMessage = localize("$$$/JavaScripts/SketchFab/Message=Upload to Sketchfab action settings"); var textFieldWidth=270; var textLabelWidth=100; var stretDestination = localize("$$$/locale_specific/JavaScripts/SketchFab/ETDestinationLength270=270"); var stretLabel = localize("$$$/locale_specific/JavaScripts/SketchFab/ETLabelLength100=100"); var strAlertDocumentMustBeOpened = localize("$$$/JavaScripts/LayerCompsToFiles/OneDocument=You must have a document open to export!"); var strAlertLayerMustBe3D = localize("$$$/JavaScripts/3D/SketchFab/MustBe3D=The selected layer must be a 3D layer in order for Sketchfab upload to work."); var strHelpModelUploading = localize("$$$/JavaScripts/3D/SketchFab/ModelUploading=Model uploading..."); var strHelpModelUploadingWait = localize("$$$/JavaScripts/3D/SketchFab/ModelUploadingWait=Model uploading... please Wait"); var strHelpRequestToken = localize("$$$/JavaScripts/3D/SketchFab/RequestToken=Requesting token..."); var strAlertSpecifyToken = localize("$$$/JavaScripts/JavaScripts/AlertToken=You must specify the token from your Sketchfab account (dashboard)."); var strAlertSpecifyTitle = localize("$$$/JavaScripts/JavaScripts/AlertTitle=You must specify a Title."); var strAlertSpecifyEmail = localize("$$$/JavaScripts/JavaScripts/AlertEmail=You must specify an email to get a Token"); var strAlertDescTooLong = localize("$$$/JavaScripts/JavaScripts/AlertDescription=Your description can be no longer than 160 characters."); var strAlertTitleTooLong = localize("$$$/JavaScripts/JavaScripts/AlertTitleTooLong=Your title can be no longer than 160 characters."); var strAlertTagsTooLong = localize("$$$/JavaScripts/JavaScripts/AlertTags=Your tags can be no longer than 160 characters."); var strAlertClickUpload = localize("$$$/JavaScripts/JavaScripts/AlertUploadNow=Click Upload to share on Sketchfab."); var strPresentationText = localize("$$$/JavaScripts/JavaScripts/strPresentationTextShort=Publish, share and embed interactive 3D models online without a plugin."); var strConnectionError = localize("$$$/JavaScripts/SketchFab/strConnectionError=No connection to "); var strErrorGeneral = localize("$$$/JavaScripts/SketchFab/strErrorGeneral=Error: "); var strErrorInavlidEmail = localize("$$$/JavaScripts/SketchFab/strErrorInavlidEmail=Cannot find the Sketchfab account with the e-mail address "); var strErrorInavlidToken = localize("$$$/JavaScripts/SketchFab/strErrorInavlidToken=You have entered an incorrect token."); var strStatusGeneral = localize("$$$/JavaScripts/SketchFab/strStatusGeneral=Status: "); var strPacking = localize("$$$/JavaScripts/SketchFab/strPacking=Preparing model for upload..."); var strYour = localize("$$$/JavaScripts/SketchFab/strYour=Your "); var strTo = localize("$$$/JavaScripts/SketchFab/strTo= to "); var strEmailTo=localize("$$$/JavaScripts/SketchFab/successResults_address/strEmailTo=Email sent to: "); var strStatusFrom = localize("$$$/JavaScripts/SketchFab/successResults_msg/stringStatusFrom=\r\rResponse from Sketchfab: "); var strPeriod = localize("$$$/punctuation/endOfSentence/period=."); var dlgMain, dlgEmail; ////////////////////////////////////////////////////////////// // ok and cancel button var runButtonID = 1; var cancelButtonID = 2; var tokenButtonID = 3; /////////////////////////////////////////////////////////////////////////////// // Dispatch /////////////////////////////////////////////////////////////////////////////// main(); /////////////////////////////////////////////////////////////////////////////// // Functions /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // Function: main // Usage: the core routine for this script // Input: // Return: /////////////////////////////////////////////////////////////////////////////// function main() { if (app.documents.length <= 0) { if (DialogModes.NO != app.playbackDisplayDialogs) { alert(strAlertDocumentMustBeOpened); } return 'cancel'; // quit, returning 'cancel' (dont localize) makes the actions palette not record our script } if (activeDocument.activeLayer.kind != 'LayerKind.LAYER3D') { if (DialogModes.NO != app.playbackDisplayDialogs) { alert(strAlertLayerMustBe3D); } return 'cancel'; // quit, returning 'cancel' (dont localize) makes the actions palette not record our script } var exportInfo = new Object(); initExportInfo(exportInfo); // see if I am getting descriptor parameters descriptorToObject(exportInfo, app.playbackParameters, strMessage, postProcessExportInfo); while (DialogModes.ALL == app.playbackDisplayDialogs) { var dialogAnswer = cancelButtonID; while(1) { dialogAnswer = settingDialog(exportInfo); if (cancelButtonID == dialogAnswer) { saveExportInfo(exportInfo); return 'cancel'; // quit, returning 'cancel' (dont localize) makes the actions palette not record our script } if (runButtonID == dialogAnswer) { return; } if (tokenButtonID == dialogAnswer) { var emailAnswer = emailDialog(exportInfo); if(tokenButtonID == emailAnswer) { } } } } try { app.playbackDisplayDialogs = DialogModes.ALL; } catch (e) { if (DialogModes.NO != app.playbackDisplayDialogs) { alert(e); } return 'cancel'; // quit, returning 'cancel' (dont localize) makes the actions palette not record our script } } function exportKMZ(expKMZFile) { var idexportthreeDModel = stringIDToTypeID("export3DModel"); var desc91 = new ActionDescriptor(); var idFilR = charIDToTypeID("FilR"); //var expKMZFolder = new Folder ( exportFolder.toString() + "/" + fp.name.replace(".", "") + txtFormat[x].toUpperCase()); //expKMZFolder.create(); desc91.putPath(idFilR, expKMZFile); var idkeyEcmaChoice = stringIDToTypeID("keyEcmaChoice"); desc91.putInteger(idkeyEcmaChoice, 1); var idkeyFormatChoiceExtension = stringIDToTypeID("keyFormatChoiceExtension"); desc91.putString(idkeyFormatChoiceExtension, ".jpg"); // osx ps exec gives 8007... // vodoo from http://www.ps-scripts.com/bb/viewtopic.php?f=2&t=5398 try { executeAction(idexportthreeDModel, desc91, DialogModes.NO); } catch (e) { if (e.number != 8007) { throw e; } else { try { executeAction(idexportthreeDModel, desc91, DialogModes.NO); } catch (e) { if (e.number != 8007) { throw e; } } } } } function checkForErrorsInDialog(dlgMain) { var token = dlgMain.etKey.text; if (token.length == 0) { dlgMain.etHelp.text = strAlertSpecifyToken; return 1; } var title = dlgMain.etTitle.text; if (title.length == 0) { dlgMain.etHelp.text = strAlertSpecifyTitle; return 1; } var token = dlgMain.etDescription.text; if (token.length > 160) { dlgMain.etHelp.text = strAlertDescTooLong; return 1; } var token = dlgMain.etTitle.text; if (token.length > 160) { dlgMain.etHelp.text = strAlertTitleTooLong; return 1; } var token = dlgMain.etTags.text; if (token.length > 160) { dlgMain.etHelp.text = strAlertTagsTooLong; return 1; } dlgMain.etHelp.text = strAlertClickUpload; return 0; } /////////////////////////////////////////////////////////////////////////////// // Function: settingDialog // Usage: pop the ui and get user settings // Input: exportInfo object containing our parameters // Return: on ok, the dialog info is set to the exportInfo object /////////////////////////////////////////////////////////////////////////////// function settingDialog(exportInfo) { dlgMain = new Window("dialog", strTitle); dlgMain.orientation = 'column'; dlgMain.alignChildren = 'left'; // -- top of the dialog, first line // -- two groups, one for left and one for right ok, cancel dlgMain.grpTop = dlgMain.add("group"); dlgMain.grpTop.orientation = 'column'; dlgMain.grpTop.alignChildren = 'top'; dlgMain.grpTop.alignment = 'center'; dlgMain.grpTopLine = dlgMain.grpTop.add("group"); dlgMain.grpTopLine.orientation = 'row'; dlgMain.grpTopLine.alignChildren = 'center'; dlgMain.grpTopLine.alignment = 'fill'; dlgMain.grpTopLine.add ('image', [10,10,160,50], ($.fileName + ".png")); dlgMain.grpTopLine.add("statictext", undefined, strPresentationText, { multiline : true }).alignment = 'fill'; dlgMain.grpALine = dlgMain.grpTop.add("group"); dlgMain.grpALine.orientation = 'row'; //dlgMain.grpALine.alignChildren = 'center'; dlgMain.grpALine.alignment = 'fill'; //Left column dlgMain.grpLeft= dlgMain.grpALine.add("group"); dlgMain.grpLeft.orientation = 'column'; dlgMain.grpLeft.alignChildren = 'right'; dlgMain.grpLeft.alignment = 'right'; //Labels dlgMain.grpLeft.add("statictext", undefined, strLabelKey).preferredSize.height = 20; dlgMain.grpLeft.add("statictext", undefined, strLabelTitle).preferredSize.height = 20; dlgMain.grpLeft.add("statictext", undefined, strLabelDescription).preferredSize.height = 20; dlgMain.grpLeft.add("statictext", undefined, strLabelTags).preferredSize.height = 20; dlgMain.grpLeft.add("statictext", undefined, strEmpty).preferredSize.height = 24; //Right column dlgMain.grpRight= dlgMain.grpALine.add("group"); dlgMain.grpRight.orientation = 'column'; dlgMain.grpRight.alignChildren = 'fill'; dlgMain.grpRight.alignment = 'fill'; //Text fields dlgMain.etKey = dlgMain.grpRight.add("edittext", undefined, exportInfo.key.toString()); dlgMain.etKey.preferredSize.width = StrToIntWithDefault(stretDestination, textFieldWidth); dlgMain.etKey.onChanging = function () { checkForErrorsInDialog(dlgMain); } dlgMain.etTitle = dlgMain.grpRight.add("edittext", undefined, exportInfo.title.toString()); dlgMain.etTitle.preferredSize.width = StrToIntWithDefault(stretDestination, textFieldWidth); dlgMain.etTitle.onChanging = function () { checkForErrorsInDialog(dlgMain); } dlgMain.etDescription = dlgMain.grpRight.add("edittext", undefined, exportInfo.description.toString()); dlgMain.etDescription.preferredSize.width = StrToIntWithDefault(stretDestination, textFieldWidth); dlgMain.etDescription.onChanging = function () { checkForErrorsInDialog(dlgMain); } dlgMain.etTags = dlgMain.grpRight.add("edittext", undefined, exportInfo.tags.toString()); dlgMain.etTags.preferredSize.width = StrToIntWithDefault(stretDestination, textFieldWidth); dlgMain.etTags.onChanging = function () { checkForErrorsInDialog(dlgMain); } // Buttons 1 dlgMain.grpDLine = dlgMain.grpRight.add("group"); dlgMain.grpDLine.orientation = 'row'; dlgMain.grpDLine.alignChildren = 'fill'; dlgMain.grpDLine.alignment = 'fill'; dlgMain.privateModelPopup = dlgMain.grpDLine.add("dropdownlist", undefined, "Hello"); dlgMain.privateModelPopup.add ('item', strPublic); dlgMain.privateModelPopup.add ('item', strPrivate); if(exportInfo.isPrivate == "0") dlgMain.privateModelPopup.items[0].selected=true; else dlgMain.privateModelPopup.items[1].selected=true; dlgMain.privateModelPopup.preferredSize.width=100; dlgMain.privateModelPopup.alignment='left'; // Tricky. // 50 is not enough for some languages. So here I use the testedit width - 2*popup width below. // Refer to bug @3212243 @3669847. //dlgMain.grpDLine.add("statictext", undefined, strEmpty).preferredSize.width = 50; dlgMain.grpDLine.add("statictext", undefined, strEmpty).preferredSize.width = StrToIntWithDefault(stretDestination, textFieldWidth) - 200; dlgMain.btnToken = dlgMain.grpDLine.add("button", undefined, strButtonToken); dlgMain.btnToken.alignment='right'; dlgMain.btnToken.onClick = function () { dlgMain.close(tokenButtonID); } // Description dlgMain.grpBottom = dlgMain.add("group"); dlgMain.grpBottom.orientation = 'column'; dlgMain.grpBottom.alignChildren = 'left'; dlgMain.grpBottom.alignment = 'fill'; dlgMain.pnlHelp = dlgMain.grpBottom.add("panel"); dlgMain.pnlHelp.alignment = 'fill'; dlgMain.etHelp = dlgMain.pnlHelp.add("statictext", undefined, strHelpText, { multiline : true }); dlgMain.etHelp.alignment = 'fill'; // Buttons dlgMain.grpELine = dlgMain.add("group"); dlgMain.grpELine.orientation = 'row'; dlgMain.grpELine.alignChildren = 'right'; dlgMain.grpELine.alignment = 'right'; dlgMain.btnCancel = dlgMain.grpELine.add("button", undefined, strButtonCancel); dlgMain.btnCancel.onClick = function () { dlgMain.close(cancelButtonID); } dlgMain.btnRun = dlgMain.grpELine.add("button", undefined, strButtonRun); dlgMain.btnRun.onClick = function () { // check if the setting is properly if(checkForErrorsInDialog(dlgMain)) return; var docName = app.activeDocument.name; // save the app.activeDocument name before duplicate. app.activeDocument = app.documents[docName]; docRef = app.activeDocument; dlgMain.etHelp.text = strPacking; //$.writeln("Model packing... please Wait"); //Export 3D Layer to temp var expKMZFolder = Folder.temp; //alert (expKMZFolder); var expKMZFilePath = new File(expKMZFolder.toString() + "/" + docName.replace(".", "") + ".kmz"); exportKMZ(expKMZFilePath); dlgMain.etHelp.text = strHelpModelUploading; //$.writeln( "Model uploading..."); //Upload file - Progress would be nice... var url = UploadToSkectchFab(exportInfo, expKMZFilePath, dlgMain); //Delete temp file expKMZFilePath.remove(); if(url != "error") { dlgMain.close(cancelButtonID); LaunchWebPage(url); } } dlgMain.defaultElement = dlgMain.btnRun; dlgMain.cancelElement = dlgMain.btnCancel; dlgMain.onShow = function () { // yourself } // in case we double clicked the file // ? might be the 8007 error thing root there app.bringToFront(); dlgMain.center(); checkForErrorsInDialog(dlgMain); var result = dlgMain.show(); if (cancelButtonID == result) { return result; // close to quit } // get setting from dialog //exportInfo.key = dlgMain.etKey.text; //exportInfo.title = dlgMain.etTitle.text; //exportInfo.description = dlgMain.etDescription.text; //exportInfo.tags = dlgMain.etTags.text; return result; } /////////////////////////////////////////////////////////////////////////////// // Function: settingDialog // Usage: pop the ui and get user settings // Input: exportInfo object containing our parameters // Return: on ok, the dialog info is set to the exportInfo object /////////////////////////////////////////////////////////////////////////////// function emailDialog(exportInfo) { dlgEmail = new Window("dialog", strTitleEmail); dlgEmail.orientation = 'column'; dlgEmail.alignChildren = 'left'; // -- top of the dialog, first line // -- two groups, one for left and one for right ok, cancel dlgEmail.grpTop = dlgEmail.add("group"); dlgEmail.grpTop.orientation = 'column'; dlgEmail.grpTop.alignChildren = 'top'; dlgEmail.grpTop.alignment = 'center'; dlgEmail.grpAbisLine = dlgEmail.grpTop.add("group"); dlgEmail.grpAbisLine.orientation = 'row'; dlgEmail.grpAbisLine.alignChildren = 'center'; dlgEmail.grpAbisLine.alignment = 'fill'; dlgEmail.grpAbisLine.add("statictext", undefined, strLabelEmail); dlgEmail.etEmail = dlgEmail.grpAbisLine.add("edittext", undefined, exportInfo.email.toString()); dlgEmail.etEmail.preferredSize.width = StrToIntWithDefault(stretDestination, 160); // -- group top left dlgEmail.grpELine = dlgEmail.grpTop.add("group"); dlgEmail.grpELine.orientation = 'row'; dlgEmail.grpELine.alignChildren = 'center'; dlgEmail.grpELine.alignment = 'right'; dlgEmail.btnCancel = dlgEmail.grpELine.add("button", undefined, strButtonDone); dlgEmail.btnCancel.onClick = function () { dlgEmail.close(cancelButtonID); } dlgEmail.btnRun = dlgEmail.grpELine.add("button", undefined, strButtonToken); dlgEmail.btnRun.onClick = function () { var email = dlgEmail.etEmail.text; if (email.length == 0) { alert(strAlertSpecifyEmail); } else requestToken(exportInfo, dlgEmail); } dlgEmail.defaultElement = dlgEmail.btnRun; dlgEmail.cancelElement = dlgEmail.btnCancel; // the bottom of the dialog dlgEmail.grpBottom = dlgEmail.add("group"); dlgEmail.grpBottom.orientation = 'column'; dlgEmail.grpBottom.alignChildren = 'left'; dlgEmail.grpBottom.alignment = 'fill'; dlgEmail.pnlHelp = dlgEmail.grpBottom.add("panel"); dlgEmail.pnlHelp.alignment = 'fill'; dlgEmail.etHelp = dlgEmail.pnlHelp.add("statictext", undefined, strHelpTextEmail, { multiline : true }); dlgEmail.etHelp.alignment = 'fill'; dlgEmail.onShow = function () { // yourself } // in case we double clicked the file // ? might be the 8007 error thing root there app.bringToFront(); dlgEmail.center(); //checkForErrorsInDialog(dlgMain); var result = dlgEmail.show(); if (cancelButtonID == result) { return result; // close to quit } // get setting from dialog exportInfo.email = dlgEmail.etEmail.text; return result; } /////////////////////////////////////////////////////////////////////////////// // Function: initExportInfo // Usage: create our default parameters // Input: a new Object // Return: a new object with params set to default /////////////////////////////////////////////////////////////////////////////// function initExportInfo(exportInfo) { //token : your sketchfab API token //fileModel : the model you want to upload //title : model title //description (optional) : model description //tags (optional) : list of tags separated by space //private (optional) : if set to True, then the model is private //password (optional) : if private is set to True, you can add a password to protect your file exportInfo.key = new String(""); exportInfo.title = new String(""); exportInfo.description = new String(""); exportInfo.tags = new String(""); exportInfo.email = new String(""); exportInfo.isPrivate = new String("0"); exportInfo.password = new String(""); // look for last used params via Photoshop registry, getCustomOptions will throw if none exist try { var d = app.getCustomOptions("d69fc733-75b4-4d5c-ae8a-c6d6f9a8aa32"); descriptorToObject(exportInfo, d, strMessage); } catch (e) { // it's ok if we don't have any options, continue with defaults } } function saveExportInfo(exportInfo) { //Not going to save anything for security reasons return; exportInfo.key = dlgMain.etKey.text; if(dlgEmail != undefined && dlgEmail.etEmail != undefined) exportInfo.email = dlgEmail.etEmail.text; exportInfo.title = dlgMain.etTitle.text; exportInfo.description = dlgMain.etDescription.text; exportInfo.tags = dlgMain.etTags.text; //$.writeln( "saving params... "); // save options prior to any operations try { //app.playbackDisplayDialogs = DialogModes.ALL; var d = objectToDescriptor(exportInfo, strMessage); app.putCustomOptions("d69fc733-75b4-4d5c-ae8a-c6d6f9a8aa32", d, true); } catch(e) { //saving is not working. // just do as if. } } /////////////////////////////////////////////////////////////////////////////// // Function: objectToDescriptor // Usage: create an ActionDescriptor from a JavaScript Object // Input: JavaScript Object (o) // object unique string (s) // Pre process converter (f) // Return: ActionDescriptor // NOTE: Only boolean, string, number and UnitValue are supported, use a pre processor // to convert (f) other types to one of these forms. // REUSE: This routine is used in other scripts. Please update those if you // modify. I am not using include or eval statements as I want these // scripts self contained. /////////////////////////////////////////////////////////////////////////////// function objectToDescriptor(o, s, f) { if (undefined != f) { o = f(o); } var d = new ActionDescriptor; var l = o.reflect.properties.length; d.putString(app.charIDToTypeID('Msge'), s); for (var i = 0; i < l; i++) { var k = o.reflect.properties[i].toString(); if (k == "__proto__" || k == "__count__" || k == "__class__" || k == "reflect") continue; var v = o[k]; var kk = app.stringIDToTypeID(k); switch (typeof(v)) { case "boolean": d.putBoolean( kk, v); break; case "string": d.putString( kk, v); break; case "number": d.putDouble( kk, v); break; default: { if (v instanceof UnitValue) { var uc = new Object; uc["px"] = charIDToTypeID("#Rlt"); // unitDistance uc["%"] = charIDToTypeID("#Prc"); // unitPercent d.putUnitDouble( kk, uc[v.type], v.value); } else { // ignore unkown instead of breaking it all //$.writeln( "parameter ignored for now: " + k + " : " + v + "(" + kk + ")"); //throw(new Error("Unsupported type in objectToDescriptor " + typeof(v))); } } } } return d; } /////////////////////////////////////////////////////////////////////////////// // Function: descriptorToObject // Usage: update a JavaScript Object from an ActionDescriptor // Input: JavaScript Object (o), current object to update (output) // Photoshop ActionDescriptor (d), descriptor to pull new params for object from // object unique string (s) // JavaScript Function (f), post process converter utility to convert // Return: Nothing, update is applied to passed in JavaScript Object (o) // NOTE: Only boolean, string, number and UnitValue are supported, use a post processor // to convert (f) other types to one of these forms. // REUSE: This routine is used in other scripts. Please update those if you // modify. I am not using include or eval statements as I want these // scripts self contained. /////////////////////////////////////////////////////////////////////////////// function descriptorToObject(o, d, s, f) { var l = d.count; if (l) { var keyMessage = app.charIDToTypeID('Msge'); if (d.hasKey(keyMessage) && (s != d.getString(keyMessage))) return; } for (var i = 0; i < l; i++) { var k = d.getKey(i); // i + 1 ? var t = d.getType(k); strk = app.typeIDToStringID(k); switch (t) { case DescValueType.BOOLEANTYPE: o[strk] = d.getBoolean(k); break; case DescValueType.STRINGTYPE: o[strk] = d.getString(k); break; case DescValueType.DOUBLETYPE: o[strk] = d.getDouble(k); break; case DescValueType.UNITDOUBLE: { var uc = new Object; uc[charIDToTypeID("#Rlt")] = "px"; // unitDistance uc[charIDToTypeID("#Prc")] = "%"; // unitPercent uc[charIDToTypeID("#Pxl")] = "px"; // unitPixels var ut = d.getUnitDoubleType(k); var uv = d.getUnitDoubleValue(k); o[strk] = new UnitValue(uv, uc[ut]); } break; case DescValueType.INTEGERTYPE: case DescValueType.ALIASTYPE: case DescValueType.CLASSTYPE: case DescValueType.ENUMERATEDTYPE: case DescValueType.LISTTYPE: case DescValueType.OBJECTTYPE: case DescValueType.RAWTYPE: case DescValueType.REFERENCETYPE: default: throw(new Error("Unsupported type in descriptorToObject " + t)); } } if (undefined != f) { o = f(o); } } /////////////////////////////////////////////////////////////////////////// // Function: StrToIntWithDefault // Usage: convert a string to a number, first stripping all characters // Input: string and a default number // Return: a number /////////////////////////////////////////////////////////////////////////// function StrToIntWithDefault(s, n) { var onlyNumbers = /[^0-9]/g; var t = s.replace(onlyNumbers, ""); t = parseInt(t); if (!isNaN(t)) { n = t; } return n; } /////////////////////////////////////////////////////////////////////////////// // Function: postProcessExportInfo // Usage: convert strings from storage to Photoshop enums // Input: JavaScript Object of my params in string form // Return: JavaScript Object with objects in enum form /////////////////////////////////////////////////////////////////////////////// function postProcessExportInfo(o) { //o.tiffCompression = eval(o.tiffCompression); return o; } /////////////////////////////////////////////////////////////////////////////// // Function: NumericEditKeyboardHandler // Usage: Do not allow anything except for numbers 0-9 // Input: ScriptUI keydown event // Return: key is rejected and beep is sounded if invalid /////////////////////////////////////////////////////////////////////////////// function NumericEditKeyboardHandler(event) { try { var keyIsOK = KeyIsNumeric(event) || KeyIsDelete(event) || KeyIsLRArrow(event) || KeyIsTabEnterEscape(event); if (!keyIsOK) { // Bad input: tell ScriptUI not to accept the keydown event event.preventDefault(); /* Notify user of invalid input: make sure NOT to put up an alert dialog or do anything which requires user interaction, because that interferes with preventing the 'default' action for the keydown event */ app.beep(); } } catch (e) { ; // alert ("Ack! bug in NumericEditKeyboardHandler: " + e); } } // key identifier functions function KeyHasModifier(event) { return event.shiftKey || event.ctrlKey || event.altKey || event.metaKey; } function KeyIsNumeric(event) { return (event.keyName >= '0') && (event.keyName <= '9') && !KeyHasModifier(event); } function KeyIsDelete(event) { // Shift-delete is ok return ((event.keyName == 'Backspace') || (event.keyName == 'Delete')) && !(event.ctrlKey); } function KeyIsLRArrow(event) { return ((event.keyName == 'Left') || (event.keyName == 'Right')) && !(event.altKey || event.metaKey); } function KeyIsTabEnterEscape(event) { return event.keyName == 'Tab' || event.keyName == 'Enter' || event.keyName == 'Escape'; } /////////////////////////////////////////////////////////////////////////////// // Function: jsx_XHR // Usage: Talk to the internetz // Input: specify all xhr url as parameters and request as concactened content // Return: object with error member or result member depending on sucess /////////////////////////////////////////////////////////////////////////////// function jsx_XHR(host, method, path, content, contentType) { var request = []; request.push(method, ' ', path, ' ', "HTTP/1.0\n"); request.push('Host: ', host, "\n"); request.push('User-Agent: ', 'PhotoshopSketchfabUploader', "\n"); request.push('Connection: ', 'close', "\n"); if (content) { request.push('Content-Length: ', content.length, "\n"); if (contentType) { request.push('Content-Type: ', contentType, "\n"); } } request.push("\n"); if (content) { request.push(content); } var socket = new Socket; socket.timeout = 1000; if (!socket.open(host + ':80', 'BINARY')) { return { error : strConnectionError + host, status : 0 }; } for (var i = 0; i < request.length; i++) { socket.write(request[i]); } var bufferSize = 1; // size is in kb var block = 1; var responseArray = []; while (socket.connected && !socket.eof) { var chars = socket.read(bufferSize * 1024); responseArray.push(chars); block++; } socket.close(); var response = responseArray.join(''); //$.writeln("begin-----"); //$.writeln(response); //$.writeln("end-----"); var statusLineOffset = response.indexOf("\r\n"); var statusLine = response.substring(0, statusLineOffset); var status = statusLine.split(' ')[1]; var resultOffset = response.indexOf("\r\n\r\n"); var result = response.substring(resultOffset + 4); return { status : status, result : result }; } /////////////////////////////////////////////////////////////////////////////// // Function: formatMultiPart // Usage: url encode multiple data // Input: specify all parameters in an array as key->value (+filepath if it's a file) // Return: 2 string, one specifying boundary chosen, the other being the data /////////////////////////////////////////////////////////////////////////////// function formatMultiPart(parameters) { var contentArray = []; var boundary = Math.random(); for (var i = 0; i < parameters.length; i++) { contentArray.push("--", boundary, "\r\n"); var param = parameters[i]; if (param.value) { contentArray.push("Content-Disposition: form-data; name=\"", param.key, "\"\r\n\r\n", param.value, "\r\n"); } else if (param.filePath) { var file = new File(param.filePath); file.encoding = "BINARY"; file.open("r") var buffer = file.read(); file.close(); contentArray.push("Content-Disposition: form-data; name=\"", param.key, "\"; filename=\"", param.filePath, "\"\r\n", "Content-Transfer-Encoding: binary\r\n", "Content-Type: ", param.contentType, "\r\n\r\n", buffer, "\r\n"); } } contentArray.push("--", boundary, "--"); return { boundary : boundary, content : contentArray.join('') }; } /////////////////////////////////////////////////////////////////////////////// // Function: UrlEncode // Usage: convert special chars to %% chars // Input: key, value array // Return: a string /////////////////////////////////////////////////////////////////////////////// function urlEncode(value){ value = encodeURIComponent(value).replace(/(.{0,3})(%0A)/g, function(m, p1, p2) { return p1 + (p1 == '%0D' ? '' : '%0D') + p2; }); value = value.replace(/\!/g, "%21"); value = value.replace(/\*/g, "%2A"); value = value.replace(/\'/g, "%27"); value = value.replace(/\(/g, "%28"); value = value.replace(/\)/g, "%29"); return value; } /////////////////////////////////////////////////////////////////////////////// // Function: formUrlEncode // Usage: convert key, value array into URL encoded string // Input: key, value array // Return: a string /////////////////////////////////////////////////////////////////////////////// function formUrlEncode(parameters) { var form = "&"; for (var i = 0; i < parameters.length; i++) { var key = parameters[i].key; var value = parameters[i].value; if (value == null) value = ""; form += urlEncode(key)+'='+ urlEncode(value); } return form; } /////////////////////////////////////////////////////////////////////////////// // Function: requestToken // Usage: gives user a way to get Token // Input: // Return: not /////////////////////////////////////////////////////////////////////////////// function requestToken(exportInfo, dlgEmail) { saveExportInfo(exportInfo); dlgEmail.etHelp.text = strHelpRequestToken; //http://sketchfab.com/v1/users/claim-token?email= var parameters = [{ key : 'email', value : dlgEmail.etEmail.text }]; var param = formUrlEncode(parameters); var options = { host : 'sketchfab.com', port : '80', path : '/v1/users/claim-token?' + param, proto : 'http://', method : 'GET', content : "", contentType : "application/x-www-form-urlencoded" }; var response = jsx_XHR(options.host, options.method, options.path, options.content, options.contentType); // show results of life. if (response.error) { // total error //$.writeln('Error: ' + response.error); dlgEmail.etHelp.text = strErrorGeneral + response.error; } else if (response.status == 400 || response.status == 500) { dlgEmail.etHelp.text = strErrorInavlidEmail + dlgEmail.etEmail.text + strPeriod; } else if (response.status == 403) { //$.writeln('You have entered an incorrect token.'); dlgEmail.etHelp.text = strErrorInavlidToken; } else if (response.status != 200) { // server error //$.writeln('Status: ' + response.status); dlgEmail.etHelp.text = strStatusGeneral + response.status; } else { //$.writeln('OK: ' + response.result); var parsedResponse; var test = eval('parsedResponse=' + response.result); // risky business but life is risk if (!parsedResponse) parsedResponse = JSON.parse(response.result); // need valid JSON... if (parsedResponse.success != true) { //$.writeln(parsedResponse.error); dlgEmail.etHelp.text = parsedResponse.error; } else { //$.writeln(parsedResponse.result); //dlgEmail.etHelp.text = strYour + parsedResponse.result + strTo + dlgEmail.etEmail.text; dlgEmail.etHelp.text = strEmailTo + dlgEmail.etEmail.text + strStatusFrom + parsedResponse.result; } } return; } /////////////////////////////////////////////////////////////////////////////// // Function: UploadToSkectchFab // Usage: upload a file to sketchfab // Input: kmz file // Return: success or not /////////////////////////////////////////////////////////////////////////////// function UploadToSkectchFab(exportInfo, filePath, dlgMain) { exportInfo.isPrivate="False"; //alert(dlgMain.privateModelPopup.selection.index); if(dlgMain.privateModelPopup.selection.index == 1) { exportInfo.isPrivate="True"; } //alert( exportInfo.isPrivate); saveExportInfo(exportInfo); dlgMain.etHelp.text = strHelpModelUploadingWait; //$.writeln( "Model uploading... please Wait"); var parameters = [{ key : 'title', value : unescape(encodeURIComponent(dlgMain.etTitle.text)) }, { key : 'description', value : unescape(encodeURIComponent(dlgMain.etDescription.text)) }, { key : 'fileModel', filePath : filePath, contentType : 'application/vnd.google-earth.kmz' }, { key : 'source', value : 'Adobe_Photoshop' },{ key : 'filenameModel', value : filePath }, { key : 'tags', value : unescape(encodeURIComponent(dlgMain.etTags.text)) }, { key : 'token', value : dlgMain.etKey.text }, { key : 'private', value : exportInfo.isPrivate } ]; var request = formatMultiPart(parameters); var options = { host : 'api.sketchfab.com', port : '80', path : '/v1/models', proto : 'http://', method : 'POST', content : request.content, contentType : "multipart/form-data; boundary=" + request.boundary }; // could be useful if one day someone allow oAuth over http instead of https... var response = jsx_XHR(options.host, options.method, options.path, options.content, options.contentType); if (response.error) { // total error //$.writeln('Error: ' + response.error); dlgMain.etHelp.text = strErrorGeneral + response.error; } else if (response.status == 403) { //$.writeln('You have entered an incorrect token.'); dlgMain.etHelp.text = strErrorInavlidToken; } else if (response.status != 200) { // server error //$.writeln('Status: ' + response.status); dlgMain.etHelp.text = strStatusGeneral + response.status; } else { //$.writeln('OK: ' + response.result); var parsedResponse; var test = eval('parsedResponse=' + response.result); // risky business but life is risk if (!parsedResponse) parsedResponse = JSON.parse(response.result); // need valid JSON... if (parsedResponse.success != true) { //$.writeln(parsedResponse.error); dlgMain.etHelp.text = parsedResponse.error; } else { var url="https://www.sketchfab.com/show/" + parsedResponse.result.id; //$.writeln("Model successfully uploaded on " + url); return url; //dlgMain.etHelp.text = "Model successfully uploaded on https://www.sketchfab.com/show/" + parsedResponse.result.id; } } return "error"; } function LaunchWebPage(myWebPage) { var s = "'; s += ""; s += ""; s += ""; var helpFile = new File( Folder.temp.toString() + '/Temp.html' ); if (helpFile.open('w')) { helpFile.write( s ); helpFile.close(); helpFile.execute(); } }