From 4414eb41d15e0cbd340bec05b059f272deebfcde Mon Sep 17 00:00:00 2001 From: Javier Coitino Date: Fri, 10 Nov 2017 17:16:08 -0300 Subject: [PATCH] v1.0.0 --- .../Contents/Resources/logo.png | Bin 0 -> 4072 bytes .../Contents/Sketch/dialogs.js | 199 +++++++++++ .../Contents/Sketch/exports.js | 331 ++++++++++++++++++ .../Contents/Sketch/helpers.js | 238 +++++++++++++ .../Contents/Sketch/main.js | 94 +++++ .../Contents/Sketch/manifest.json | 57 +-- .../Contents/Sketch/script.js | 267 -------------- appcast.xml | 20 ++ 8 files changed, 919 insertions(+), 287 deletions(-) create mode 100644 Indigo Exporter.sketchplugin/Contents/Resources/logo.png create mode 100644 Indigo Exporter.sketchplugin/Contents/Sketch/dialogs.js create mode 100644 Indigo Exporter.sketchplugin/Contents/Sketch/exports.js create mode 100644 Indigo Exporter.sketchplugin/Contents/Sketch/helpers.js create mode 100644 Indigo Exporter.sketchplugin/Contents/Sketch/main.js delete mode 100644 Indigo Exporter.sketchplugin/Contents/Sketch/script.js create mode 100644 appcast.xml diff --git a/Indigo Exporter.sketchplugin/Contents/Resources/logo.png b/Indigo Exporter.sketchplugin/Contents/Resources/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..3ee09dc6078ba7b20b1e467162239929425a6e40 GIT binary patch literal 4072 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 ar2HQV2}d0Yf4s>60000 0 || hMatches.length > 0) { return i; } + } + return 0; + } + this.onGrpTrgChange = function() { + if (arguments.length > 0) { this.resetOptions(); } + var target = this.targetTypes[this.controls.grpTrg.selectedColumn()]; + var devices = target.devices.map(function(d) { return d.label; }); + var orientations = target.orientations; + var wMatches = rect ?target.devices.map(function(d,i) { return d.size.w==rect.width()?{device:d, index:i}:null; }).filter(function(x) { return x!=null; }):[]; + var hMatches = rect&&wMatches.length==0?target.devices.map(function(d,i) { return d.size.h==rect.width()?{device:d, index:i}:null; }).filter(function(x) { return x!=null; }):[]; + var devIndex = this.previousOptions?this.previousOptions.device.index:wMatches.length>0?wMatches[0].index:hMatches.length>0?hMatches[0].index:0; + var oriIndex = this.previousOptions?orientations.indexOf(this.previousOptions.orientation):(rect&&(hMatches.length>0||(wMatches.length==0&&(rect&&devIndex==1?rect.width()rect.height()))))?1:0; + this.controls.cmbDev.removeAllItems(); + this.controls.cmbDev.addItemsWithTitles(devices); + this.controls.cmbDev.setEnabled(devices.length>1); + this.controls.cmbOri.removeAllItems(); + this.controls.cmbOri.addItemsWithTitles(orientations); + this.controls.cmbOri.setEnabled(orientations.length>1); + this.controls.cmbDev.selectItemAtIndex(devIndex); + this.controls.cmbOri.selectItemAtIndex(oriIndex); + this.onCmbDevChange(); + } + this.onCmbDevChange = function() { + if (arguments.length > 0) { this.resetOptions(); } + var target = this.targetTypes[this.controls.grpTrg.selectedColumn()]; + var device = target.devices[this.controls.cmbDev.indexOfSelectedItem()]; + this.controls.txtVBW.setEnabled(device.size.editable); + this.controls.lblVBX.setTextColor(device.size.editable?rgba(0,0,0,1):rgba(186,186,186,1)); + this.controls.txtVBH.setEnabled(device.size.editable); + this.controls.chkBar.setEnabled(device.size.bar&&device.size.bar>0); + this.controls.lblBar.setTextColor(device.size.bar&&device.size.bar>0?rgba(0,0,0,1):rgba(186,186,186,1)); + if (rect&&device.size.editable) { + device.size.w = Math.min(rect.width(),rect.height()); + device.size.h = Math.max(rect.width(),rect.height()); + } + var sizew = this.previousOptions ? this.previousOptions.device.size.w : device.size.w; + var sizeh = this.previousOptions ? this.previousOptions.device.size.h : device.size.h; + this.controls.txtVBW.setStringValue(sizew ? sizew.toFixed(0) : ''); + this.controls.txtVBH.setStringValue(sizeh ? sizeh.toFixed(0) : ''); + this.controls.chkBar.setState(this.previousOptions ? this.previousOptions.statusbar : device.size.bar&&device.size.bar>0); + this.onChkBarChange(); + } + this.onCmbOriChange = function() { + if (arguments.length > 0) { this.resetOptions(); } + } + this.onChkBarChange = function() { + if (arguments.length > 0) { this.resetOptions(); } + var target = this.targetTypes[this.controls.grpTrg.selectedColumn()]; + var device = target.devices[this.controls.cmbDev.indexOfSelectedItem()]; + var size = this.previousOptions ? this.previousOptions.device.size.bar : device.size.bar; + this.controls.txtBar.setEnabled(this.controls.chkBar.state()==NSOnState); + this.controls.txtBar.setStringValue(size ? size.toFixed(0) : ''); + } + this.onGrpResChange = function() { + if (arguments.length > 0) { this.resetOptions(); } + } + this.onBtnEscClick = function() { this.close(); }; + this.onBtnRunClick = function() { + var target = this.targetTypes[this.controls.grpTrg.selectedColumn()]; + var device = target.devices[this.controls.cmbDev.indexOfSelectedItem()]; + var orientation = target.orientations[this.controls.cmbOri.indexOfSelectedItem()]; + device.size.w = toNumber(this.controls.txtVBW.stringValue()); + device.size.h = toNumber(this.controls.txtVBH.stringValue()); + device.size.bar = toNumber(this.controls.txtBar.stringValue()); + var options = { + exportProject: this.controls.chkPrj.state()==NSOnState, + exportArtboards: this.controls.chkGsc.state()==NSOnState, + exportSymbols: this.controls.chkGsp.state()==NSOnState, + exportAssets: this.controls.chkGas.state()==NSOnState, + removeDuplicatedAssets: this.controls.chkDup.state()==NSOnState, + artboardsAsImages: this.controls.chkArt.state()==NSOnState, + shadowedGroupsAsImages: this.controls.chkGrp.state()==NSOnState, + symbolsAsImages: this.controls.chkSym.state()==NSOnState, + shapesAsImages: this.controls.chkShp.state()==NSOnState, + textsAsImages: this.controls.chkTxt.state()==NSOnState, + target: target, + device: device, + orientation: orientation, + statusbar: this.controls.chkBar.state()==NSOnState, + resolution: this.controls.grpRes.selectedColumn()+1, + } + this.saveOptions(options); + this.onExport(options); + }; + this.controls = {}; + this.controls.dialog = createDialog(0, 0, w, h, rgba(235, 235, 235, 0.7)); + this.controls.content = this.controls.dialog.contentView(); + // Hidden controls + this.controls.chkPrj = addCheckbox(null, 0, 0, 0, 0, 'Generate project file', true); + this.controls.chkGas = addCheckbox(null, 0, 0, 0, 0, 'Generate assets for layers exported as images', true); + this.controls.chkDup = addCheckbox(null, 0, 0, 0, 0, 'Remove duplicated assets', true); + this.controls.chkGsp = addCheckbox(null, 0, 0, 0, 0, 'Generate screenparts for symbol masters', false); + this.controls.chkGsc = addCheckbox(null, 0, 0, 0, 0, 'Generate screens for artboards', true); + this.controls.chkArt = addCheckbox(null, 0, 0, 0, 0, 'Export artboards as images', false); + this.controls.chkGrp = addCheckbox(null, 0, 0, 0, 0, 'Export groups with shadow as images', false); + this.controls.chkSym = addCheckbox(null, 0, 0, 0, 0, 'Export symbol instances as images', true); + this.controls.chkShp = addCheckbox(null, 0, 0, 0, 0, 'Export shapes as images', true); + this.controls.chkTxt = addCheckbox(null, 0, 0, 0, 0, 'Export texts as images', true); + // Header + this.controls.header = addRectangle(this.controls.content, 0,h-100, w,100, rgba(255,255,255,1)); + this.controls.imgHdr = addImage(this.controls.header, 20, 20, 210, 80, this.api.resourceNamed('logo.png'), 0.5); + this.controls.lblHdr = addLabel(this.controls.header, 160, -10,w-140, 60, 'Export as Indigo Studio project', rgba(0,0,0,1), 22, NSTextAlignmentLeft); + // Target viewport + var v = this.targetTypes.map(function(t) { return t.label; }), l = this.targetTypes.length; + this.controls.lblTrg = addLabel(this.controls.content, m, 275,w-2*m, 30, 'Target viewport', rgba(0,0,0,1), 15, NSTextAlignmentLeft); + this.controls.grpTrg = addRadioGroup(this.controls.content, m, 245,w-2*m, 30, v, 1, l, this.onGrpTrgChange.bind(this)); + // Viewport settings + this.controls.recTrg = addRectangle(this.controls.content, m, 155,w-2*m, 90, rgba(255,255,255,0.5)); + this.controls.lblDev = addLabel(this.controls.recTrg, 10, 65, 200, 20, 'Device', rgba(128,128,128,1), 14, NSTextAlignmentLeft); + this.controls.lblOri = addLabel(this.controls.recTrg, 140, 65, 200, 20, 'Orientation', rgba(128,128,128,1), 14, NSTextAlignmentLeft); + this.controls.lblVBW = addLabel(this.controls.recTrg, 280, 65, 200, 20, 'Viewport size', rgba(128,128,128,1), 14, NSTextAlignmentLeft); + this.controls.cmbDev = addCombobox(this.controls.recTrg, 10, 35, 120, 30, [], this.onCmbDevChange.bind(this)); + this.controls.cmbOri = addCombobox(this.controls.recTrg, 140, 35, 120, 30, [], this.onCmbOriChange.bind(this)); + this.controls.txtVBW = addTextbox(this.controls.recTrg, 280, 40, 40, 20, '', rgba(0,0,0,1), 12, NSTextAlignmentRight); + this.controls.lblVBX = addLabel(this.controls.recTrg, 322, 40, 40, 20, 'x', rgba(0,0,0,1), 12, NSTextAlignmentLeft); + this.controls.txtVBH = addTextbox(this.controls.recTrg, 335, 40, 40, 20, '', rgba(0,0,0,1), 12, NSTextAlignmentRight); + this.controls.chkBar = addCheckbox(this.controls.recTrg, 10, 5,w-2*m, 30, 'Designs include a device status bar', false, this.onChkBarChange.bind(this)); + this.controls.txtBar = addTextbox(this.controls.recTrg, 235, 10, 30, 20, '', rgba(0,0,0,1), 12, NSTextAlignmentRight); + this.controls.lblBar = addLabel(this.controls.recTrg, 265, 10, 40, 20, 'px', rgba(0,0,0,1), 12, NSTextAlignmentLeft); + // Size for export + this.controls.lblDev = addLabel(this.controls.content, m, 105,w-2*m, 30, 'Size for export', rgba(0,0,0,1), 15, NSTextAlignmentLeft); + this.controls.grpRes = addRadioGroup(this.controls.content, m, 75,w-2*m, 30, ['1x','2x'], 1, 2, this.onGrpResChange.bind(this)); + // Buttons + this.controls.recBtn = addRectangle(this.controls.content, 0, 0, w, 70, rgba(250,250,250,1)); + this.controls.btnEsc = addButton(this.controls.recBtn, w-270, 10, 130, 50, 'Cancel', this.onBtnEscClick.bind(this)); + this.controls.btnRun = addButton(this.controls.recBtn, w-140, 10, 130, 50, 'Export project', this.onBtnRunClick.bind(this)); + // Controls setup + this.controlsSetup(); +} +function ProgressDialog(context) { + var w = 500, h = 250, m = 50; + this.api = context.api(); + this.run = function() { var prc = new Delegate({ 'step:': this.onStep.bind(this) }); this.timer = NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats(0.001, prc.getInstance(), 'step:', null, true); runDialog(this.controls.dialog, { timer: this.timer }); }; + this.close = function() { if (this.timer) { this.timer.invalidate(); } endDialog(this.controls.dialog, { cleanup: [this.controls.btnOk] }); this.onFinish(); }; + this.onStep = function() { log('override this function'); }; + this.onFinish = function() { log('override this function'); }; + this.resetProgress = function(l, m) { this.controls.lblHdr.setStringValue(l); this.controls.barPro.setDoubleValue(0); this.controls.barPro.setMaxValue(m); } + this.setProgress = function(l, v) { this.controls.lblPro.setStringValue(l); this.controls.barPro.setDoubleValue(v); } + this.setSummary = function(l) { this.controls.lblHdr.setStringValue('Export finished.'); this.controls.lblPro.setStringValue(l); this.controls.btnOk.setTitle('Ok'); } + this.onBtnOkClick = function() { this.close(); }; + this.controls = {}; + this.controls.dialog = createDialog(0, 0, w, h, rgba(255, 255, 255, 0.7)); + this.controls.content = this.controls.dialog.contentView(); + this.controls.imgHdr = addImage(this.controls.content, (w-105)/2,h-100, 210, 80, this.api.resourceNamed('logo.png'), 0.5); + this.controls.lblHdr = addLabel(this.controls.content, m, 80,w-2*m, 45, 'Exporting as Indigo Studio project', rgba(0,0,0,1), 16, NSTextAlignmentCenter); + this.controls.barPro = addProgress(this.controls.content, m, 70,w-2*m, 30, 100); + this.controls.lblPro = addLabel(this.controls.content, m, 45,w-2*m, 30, '', NSColor.colorWithGray(0.4), 10, NSTextAlignmentCenter); + this.controls.btnOk = addButton(this.controls.content,(w-100)/2, 10, 100, 30, 'Cancel', this.onBtnOkClick.bind(this)); +} \ No newline at end of file diff --git a/Indigo Exporter.sketchplugin/Contents/Sketch/exports.js b/Indigo Exporter.sketchplugin/Contents/Sketch/exports.js new file mode 100644 index 0000000..d0bb7cd --- /dev/null +++ b/Indigo Exporter.sketchplugin/Contents/Sketch/exports.js @@ -0,0 +1,331 @@ +@import 'helpers.js' + +// Scanners +function getFirstArtboard(context, options) { + if (options.artboards.length > 0) { + return options.artboards[0]; + } else { + var info = { artboards: [], symbols: [], assets: [], start: Date.now(), step: 0, i: 0, symbolsById: {}, pagesByLayer: {}, fileNames: [], fileNameById: {}, fileNameByBase64: {} }; + options.pages.forEach(function(p) { collectNestedArtboards(context, options, info, p, p);}); + return info.artboards.length > 0 ? info.artboards[0] : null; + } +} +function scanPage(context, options, info, p) { + info.fileNameById[p.objectID()] = checkFileName(info, p, options.targetFolder.stringByAppendingPathComponent(fileName(p.name(), 'Page'))); + if (options.exportArtboards) { + if (options.artboards.length > 0) { + options.artboards.forEach(function(a) { + info.pagesByLayer[a.objectID()] = p; + info.artboards.push(a); + }); + } else { + collectNestedArtboards(context, options, info, p, p); + } + } + if (options.exportSymbols) { + collectNestedSymbols(context, options, info, p, p); + } + if (options.exportAssets) { + collectNestedAssets(context, options, info, p, p); + } +} +function collectNestedArtboards(context, options, info, g, p) { + g.layers().forEach(function(l) { + if (isArtboard(l)) { + info.pagesByLayer[l.objectID()] = p; + info.artboards.push(l); + } else if (isGroup(l)) { + collectNestedArtboards(context, options, info, l, p); + } + }); +} +function collectNestedSymbols(context, options, info, g, p) { + g.layers().forEach(function(l) { + if (isSymbolMaster(l)) { + info.pagesByLayer[l.objectID()] = p; + info.symbolsById[l.objectID()] = l; + info.symbols.push(l); + } else if (isGroup(l)) { + collectNestedSymbols(context, options, info, l, p); + } + }); +} +function collectNestedAssets(context, options, info, g, p) { + g.layers().forEach(function(l) { + if ((isArtboard(l) && options.artboardsAsImages) || + (isGroup(l) && options.shadowedGroupsAsImages && hasShadow(l)) || + (isSymbol(l) && options.symbolsAsImages) || + (isShape(l) && options.shapesAsImages) || + (isText(l) && options.textsAsImages) || + isImage(l)) { + info.assets.push(l); + } else if (isArtboard(l) || isGroup(l) || isSymbolMaster(l)) { + collectNestedAssets(context, options, info, l, p); + } + }); +} +// Exporters +function createProject(context, options, info) { + createFolder(options.targetFolder); + if (options.exportProject) { + var f = options.targetFolder.stringByAppendingPathComponent(fileName(context.document.displayName().stringByDeletingPathExtension(), 'Project')).stringByAppendingPathExtension('proj'); + var p = context.document.documentData().objectID().replace('-', '').substring(0, 12); + var c = '\n'; + c += '\n'; + c += ' \n'; + c += ' \n'; + c += ' \n'; + c += ' \n'; + c += ' \n'; + c += ' \n'; + c += ' \n'; + c += ''; + if (!fileExists(f)) { createFile(f, c); } + } + if (options.exportAssets) { + createFolder(options.targetFolder.stringByAppendingPathComponent('assets')); + } +} +function exportAsset(context, options, info, skLayer) { + var t = MSExportFormat.formatWithScale_name_fileFormat(options.resolution, options.resolution.toFixed(0)+'x', 'png'); + var r = MSExportRequest.exportRequestsFromExportableLayer_exportFormats_useIDForName(skLayer, [t], false); + var f = options.targetFolder.stringByAppendingPathComponent('assets').stringByAppendingPathComponent(assetName(skLayer)).stringByAppendingPathExtension('png'); + context.document.saveExportRequest_toFile(r.firstObject(), f); + var d = NSData.alloc().initWithContentsOfFile(f).base64EncodedStringWithOptions(0); + if (options.removeDuplicatedAssets) { + if (info.fileNameByBase64[d]) { + deleteFile(f); + f = info.fileNameByBase64[d]; + } else { + info.fileNameByBase64[d] = f; + } + } + info.fileNameById[skLayer.objectID()] = f; +} +function exportScreenpart(context, options, info, skSymbol) { + var b = skSymbol.absoluteRect(); + var g = skSymbol.objectID(); + var r = '\n'; + r += '\n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n' + r += ' \n' + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + skSymbol.layers().forEach(function(skLayer) { r += exportLayer(context, options, info, skLayer, b, ' '); }); + r += ' \n' + r += ' \n' + r += ' \n' + r += ' \n' + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += '\n'; + var p = info.pagesByLayer[g]; + var f = checkFileName(info, skSymbol, info.fileNameById[p.objectID()].stringByAppendingPathComponent(fileName(skSymbol.name(), 'Symbol')), 'screenpart'); + if (fileExists(f)) { + createXmlFile(info, skSymbol, f, merge(loadXml(f), parseXml(r), puid(g,'container'))); + } else { + createFile(f, r); + } +} +function exportScreen(context, options, info, skArtboard) { + var b = skArtboard.absoluteRect(); + var g = skArtboard.objectID(); + var d = w(b) === ''+options.device.size.w || w(b) === ''+options.device.size.h; + var r = '\n'; + r += '\n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + if (d) { + r += ' \n'; + } else { + r += ' \n'; + } + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n' + r += ' \n' + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + if (options.artboardAsImage) { + r += exportImage(context, options, info, skArtboard, b, ' '); + } else { + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + skArtboard.layers().forEach(function(skLayer) { r += exportLayer(context, options, info, skLayer, b, ' '); }); + r += ' \n' + r += ' \n' + r += ' \n' + r += ' \n' + } + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n' + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += ' \n'; + r += '\n'; + var p = info.pagesByLayer[g]; + var f = checkFileName(info, skArtboard, info.fileNameById[p.objectID()].stringByAppendingPathComponent(fileName(skArtboard.name(), 'Artboard')), 'screen'); + if (fileExists(f)) { + createXmlFile(info, skArtboard, f, merge(loadXml(f), parseXml(r), puid(g,'container'))); + } else { + createFile(f, r); + } +} +function exportLayer(context, options, info, skLayer, p, s) { + if (isGroup(skLayer)) { + return exportGroup(context, options, info, skLayer, p, s); + } else if (isSymbol(skLayer)) { + return exportSymbol(context, options, info, skLayer, p, s); + } else if (isShape(skLayer)) { + return exportShape(context, options, info, skLayer, p, s); + } else if (isText(skLayer)) { + return exportText(context, options, info, skLayer, p, s); + } else if (isImage(skLayer)) { + return exportImage(context, options, info, skLayer, p, s); + } else { + log('Unexpected layer type: '+ skLayer.className()); + return ''; + } +} +function exportGroup(context, options, info, skGroup, p, s) { + if (options.shadowedGroupsAsImages && hasShadow(skGroup)) { return exportImage(context, options, info, skGroup, p, s); } + var b = skGroup.absoluteRect(); + var r = ''; + r += s+'\n'; + r += s+' \n'; + skGroup.layers().forEach(function(skLayer) { r += exportLayer(context, options, info, skLayer, b, s+' '); }); + r += s+' \n'; + r += s+'\n'; + return r; +} +function exportSymbol(context, options, info, skSymbol, p, s) { + if (options.symbolsAsImages) { return exportImage(context, options, info, skSymbol, p, s); } + log('No implemented yet (export as screenpart instance)'); + return ''; +} +function exportShape(context, options, info, skShape, p, s) { + if (options.shapesAsImages) { return exportImage(context, options, info, skShape, p, s); } + log('No implemented yet (export as shape)'); + return ''; +} +function exportText(context, options, info, skText, p, s) { + if (options.textsAsImages) { return exportImage(context, options, info, skText, p, s); } + log('No implemented yet (export as text)'); + return ''; +} +function exportImage(context, options, info, skImage, p, s) { + var a = MSImmutableLayerAncestry.ancestryWithMSLayer(skImage); + var l = MSSliceTrimming.trimmedRectForLayerAncestry(a); + var x = Math.round(l.origin.x-p.x()).toFixed(0); + var y = Math.round(l.origin.y-p.y()).toFixed(0); + var w = Math.round(l.size.width).toFixed(0); + var h = Math.round(l.size.height).toFixed(0); + return s+'\n'; +} +// Merger +function createXmlFile(info, l, f, m) { + while (!m.done) { + var n = f.stringByDeletingPathExtension(); + var e = f.pathExtension(); + var i = 1; + var g = n.stringByAppendingString(' ('+i+')').stringByAppendingPathExtension(e); + if (fileExists(g)) { + m = merge(loadXml(g), m.result, m.guid); + if (m.done) { f = g; } + } else { + m.done = true; + f = g; + } + } + info.fileNames.push((''+f).toLowerCase()); + info.fileNameById[l.objectID()] = f; + m.result.XMLDataWithOptions(NSXMLNodePrettyPrint).writeToFile_atomically(f, true); +} +function merge(indigoXml, sketchXml, guid) { + var indigoNode = queryXml(indigoXml, './/Container[@Id="'+guid+'"]/Container.Children'); + var sketchNode = queryXml(sketchXml, './/Container[@Id="'+guid+'"]/Container.Children'); + if (indigoNode && indigoNode.count() > 0 && sketchNode && sketchNode.count() > 0) { + if (sketchNode[0].childCount() > 0) { + sketchNode[0].children().forEach(function(n) { + mergeInteractions(n, indigoXml); + }); + } + var targetNode = indigoNode[0].parent(); + indigoNode[0].detach(); + sketchNode[0].detach(); + targetNode.addChild(sketchNode[0]); + return { done: true, result: indigoXml, guid: guid }; + } else { + return { done: false, result: sketchXml, guid: guid }; + } +} +function mergeInteractions(sketchNode, indigoXml) { + var node = findXml(indigoXml, sketchNode.localName(), sketchNode.attributeForName('Id').stringValue()); + if (node && node.childCount() > 0) { + node.children().forEach(function(n) { + if (!n.localName().isEqualToString('Group.Children')) { + n.detach(); + sketchNode.addChild(n); + } + }); + } + if (sketchNode.localName().isEqualToString('Group') && sketchNode.childAtIndex(0).localName().isEqualToString('Group.Children') && sketchNode.childAtIndex(0).childCount() > 0) { + sketchNode.childAtIndex(0).children().forEach(function(n) { + mergeInteractions(n, indigoXml); + }); + } +} \ No newline at end of file diff --git a/Indigo Exporter.sketchplugin/Contents/Sketch/helpers.js b/Indigo Exporter.sketchplugin/Contents/Sketch/helpers.js new file mode 100644 index 0000000..c4ef981 --- /dev/null +++ b/Indigo Exporter.sketchplugin/Contents/Sketch/helpers.js @@ -0,0 +1,238 @@ +// Miscelaneous +function alert(msg, title) { NSApplication.sharedApplication().displayDialog_withTitle(msg, title || 'Alert'); } +function uuid() { return NSString.stringWithUUID(); } +function puid(seed, seq) { return seed.stringByAppendingString('-').stringByAppendingString(seq); } +function toNumber(s) { return isNaN(parseInt(s))?null:parseInt(s); } +function rgba(r, g, b, a) { return NSColor.colorWithRed_green_blue_alpha(r/255, g/255, b/255, a); } +function w(rect) { return rect ? rect.width().toFixed(0) : ''; }; +function h(rect) { return rect ? rect.height().toFixed(0) : ''; }; +function wm(rect, m) { return rect ? Math.max(m, rect.width()).toFixed(0) : ''; }; +function hm(rect, m) { return rect ? Math.max(m, rect.height()).toFixed(0) : ''; }; +function rx(rect, parentRect) { return rect ? Math.round(rect.x()-parentRect.x()).toFixed(0) : ''; }; +function ry(rect, parentRect) { return rect ? Math.round(rect.y()-parentRect.y()).toFixed(0) : ''; }; +function getBackground(skLayer) { return skLayer.hasBackgroundColor() != 0 ? color(skLayer.backgroundColor()) : '#00FFFFFF'; }; +function color(nsColor) { return '#'+hex(nsColor.alpha())+hex(nsColor.red())+hex(nsColor.green())+hex(nsColor.blue()); }; +function hex(value) { return pad0((0xFF&(255*value)).toString(16)); } +function pad0(val) { return '00'.slice(0, 2 - val.length) + val; } +function fileName(f, t) { + var name = NSRegularExpression + .regularExpressionWithPattern_options_error('[^-_a-zA-Z0-9]+', 0, null) + .stringByReplacingMatchesInString_options_range_withTemplate(f, 0, NSMakeRange(0, f.length()), ' ') + .stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()); + if (isReservedFileName(name)) { name = NSString.stringWithString(t).stringByAppendingString(' ').stringByAppendingString(name); } + if (name.length() > 100) { name = name.substringToIndex(100).stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()); } + return name; +} +function checkFileName(info, l, n, e) { + var i = 1, f = e ? n.stringByAppendingPathExtension(e) : n; + while (info.fileNames.indexOf((''+f).toLowerCase()) >= 0) { + var m = n.stringByAppendingString(' ('+(i++)+')'); + f = e ? m.stringByAppendingPathExtension(e) : m; + } + info.fileNames.push((''+f).toLowerCase()); + info.fileNameById[l.objectID()] = f; + return f; +} +function isReservedFileName(f) { return NSRegularExpression.regularExpressionWithPattern_options_error('^(con|prn|aux|clock$|nul|com0|com1|com2|com3|com4|com5|com6|com7|com8|com9|lpt0|lpt1|lpt2|lpt3|lpt4|lpt5|lpt6|lpt7|lpt8|lpt9|assets|autorecover|_archive|_codesnippets)$', 0, null).numberOfMatchesInString_options_range(f, 0, NSMakeRange(0, f.length())) > 0; } +function assetName(l) { return className(l).stringByAppendingString(' ').stringByAppendingString(l.objectID().lowercaseString()); } +function className(l) { return isArtboard(l) ? NSString.stringWithString('artboard') : isSymbolMaster(l) ? NSString.stringWithString('symbolmaster') : isSymbol(l) ? NSString.stringWithString('symbol') : isGroup(l) ? NSString.stringWithString('group') : isShape(l) ? NSString.stringWithString('shape') : isText(l) ? NSString.stringWithString('text') : isImage(l) ? NSString.stringWithString('image') : l.className(); } +function pickTargetFolder(defaultFolder) { + var dlg = NSSavePanel.savePanel(); + dlg.setAllowedFileTypes(NSArray.arrayWithObject('indigo')); + dlg.setExtensionHidden(false); + dlg.setNameFieldStringValue(defaultFolder); + return dlg.runModal() != 0 ? dlg.URL().path() : null; +} +function escapeHtml(string) { + var matchHtmlRegExp = new RegExp('["\'&<>\u005d'); // Use unicode escape sequence for end brace to workaround cocoascript + var str = '' + string; + var match = matchHtmlRegExp.exec(str); + if (!match) { return str; } + var escape; + var html = ''; + var index = 0; + var lastIndex = 0; + for (index = match.index; index < str.length; index++) { + switch (str.charCodeAt(index)) { + case 34: escape = '"'; break; // " + case 38: escape = '&'; break; // & + case 39: escape = '''; break; // ' + case 60: escape = '<'; break; // < + case 62: escape = '>'; break; // > + default: continue; + } + if (lastIndex !== index) { html += str.substring(lastIndex, index); } + lastIndex = index + 1; + html += escape; + } + return lastIndex !== index ? html + str.substring(lastIndex, index) : html; +} +// File System +function fileExists(fileURL) { return NSFileManager.defaultManager().fileExistsAtPath(fileURL); } +function deleteFile(fileURL) { NSFileManager.defaultManager().removeItemAtPath_error(fileURL, null); } +function createFolder(folderURL) { NSFileManager.defaultManager().createDirectoryAtURL_withIntermediateDirectories_attributes_error(NSURL.fileURLWithPath_isDirectory(folderURL, true), true, null, null); } +function createFile(fileURL, content) { createFolder(fileURL.stringByDeletingLastPathComponent()); NSString.stringWithString(content).dataUsingEncoding(NSUTF8StringEncoding).writeToFile(fileURL); } +// XML +function parseXml(strXml) { + try { + return NSXMLDocument.alloc().initWithXMLString_options_error(strXml, NSXMLDocumentTidyXML, null); + } catch(e) { + alert(e.message, 'ERROR'); + return null; + } +} +function loadXml(fileName) { + try { + return NSXMLDocument.alloc().initWithContentsOfURL_options_error(NSURL.fileURLWithPath(fileName), NSXMLDocumentTidyXML, null); + } catch(e) { + alert(e.message, 'ERROR'); + return null; + } +} +function findXml(doc, tag, guid) { + var res = doc ? queryXml(doc,'.//'+tag+'[@Id="'+guid+'"]') : null; + return res && res.count() > 0 ? res[0]: null; +} +function queryXml(doc, qry) { + return doc ? doc.nodesForXPath_error(qry, null) : []; +} +// Async +function Delegate(selectors) { + this.uniqueName = 'DelegateClass' + uuid(); + this.classDesc = MOClassDescription.allocateDescriptionForClassWithName_superclass_(this.uniqueName, NSObject); + this.handlers = {}; + this.getClass = function() { return NSClassFromString(this.uniqueName); }; + this.getInstance = function() { return NSClassFromString(this.uniqueName).new(); }; + this.classDesc.registerClass(); + for (s in selectors) { + this.handlers[s] = selectors[s]; + var h = (function() { return this.handlers[s].apply(this.classDesc, arguments); }).bind(this); + var args = [], regex = /:/g; while (match = regex.exec(s)) { args.push('arg'+args.length); } + this.classDesc.addInstanceMethodWithSelector_function_(NSSelectorFromString(s), eval('(function('+args.join(',')+'){ return h.apply(this, arguments); })')); + } +} +// UI +function createDialog(x, y, w, h, c) { + var dlg = NSWindow.alloc().init(); + var cnt = dlg.contentView(); + cnt.setWantsLayer(true); + cnt.setBackgroundColor(c); + dlg.setFrame_display(NSMakeRect(x, y, w, h), false); + return dlg; +} +function runDialog(dlg, options) { + COScript.currentCOScript().setShouldKeepAround_(true); + var app = NSApplication.sharedApplication(); + var win = app.mainWindow(); + if (options && options.defaultButton) { dlg.setDefaultButtonCell(options.defaultButton); } + if (options && options.timer) { NSRunLoop.currentRunLoop().addTimer_forMode(options.timer, NSRunLoopCommonModes); } + dlg.orderOut(null); + win.beginSheet_completionHandler(dlg, null); +} +function endDialog(dlg, options) { + var app = NSApplication.sharedApplication(); + var win = app.mainWindow(); + if (options && options.cleanup) { options.cleanup.forEach(function(l) { l.setCOSJSTargetFunction(undefined); }); } + dlg.orderOut(null); + win.endSheet(dlg); + COScript.currentCOScript().setShouldKeepAround_(false); +} +function addRectangle(cnt, x, y, w, h, c, f) { + var rec = NSView.alloc().initWithFrame(NSMakeRect(x, y, w, h)); + rec.setWantsLayer(true); + rec.setBackgroundColor(c); + if (f) { rec.setCOSJSTargetFunction(f); } + if (cnt) { cnt.addSubview(rec); } + return rec; +} +function addImage(cnt, x, y, w, h, r, s, f) { + var img = NSImageView.alloc().initWithFrame(NSMakeRect(x, y, w*s, h*s)); + img.setImage(NSImage.alloc().initByReferencingURL(r)); + if (f) { img.setCOSJSTargetFunction(f); } + if (cnt) { cnt.addSubview(img); } + return img; +} +function addLabel(cnt, x, y, w, h, l, c, s, a, f, b) { + var lbl = NSTextField.alloc().initWithFrame(NSMakeRect(x, y, w, h)); + lbl.setEditable(false); + lbl.setBordered(false); + lbl.setDrawsBackground(false); + lbl.setFont(f ? NSFontManager.sharedFontManager().fontWithFamily_traits_weight_size(f, NSBoldFontMask, b, s) : NSFont.systemFontOfSize(s)); + lbl.setTextColor(c); + lbl.setAlignment(a); + lbl.setStringValue(l) + if (cnt) { cnt.addSubview(lbl); } + return lbl; +} +function addTextbox(cnt, x, y, w, h, l, c, s, a, f, b) { + var txt = NSTextField.alloc().initWithFrame(NSMakeRect(x, y, w, h)); + txt.setEditable(true); + txt.setBordered(false); + txt.setDrawsBackground(true); + txt.setFont(f ? NSFontManager.sharedFontManager().fontWithFamily_traits_weight_size(f, NSBoldFontMask, b, s) : NSFont.systemFontOfSize(s)); + txt.setTextColor(c); + txt.setAlignment(a); + txt.setStringValue(l); + if (cnt) { cnt.addSubview(txt); } + return txt; +} +function addCheckbox(cnt, x, y, w, h, t, s, f) { + var chk = NSButton.alloc().initWithFrame(NSMakeRect(x, y, w, h)); + chk.setButtonType(NSSwitchButton); + chk.setBezelStyle(0); + chk.setTitle(t); + chk.setState(s); + if (f) { chk.setCOSJSTargetFunction(f); } + if (cnt) { cnt.addSubview(chk); } + return chk; +} +function addCombobox(cnt, x, y, w, h, l, f) { + var cmb = NSPopUpButton.alloc().initWithFrame(NSMakeRect(x, y, w, h)); + cmb.addItemsWithTitles(l); + cmb.setFont(NSFont.systemFontOfSize(13)); + cmb.selectItemAtIndex(0); + if (f) { cmb.setCOSJSTargetFunction(f); } + if (cnt) { cnt.addSubview(cmb); } + return cmb; +} +function addRadioGroup(cnt, x, y, w, h, l, r, c, f) { + var str = ''; l.forEach(function(m, i) { str = m.length >= str.length ? m + ' ' : str; }); + var tpl = NSButtonCell.alloc().init(); tpl.setTitle(str); tpl.setButtonType(NSRadioButton); + var grp = NSMatrix.alloc().initWithFrame_mode_prototype_numberOfRows_numberOfColumns(NSMakeRect(x, y, w, h), NSRadioModeMatrix, tpl, r, c); + l.forEach(function(m, i) { grp.cells().objectAtIndex(i).setTitle(m); }); + if (f) { grp.setCOSJSTargetFunction(f); } + if (cnt) { cnt.addSubview(grp); } + return grp; +} +function addButton(cnt, x, y, w, h, l, f) { + var btn = NSButton.alloc().initWithFrame(NSMakeRect(x, y, w, h)); + btn.setAction('callAction:'); + btn.setTitle(l); + btn.setBezelStyle(NSRoundedBezelStyle); + if (f) { btn.setCOSJSTargetFunction(f); } + if (cnt) { cnt.addSubview(btn); } + return btn; +} +function addProgress(cnt, x, y, w, h, l) { + var bar = NSProgressIndicator.alloc().initWithFrame(NSMakeRect(x, y, w, h)); + bar.setStyle(0); + bar.setBezeled(true); + bar.setMinValue(0); + bar.setMaxValue(l); + bar.setDoubleValue(0); + bar.setIndeterminate(false); + if (cnt) { cnt.addSubview(bar); } + return bar; +} +// Sketch Layer Type checking +function isArtboard(skLayer) { return skLayer.isMemberOfClass(MSImmutableArtboardGroup) || skLayer.isMemberOfClass(MSArtboardGroup); }; +function isSymbolMaster(skLayer) { return skLayer.isMemberOfClass(MSSymbolMaster); }; +function isSymbol(skLayer) { return skLayer.isMemberOfClass(MSSymbolInstance); }; +function isGroup(skLayer) { return skLayer.isMemberOfClass(MSLayerGroup); }; +function isShape(skLayer) { return skLayer.isMemberOfClass(MSShapeGroup); }; +function isImage(skLayer) { return skLayer.isMemberOfClass(MSBitmapLayer); }; +function isText(skLayer) { return skLayer.isMemberOfClass(MSTextLayer); }; +function isSlice(skLayer) { return skLayer.isMemberOfClass(MSSliceLayer); }; +function getSelectedArtboards(skPage) { var s = []; if (skPage) { skPage.selectedLayers().layers().forEach(function(a) { if (isArtboard(a)) { s.push(a); } }); } return s; } +function hasMask(skLayer) { var res = false; skLayer.layers().forEach(function(l) { if (l.hasClippingMask()) { res = true; } }); return res; } +function hasShadow(skLayer) { var res = false; (skLayer.style() || { shadows: [] }).shadows().forEach(function(s) { if (s.isEnabled()) { res = true; } }); return res; } diff --git a/Indigo Exporter.sketchplugin/Contents/Sketch/main.js b/Indigo Exporter.sketchplugin/Contents/Sketch/main.js new file mode 100644 index 0000000..966976c --- /dev/null +++ b/Indigo Exporter.sketchplugin/Contents/Sketch/main.js @@ -0,0 +1,94 @@ +@import 'exports.js' +@import 'dialogs.js' +@import 'helpers.js' + +// Sketch commands +function onExportAll(context) { + exportToIndigo(context, { pages: context.document.pages(), artboards: [] }); +} +function onExportCurrentPage(context) { + if (context.document.currentPage()) { + exportToIndigo(context, { pages: [context.document.currentPage()], artboards: [] }); + } else { + alert('Select a page to export'); + } +} +function onExportSelectedArtboards(context) { + var s = getSelectedArtboards(context.document.currentPage()); + if (s.length > 0) { + exportToIndigo(context, { pages: [context.document.currentPage()], artboards: s }); + } else { + alert('Select at least one artboard to export'); + } +} +function exportToIndigo(context, options) { + var art = getFirstArtboard(context, options); + var dlg = new ExportDialog(context, art ? art.absoluteRect() : null); + dlg.onExport = function(state) { dlg.close(); onExportHandler(context, options, state); } + dlg.run(); +} +// Dialogs Event Handlers +function onExportHandler(context, options, state) { + options.exportProject = state.exportProject; + options.exportArtboards = state.exportArtboards; + options.exportSymbols = state.exportSymbols; + options.exportAssets = state.exportAssets; + options.removeDuplicatedAssets = state.removeDuplicatedAssets; + options.artboardsAsImages = state.artboardsAsImages; + options.shadowedGroupsAsImages = state.shadowedGroupsAsImages; + options.symbolsAsImages = state.symbolsAsImages; + options.shapesAsImages = state.shapesAsImages; + options.textsAsImages = state.textsAsImages; + options.target = state.target; + options.device = state.device; + options.orientation = state.orientation; + options.statusbar = state.statusbar; + options.resolution = state.resolution; + var info = { artboards: [], symbols: [], assets: [], start: Date.now(), step: 0, i: 0, symbolsById: {}, pagesByLayer: {}, fileNames: [], fileNameById: {}, fileNameByBase64: {} }; + var fld = context.document.displayName().stringByDeletingPathExtension().stringByAppendingPathExtension('indigo'); + var trg = pickTargetFolder(fld); + if (trg) { + options.targetFolder = trg; + var dlg = new ProgressDialog(context); + dlg.onStep = function(timer) { onStepHandler(context, options, info, dlg, timer); } + dlg.run(); + } +} +function onStepHandler(context, options, info, dlg, timer) { + try { + switch (info.step) { + case 0: // Gather info to export + if (info.i>=options.pages.length) { info.step++; info.i=0; return; } + if (info.i==0) { dlg.resetProgress('Gathering data...', options.pages.length); createProject(context, options); } + var l = options.pages[info.i++]; dlg.setProgress('Scaning page: '+fileName(l.name(), 'Page'), info.i); + scanPage(context, options, info, l); + break; + case 1: // Export assets + if (info.i>=info.assets.length) { info.step++; info.i=0; return; } + if (info.i==0) { dlg.resetProgress('Exporting assets...', info.assets.length); } + var l = info.assets[info.i++]; dlg.setProgress((100*info.i/info.assets.length).toFixed(0)+'% ('+info.i+' of '+info.assets.length+')', info.i); + exportAsset(context, options, info, l); + break; + case 2: // Export Screenparts + if (info.i>=info.symbols.length) { info.step++; info.i=0; return; } + if (info.i==0) { dlg.resetProgress('Exporting Screensparts...', info.symbols.length); } + var l = info.symbols[info.i++]; dlg.setProgress((100*info.i/info.symbols.length).toFixed(0)+'% ('+info.i+' of '+info.symbols.length+')', info.i); + exportScreenpart(context, options, info, l); + break; + case 3: // Export screens + if (info.i>=info.artboards.length) { info.step++; info.i=0; return; } + if (info.i==0) { dlg.resetProgress('Exporting Screens...', info.artboards.length); } + var l = info.artboards[info.i++]; dlg.setProgress((100*info.i/info.artboards.length).toFixed(0)+'% ('+info.i+' of '+info.artboards.length+')', info.i); + exportScreen(context, options, info, l); + break; + case 4: // End + timer.invalidate(); + dlg.setSummary(info.artboards.length+' screens exported.'); + dlg.onFinish = function() { NSWorkspace.sharedWorkspace().openFile(options.targetFolder); } + break; + } + } catch (e) { + timer.invalidate(); + alert('Error '+e.message, e); + } +} diff --git a/Indigo Exporter.sketchplugin/Contents/Sketch/manifest.json b/Indigo Exporter.sketchplugin/Contents/Sketch/manifest.json index 51c2b7b..d13eaa3 100644 --- a/Indigo Exporter.sketchplugin/Contents/Sketch/manifest.json +++ b/Indigo Exporter.sketchplugin/Contents/Sketch/manifest.json @@ -1,22 +1,39 @@ { - "name" : "Infragistics", - "identifier" : "com.infragistics.indigo.exportplugin", - "version" : "1.0.0", - "description" : "Export scketch projects to Indigo Prototypes.", - "authorEmail" : "indigo@infragistics.com", - "author" : "Infragistics", - "commands" : [ - { - "script" : "script.js", - "handler" : "onRun", - "shortcut" : "", - "name" : "Export to Indigo Prototype", - "identifier" : "exportcommand" - }, - ], - "menu": { - "items": [ - "exportcommand" - ] - } + "name": "Indigo Studio", + "identifier": "com.infragistics.indigo.exportplugin", + "version": "1.0.0", + "description": "Export to Indigo Studio to create interactive, animated prototypes.", + "authorEmail": "indigo@infragistics.com", + "author": "Infragistics", + "appcast": "https://indigo.infragistics.com/sketch/appcast.xml", + "commands": [ + { + "script": "main.js", + "handler": "onExportAll", + "shortcut": "", + "name": "Export all pages", + "identifier": "export-all-command" + }, + { + "script": "main.js", + "handler": "onExportCurrentPage", + "shortcut": "", + "name": "Export current page", + "identifier": "export-current-page-command" + }, + { + "script": "main.js", + "handler": "onExportSelectedArtboards", + "shortcut": "", + "name": "Export selected artboards", + "identifier": "export-selected-artboards-command" + } + ], + "menu": { + "items": [ + "export-all-command", + "export-current-page-command", + "export-selected-artboards-command" + ] + } } diff --git a/Indigo Exporter.sketchplugin/Contents/Sketch/script.js b/Indigo Exporter.sketchplugin/Contents/Sketch/script.js deleted file mode 100644 index a3cdb26..0000000 --- a/Indigo Exporter.sketchplugin/Contents/Sketch/script.js +++ /dev/null @@ -1,267 +0,0 @@ -function onRun(context) { - // Auxiliar - function uuid() { return NSString.stringWithUUID(); } - function w(rect) { return rect.width().toFixed(0); } - function h(rect) { return rect.height().toFixed(0); } - function rx(rect, parentRect) { return Math.round(rect.x()-parentRect.x()).toFixed(0); } - function ry(rect, parentRect) { return Math.round(rect.y()-parentRect.y()).toFixed(0); } - function getBackground(skArtboard) { return skArtboard.hasBackgroundColor() ? color(skArtboard.backgroundColor()) : '#00FFFFFF'; } - function color(nsColor) { return '#'+((1<<24)+(nsColor.red()<<16)+(nsColor.green()<<8)+nsColor.blue()).toString(16).slice(9); } - // Layer Type Checking - function isArtboard(skLayer) { return skLayer.isMemberOfClass(MSImmutableArtboardGroup) || skLayer.isMemberOfClass(MSArtboardGroup); } - function isSymbolMaster(skLayer) { return skLayer.isMemberOfClass(MSSymbolMaster); } - function isGroup(skLayer) { return skLayer.isMemberOfClass(MSLayerGroup); } - function isShape(skLayer) { return skLayer.isMemberOfClass(MSShapeGroup); } - function isImage(skLayer) { return skLayer.isMemberOfClass(MSBitmapLayer); } - function isText(skLayer) { return skLayer.isMemberOfClass(MSTextLayer); } - function isSymbol(skLayer) { return skLayer.isMemberOfClass(MSSymbolInstance); } - // Layer Exporters - function exportSymbols(context, skPage) { - pageFOLDER = projFOLDER.stringByAppendingPathComponent(skPage.name().stringByReplacingOccurrencesOfString_withString('/','-')); - createFolder(pageFOLDER); - skPage.layers().forEach(function(skLayer) { - if (isSymbolMaster(skLayer)) { - try { - exportLayer(context, skLayer, ''); - } catch(e) { - logINFO.log.push(e.message); - logINFO.errors.push(e); - } - } - }); - } - function exportPage(context, skPage) { - pageFOLDER = projFOLDER.stringByAppendingPathComponent(skPage.name().stringByReplacingOccurrencesOfString_withString('/','-')); - skPage.layers().forEach(function(skLayer) { - if (isArtboard(skLayer)) { - try { - exportLayer(context, skLayer, ''); - } catch(e) { - logINFO.log.push(e.message); - logINFO.errors.push(e); - } - } - }); - } - function exportLayer(context, skLayer, p, s) { - if (isArtboard(skLayer)) { - return exportArtboard(context, skLayer, p, s); - } else if (isSymbolMaster(skLayer)) { - return exportSymbolMaster(context, skLayer, p, s); - } else if (isGroup(skLayer)) { - return exportGroup(context, skLayer, p, s); - } else if (isShape(skLayer)) { - return exportShape(context, skLayer, p, s); - } else if (isImage(skLayer)) { - return exportImage(context, skLayer, p, s); - } else if (isText(skLayer)) { - return exportText(context, skLayer, p, s); - } else if (isSymbol(skLayer)) { - return exportSymbol(context, skLayer, p, s); - } else { - return ''; - } - } - function exportArtboard(context, skArtboard, p, s) { - var b = skArtboard.absoluteRect(); - var l = uuid(); // Layer id - var c = uuid(); // Content id - var r = '\n'; - r += '\n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n' - r += ' \n' - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - skArtboard.layers().forEach(function(skLayer) { - r += exportLayer(context, skLayer, b, ' '); - }); - r += ' \n' - r += ' \n' - r += ' \n' - r += ' \n' - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += '\n'; - createFile(pageFOLDER.stringByAppendingPathComponent(skArtboard.name().stringByReplacingOccurrencesOfString_withString('/','-').stringByAppendingPathExtension('screen')), r); - return r; - } - function exportSymbolMaster(context, skSymbol, p, s) { - var b = skSymbol.absoluteRect(); - var l = uuid(); // Layer id - var c = uuid(); // BorderedContent id - var r = '\n'; - r += '\n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n' - r += ' \n' - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - skSymbol.layers().forEach(function(skLayer) { - r += exportLayer(context, skLayer, b, ' '); - }); - r += ' \n' - r += ' \n' - r += ' \n' - r += ' \n' - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += '\n'; - symbolPATH = pageFOLDER.stringByAppendingPathComponent(skSymbol.name().stringByReplacingOccurrencesOfString_withString('/','-').stringByAppendingPathExtension('screenpart')); - symbolMAP[skSymbol.objectID()] = symbolPATH.substringFromIndex(projFOLDER.length()); - createFile(symbolPATH, r); - return r; - } - function exportGroup(context, skGroup, p, s) { - var b = skGroup.absoluteRect(); - var r = ''; - r += s+'\n'; - r += s+' \n'; - skGroup.layers().forEach(function(skLayer) { - r += exportLayer(context, skLayer, b, s+' '); - }); - r += s+' \n'; - r += s+'\n'; - return r; - } - function exportSymbol(context, skSymbol, p, s) { - /* - var b = skSymbol.absoluteRect(); - var t = skSymbol.symbolMaster().objectID(); - var r = ''; - r += s+'\n'; - r += s+' \n'; - r += s+' \n'; - r += s+'\n'; - return r; */ - return s+generateImageFile(context, p, skSymbol); - } - function exportShape(context, skShape, p, s) { return s+generateImageFile(context, p, skShape); } - function exportImage(context, skImage, p, s) { return s+generateImageFile(context, p, skImage); } - function exportText(context, skText, p, s) { return s+generateImageFile(context, p, skText); } - // Content generation - function generateImageFile(context, p, skLayer) { - var c = skLayer.duplicate(); - var o = c.exportOptions(); o.removeAllExportFormats(); - var s = o.addExportFormat(); s.setScale(1.0); - var t = o.exportFormats(); - var l = MSExportRequest.exportRequestsFromExportableLayer_exportFormats_useIDForName(c, t, false).firstObject(); - context.document.saveExportRequest_toFile(l, projFOLDER+'/assets/'+skLayer.objectID()+'.png'); c.removeFromParent(); - var a = MSImmutableLayerAncestry.ancestryWithMSLayer(skLayer); - var r = MSSliceTrimming.trimmedRectForLayerAncestry(a); - var x = Math.round(r.origin.x-p.x()).toFixed(0); - var y = Math.round(r.origin.y-p.y()).toFixed(0); - var w = Math.round(r.size.width).toFixed(0); - var h = Math.round(r.size.height).toFixed(0); - return '\n'; - } - function generateProjectFILE(projId) { - var r = '\n'; - r += '\n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ' \n'; - r += ''; - return r; - } - function createFolder(folderURL) { - logINFO.log.push('Creating folder '+ folderURL); - var nsFM = NSFileManager.defaultManager(); - var nsURL = NSURL.fileURLWithPath_isDirectory(folderURL, true); - nsFM.createDirectoryAtURL_withIntermediateDirectories_attributes_error(nsURL, true, null, null); - } - function createFile(fileURL, content) { - logINFO.log.push('Creating file '+ fileURL); - var nsStr = NSString.stringWithString(content); - var nsData = nsStr.dataUsingEncoding(NSUTF8StringEncoding); - nsData.writeToFile(fileURL); - } - // MAIN - var projPATH = context.document.fileURL().path().stringByDeletingLastPathComponent(); - var projNAME = context.document.displayName().stringByDeletingPathExtension(); - var projFOLDER = projPATH.stringByAppendingPathComponent(projNAME.stringByAppendingPathExtension('indigo')); - var projFILE = projFOLDER.stringByAppendingPathComponent(projNAME.stringByAppendingPathExtension('proj')); - var pageFOLDER = NSString.stringWithString(''); - var symbolMAP = {}; - var logFILE = projFOLDER.stringByAppendingPathComponent('export.log'); - var logINFO = { start: Date.now(), end: Date.now(), time: 0, log: [], errors: [] }; - try { - createFolder(projFOLDER); - createFile(projFILE, generateProjectFILE('ORXKCYQJREDU')); - context.document.pages().forEach(function(skPage) { - exportSymbols(context, skPage); - }); - context.document.pages().forEach(function(skPage) { - exportPage(context, skPage); - }); - } catch(e) { - logINFO.log.push(e.message); - logINFO.errors.push(e); - } finally { - logINFO.end = Date.now(); - logINFO.time = (logINFO.end - logINFO.start) / 1000; - createFile(logFILE, JSON.stringify(logINFO, null, 4)); - } -}; \ No newline at end of file diff --git a/appcast.xml b/appcast.xml new file mode 100644 index 0000000..c960803 --- /dev/null +++ b/appcast.xml @@ -0,0 +1,20 @@ + + + + Indigo Studio export plugin + Export to Indigo Studio to create interactive, animated prototypes. + https://indigo.infragistics.com/sketch/appcast.xml + en + + Version 1.0.0 + + +
  • Initial version 1.0.0
  • + + ]]> +
    + +
    +
    +