├── .agignore ├── .gitignore ├── README.md ├── _versions ├── zenb.sketchplugin │ └── Contents │ │ └── Sketch │ │ ├── Layers │ │ ├── Append-BEM.cocoascript │ │ ├── Make-Font-Artboard-Icons.cocoascript │ │ ├── Remove-Empty-Groups.cocoascript │ │ ├── Resize-Group-Area.cocoascript │ │ ├── Send-To-Artboard.cocoascript │ │ ├── Snap-Layer-To-Eight.cocoascript │ │ ├── Toggle-Click-Throught.cocoascript │ │ ├── Toggle-User-Guides.cocoascript │ │ └── Transform-Each.cocoascript │ │ ├── Preview │ │ ├── Preview-Artboard-Desktop.cocoascript │ │ └── Preview-Artboard-Mobile.cocoascript │ │ ├── Typography │ │ └── ModularScale.cocoascript │ │ ├── _Wip │ │ ├── _Filter Shapes.cocoascript │ │ ├── _Filter Texts.cocoascript │ │ ├── _Fit Artobard Width.cocoascript │ │ ├── _Fit Group Size.cocoascript │ │ └── _clipboard.cocoascript │ │ ├── manifest.json │ │ └── sQuery │ │ ├── liveQuery.js │ │ ├── plugins │ │ ├── core.areGroups.js │ │ ├── core.areShapes.js │ │ ├── core.move.js │ │ └── core.parentRoot.js │ │ └── sQuery.js └── zenc.sketchplugin │ └── Contents │ └── Sketch │ ├── Layers │ ├── Append-BEM.cocoascript │ ├── Make-Font-Artboard-Icons.cocoascript │ ├── Remove-Empty-Groups.cocoascript │ ├── Resize-Group-Area.cocoascript │ ├── Send-To-Artboard.cocoascript │ ├── Snap-Layer-To-Eight.cocoascript │ ├── Toggle-Click-Throught.cocoascript │ ├── Toggle-User-Guides.cocoascript │ ├── Transform-Each.cocoascript │ └── UnlockAllLayers.cocoascript │ ├── Preview │ ├── Preview-Artboard-Desktop.cocoascript │ └── Preview-Artboard-Mobile.cocoascript │ ├── Typography │ └── ModularScale.cocoascript │ ├── _Wip │ ├── _Filter Shapes.cocoascript │ ├── _Filter Texts.cocoascript │ ├── _Fit Artobard Width.cocoascript │ ├── _Fit Group Size.cocoascript │ └── _clipboard.cocoascript │ ├── manifest.json │ └── sQuery │ ├── liveQuery.js │ ├── liveQuery │ ├── _liveQuery.js │ └── liveQuery.js │ ├── plugins │ ├── sQuery.areGroups.js │ ├── sQuery.areShapes.js │ ├── sQuery.move.js │ └── sQuery.sendToRoot.js │ └── sQuery.js ├── appcast.xml ├── gulpfile.js ├── package.json └── zen.sketchplugin └── Contents └── Sketch ├── Layers ├── Append-BEM.cocoascript ├── Make-Font-Artboard-Icons.cocoascript ├── Remove-Empty-Groups.cocoascript ├── Resize-Group-Area.cocoascript ├── Select-Childs-Layers.js ├── Send-To-Artboard.cocoascript ├── Snap-Layer-To-Eight.cocoascript ├── Toggle-Click-Throught.cocoascript ├── Toggle-User-Guides.cocoascript ├── Transform-Each.cocoascript └── UnlockAllLayers.cocoascript ├── Preview ├── Preview-Artboard-Desktop.cocoascript ├── Preview-Artboard-Mobile.cocoascript ├── Preview-Artboard-Simple.cocoascript ├── Preview-Artboard-iphonex.cocoascript └── common-preview.js ├── Typography └── ModularScale.cocoascript ├── manifest.json └── sQuery ├── liveQuery.js ├── liveQuery ├── _liveQuery.js └── liveQuery.js ├── plugins ├── sQuery.areGroups.js ├── sQuery.areShapes.js ├── sQuery.move.js └── sQuery.sendToRoot.js └── sQuery.js /.agignore: -------------------------------------------------------------------------------- 1 | .git 2 | node_modules 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/node 2 | 3 | ### Node ### 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | 9 | # Runtime data 10 | pids 11 | *.pid 12 | *.seed 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directory 30 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 31 | node_modules 32 | 33 | # Created by https://www.gitignore.io/api/osx 34 | 35 | ### OSX ### 36 | .DS_Store 37 | .AppleDouble 38 | .LSOverride 39 | 40 | # Icon must end with two \r 41 | Icon 42 | 43 | # Thumbnails 44 | ._* 45 | 46 | # Files that might appear in the root of a volume 47 | .DocumentRevisions-V100 48 | .fseventsd 49 | .Spotlight-V100 50 | .TemporaryItems 51 | .Trashes 52 | .VolumeIcon.icns 53 | 54 | # Directories potentially created on remote AFP share 55 | .AppleDB 56 | .AppleDesktop 57 | Network Trash Folder 58 | Temporary Items 59 | .apdisk 60 | 61 | zen.sketchplugin/Contents/Sketch/_Wip 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ZEN Sketch 2 | A very personal collection of Sketch plugins. 3 | -------------------------------------------------------------------------------- /_versions/zenb.sketchplugin/Contents/Sketch/Layers/Append-BEM.cocoascript: -------------------------------------------------------------------------------- 1 | // Append BEM 2 | // @shortcut alt cmd r 3 | 4 | var onRun = function(context) { 5 | @import '../sQuery/sQuery.js'; 6 | 7 | $("%selected%").each(function(){ 8 | 9 | var layer = $(this).MSLayer(); 10 | var B = /^[^__]*/g.exec(layer.parentGroup().name())[0]; 11 | var EM = /__.*/g.exec(layer.name()); 12 | if (EM) { 13 | layer.name = B+EM; 14 | } else { 15 | layer.name = B+"__"+layer.name(); 16 | } 17 | 18 | }); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /_versions/zenb.sketchplugin/Contents/Sketch/Layers/Make-Font-Artboard-Icons.cocoascript: -------------------------------------------------------------------------------- 1 | var onRun = function(context) { 2 | 3 | @import '../sQuery/sQuery.js'; 4 | 5 | function pad(n, width, z) { 6 | z = z || '0'; 7 | n = n + ''; 8 | return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n; 9 | } 10 | 11 | function iconNameRename(glyphName, withIconName) { 12 | withIconName = withIconName || false; 13 | var artboards = $("%artboards%"); 14 | var iconName; 15 | //artboards.layers.reverse(); 16 | artboards.each(function(idx) { 17 | if (withIconName) { 18 | iconName = "-" + this.layers().array()[0].name(); 19 | } else { 20 | iconName = ''; 21 | } 22 | this.name = glyphName + pad(idx, 3) + iconName; 23 | }); 24 | } 25 | 26 | 27 | function reorderArtboards(itemsPerFile, offset) { 28 | var artboards = $("%artboards%"); 29 | var artboardWidth = 560; 30 | var artboardHeight = 560; 31 | var file = 0; 32 | var row = 0; 33 | 34 | //artboards.layers.reverse(); 35 | 36 | for(j=0; j<(artboards.length); j++) { 37 | if(file>itemsPerFile-1) { row +=1; file=0; } 38 | artboards.layers[j].frame().x = (artboardWidth + offset) * file; 39 | artboards.layers[j].frame().y = (artboardHeight + offset) * row; 40 | file +=1; 41 | } 42 | } 43 | 44 | 45 | var CURRENTPAGE = context.document.currentPage(); 46 | var CURRENTARTBOARD = context.document.currentPage().currentArtboard(); 47 | 48 | $("%shapes%").each(function(){ 49 | 50 | var artboard = $(CURRENTPAGE).createArtboard("newArtboard", 0, 0, 560, 560); // <- sQuery 51 | artboard.addLayers([this]); 52 | CURRENTARTBOARD.removeLayer(this); 53 | this.frame().x = (560-this.frame().width())/2; 54 | this.frame().y = (560-this.frame().height())/2; 55 | 56 | }) 57 | 58 | CURRENTPAGE.removeLayer(CURRENTARTBOARD); 59 | reorderArtboards(10, 300); 60 | iconNameRename("rIcon_", false); 61 | 62 | } 63 | -------------------------------------------------------------------------------- /_versions/zenb.sketchplugin/Contents/Sketch/Layers/Remove-Empty-Groups.cocoascript: -------------------------------------------------------------------------------- 1 | // Remove empty groups 2 | // @shortcut cmd e 3 | var onRun = function(context) { 4 | const selectedEmptyGroups = () => { 5 | const DOC = context.document; 6 | const PAGE = DOC.currentPage(); 7 | const ARTBOARD = PAGE.currentArtboard(); 8 | const allLayers = ARTBOARD.children().slice(); 9 | const emptyGroups = allLayers.filter(layer => { 10 | try { 11 | return layer.layers().length === 0; 12 | } catch(e) { } 13 | }); 14 | ARTBOARD.deselectAllLayers(); 15 | emptyGroups.map(layer => layer.setIsSelected(true)); 16 | } 17 | selectedEmptyGroups(); 18 | } 19 | -------------------------------------------------------------------------------- /_versions/zenb.sketchplugin/Contents/Sketch/Layers/Resize-Group-Area.cocoascript: -------------------------------------------------------------------------------- 1 | // Fit group bounding box size 2 | 3 | var onRun = function(context) { 4 | @import '../sQuery/sQuery.js'; 5 | $("%groups%").each(function(){ 6 | $(this).MSLayer().resizeRoot(1); 7 | }); 8 | } 9 | -------------------------------------------------------------------------------- /_versions/zenb.sketchplugin/Contents/Sketch/Layers/Send-To-Artboard.cocoascript: -------------------------------------------------------------------------------- 1 | var onRun = function(context) { 2 | @import '../sQuery/sQuery.js'; 3 | @import '../sQuery/plugins/core.parentRoot.js'; 4 | 5 | $("%selected%").parentRoot(); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /_versions/zenb.sketchplugin/Contents/Sketch/Layers/Snap-Layer-To-Eight.cocoascript: -------------------------------------------------------------------------------- 1 | // Round Int near to 8 mult 2 | // Ej: 15 -> 16 or 25 -> 24 3 | function roundIntToGrid(v, n) { 4 | return Math.round((v/n))*n 5 | } 6 | 7 | // Set layer position (x,y) to eight pixel grid 8 | function snapToEight(layer) { 9 | pixelGrid = 8 10 | newX = roundIntToGrid([[layer frame] x], pixelGrid) 11 | newY = roundIntToGrid([[layer frame] y], pixelGrid) 12 | [[layer frame] setX:newX] 13 | [[layer frame] setY:newY] 14 | } 15 | 16 | // Do it! 17 | snapToEight(context.selection[0]) 18 | -------------------------------------------------------------------------------- /_versions/zenb.sketchplugin/Contents/Sketch/Layers/Toggle-Click-Throught.cocoascript: -------------------------------------------------------------------------------- 1 | // Toggle click throught 2 | // @shortcut cmd shift d 3 | 4 | var onRun = function(context) { 5 | @import '../sQuery/sQuery.js'; 6 | $('%groups%').toggleClickThrought(); 7 | } 8 | 9 | -------------------------------------------------------------------------------- /_versions/zenb.sketchplugin/Contents/Sketch/Layers/Toggle-User-Guides.cocoascript: -------------------------------------------------------------------------------- 1 | // Tootle User Guides Layer 2 | // @shortcut cmd l 3 | 4 | var onRun = function(context) { 5 | // Import sQuery 6 | @import '../sQuery/sQuery.js'; 7 | 8 | // Toggle Guides layer visibility 9 | var guidesGroup = $('*').withName('Guides').MSLayer(); 10 | guidesGroup.setIsVisible(!guidesGroup.isVisible()); 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /_versions/zenb.sketchplugin/Contents/Sketch/Layers/Transform-Each.cocoascript: -------------------------------------------------------------------------------- 1 | // Transform Each 2 | 3 | var onRun = function(context) { 4 | 5 | @import '../sQuery/sQuery.js'; 6 | 7 | var size = context.document.askForUserInput_initialValue("Enter Size Multiplier", "100%"); 8 | var re = /\d*\.*\d/; 9 | var pValue = (parseFloat(re.exec(size)[0]))/100; 10 | 11 | var x, y, w, h, frame; 12 | $('%selected%').each(function(){ 13 | 14 | // Transform data 15 | frame = this.frame(); 16 | x = frame.x(); 17 | y = frame.y(); 18 | w = frame.width(); 19 | h = frame.height(); 20 | 21 | // Moving and scaling 22 | frame.width = w*pValue; 23 | frame.height = h*pValue; 24 | frame.x = x+(w-(w*(pValue)))/2 25 | frame.y = y+(h-(h*(pValue)))/2 26 | 27 | }); 28 | 29 | }; 30 | 31 | -------------------------------------------------------------------------------- /_versions/zenb.sketchplugin/Contents/Sketch/Preview/Preview-Artboard-Desktop.cocoascript: -------------------------------------------------------------------------------- 1 | var onRun = function(context){ 2 | 3 | function writeFile(filename, the_string) { 4 | var path =[@"" stringByAppendingString: filename]; 5 | var str = [@"" stringByAppendingString: the_string]; 6 | str.dataUsingEncoding_(NSUTF8StringEncoding).writeToFile_atomically_(path, true) 7 | } 8 | 9 | //var documentPath = "/" //[[doc fileURL] path].split([doc displayName] + ".sketch")[0]; 10 | 11 | // GLOBAL 12 | var doc = context.document; 13 | var previzFolder = "/.previz"; 14 | var path = NSHomeDirectory() + previzFolder; 15 | var artboardHeight = doc.currentPage().currentArtboard().frame().height(); 16 | var artboardWidth = doc.currentPage().currentArtboard().frame().width(); 17 | 18 | // PNG 19 | var currentArtboard = doc.currentPage().currentArtboard() 20 | var currentArtboardName = currentArtboard.name() 21 | var exportImageFilePath = path + "/" + currentArtboardName + ".png" 22 | [doc saveArtboardOrSlice:currentArtboard toFile:exportImageFilePath] 23 | 24 | // HTML 25 | var content = '\ 26 | \ 32 | \ 33 | \ 34 | \ 35 | \ 36 | Zen Previz\ 37 | \ 50 | \ 51 | \ 52 |
\ 53 | \ 54 | '; 55 | 56 | writeFile(path + "/previz.html", content); 57 | 58 | [doc showMessage: "Desktop Previz create" + path]; 59 | 60 | } 61 | -------------------------------------------------------------------------------- /_versions/zenb.sketchplugin/Contents/Sketch/Preview/Preview-Artboard-Mobile.cocoascript: -------------------------------------------------------------------------------- 1 | var onRun = function(context){ 2 | 3 | function writeFile(filename, the_string) { 4 | var path =[@"" stringByAppendingString: filename]; 5 | var str = [@"" stringByAppendingString: the_string]; 6 | str.dataUsingEncoding_(NSUTF8StringEncoding).writeToFile_atomically_(path, true) 7 | } 8 | 9 | //var documentPath = "/" //[[doc fileURL] path].split([doc displayName] + ".sketch")[0]; 10 | 11 | // GLOBAL 12 | var doc = context.document; 13 | var previzFolder = "/.previz"; 14 | var path = NSHomeDirectory() + previzFolder; 15 | var artboardHeight = doc.currentPage().currentArtboard().frame().height(); 16 | var artboardWidth = doc.currentPage().currentArtboard().frame().width(); 17 | 18 | // PNG 19 | var currentArtboard = doc.currentPage().currentArtboard() 20 | var currentArtboardName = currentArtboard.name() 21 | var exportImageFilePath = path + "/" + currentArtboardName + ".png" 22 | [doc saveArtboardOrSlice:currentArtboard toFile:exportImageFilePath] 23 | 24 | // HTML 25 | var content = '\ 26 | \ 27 | \ 28 | Presentacion\ 29 | \ 39 | \ 40 | \ 41 |
\ 42 | \ 43 |
\ 44 | \ 45 | ' 46 | 47 | writeFile(path + "/previz.html", content); 48 | [doc showMessage: "Mobile Previz create" + path]; 49 | 50 | } 51 | -------------------------------------------------------------------------------- /_versions/zenb.sketchplugin/Contents/Sketch/Typography/ModularScale.cocoascript: -------------------------------------------------------------------------------- 1 | // Modular Scale 2 | 3 | var onRun = function(context) { 4 | 5 | @import '../sQuery/sQuery.js'; 6 | 7 | 8 | 9 | }; 10 | 11 | -------------------------------------------------------------------------------- /_versions/zenb.sketchplugin/Contents/Sketch/_Wip/_Filter Shapes.cocoascript: -------------------------------------------------------------------------------- 1 | // Filter selection with shape layers 2 | // Set keyboard preferences (F2) 3 | 4 | @import "common.js" 5 | 6 | filterSelectionWithShapeLayers() 7 | -------------------------------------------------------------------------------- /_versions/zenb.sketchplugin/Contents/Sketch/_Wip/_Filter Texts.cocoascript: -------------------------------------------------------------------------------- 1 | // Filter selection with text layers 2 | // Set keyboard preferences (F1) 3 | 4 | @import "common.js" 5 | 6 | filterSelectionWithTextLayers() 7 | -------------------------------------------------------------------------------- /_versions/zenb.sketchplugin/Contents/Sketch/_Wip/_Fit Artobard Width.cocoascript: -------------------------------------------------------------------------------- 1 | // (alt cmd +) 2 | var layers = selection 3 | 4 | for (var i=0; i<[selection count]; ++i) { 5 | 6 | var layer = selection[i] 7 | 8 | if ([layer class] != "MSTextLayer" && [layer class] != "MSLayerGroup") { 9 | var frame = layer.frame() 10 | var parent = layer.parentGroup() 11 | 12 | frame.setX(0) 13 | frame.setWidth(parent.frame().width()) 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /_versions/zenb.sketchplugin/Contents/Sketch/_Wip/_Fit Group Size.cocoascript: -------------------------------------------------------------------------------- 1 | // Resizes the selected groups to fit around all of its sub-layers 2 | 3 | var sel = context.selection 4 | var len = sel.length(); 5 | var i; 6 | 7 | for(i=0;i 1) { 495 | _layers.push(this.layers[i]); 496 | } 497 | } 498 | 499 | this.layers = _layers.slice(); 500 | 501 | return this; 502 | }, 503 | 504 | /** 505 | * ... 506 | * @return {sQuery} 507 | */ 508 | 509 | UISelect: function(){ 510 | // Primero deseleccionamos todo 511 | doc.currentPage().deselectAllLayers(); 512 | for(var i=0, len=this.layers.length; i= width ? n : new Array(width - n.length + 1).join(z) + n; 9 | } 10 | 11 | function iconNameRename(glyphName, withIconName) { 12 | withIconName = withIconName || false; 13 | var artboards = $("%artboards%"); 14 | var iconName; 15 | //artboards.layers.reverse(); 16 | artboards.each(function(idx) { 17 | if (withIconName) { 18 | fullIconName = glyphName + this.layers().array()[0].name(); 19 | } else { 20 | fullIconName = glyphName + pad(idx, 3) + iconName; 21 | } 22 | this.name = fullIconName; 23 | log(this.name) 24 | }); 25 | } 26 | 27 | 28 | function reorderArtboards(itemsPerFile, offset) { 29 | var artboards = $("%artboards%"); 30 | var artboardWidth = 560; 31 | var artboardHeight = 560; 32 | var file = 0; 33 | var row = 0; 34 | 35 | //artboards.layers.reverse(); 36 | 37 | for(j=0; j<(artboards.length); j++) { 38 | if(file>itemsPerFile-1) { row +=1; file=0; } 39 | artboards.layers[j].frame().x = (artboardWidth + offset) * file; 40 | artboards.layers[j].frame().y = (artboardHeight + offset) * row; 41 | file +=1; 42 | } 43 | } 44 | 45 | 46 | var CURRENTPAGE = context.document.currentPage(); 47 | var CURRENTARTBOARD = context.document.currentPage().currentArtboard(); 48 | 49 | $("%shapes%").each(function(){ 50 | 51 | var artboard = $(CURRENTPAGE).createArtboard("newArtboard", 0, 0, 560, 560); // <- sQuery 52 | artboard.addLayers([this]); 53 | CURRENTARTBOARD.removeLayer(this); 54 | this.frame().x = (560-this.frame().width())/2; 55 | this.frame().y = (560-this.frame().height())/2; 56 | 57 | }) 58 | 59 | CURRENTPAGE.removeLayer(CURRENTARTBOARD); 60 | reorderArtboards(10, 300); 61 | iconNameRename("", true); 62 | 63 | } 64 | -------------------------------------------------------------------------------- /_versions/zenc.sketchplugin/Contents/Sketch/Layers/Remove-Empty-Groups.cocoascript: -------------------------------------------------------------------------------- 1 | // Remove empty groups 2 | // @shortcut cmd e 3 | var onRun = function(context) { 4 | const selectedEmptyGroups = () => { 5 | const DOC = context.document; 6 | const PAGE = DOC.currentPage(); 7 | const ARTBOARD = PAGE.currentArtboard(); 8 | const allLayers = ARTBOARD.children().slice(); 9 | const emptyGroups = allLayers.filter(layer => { 10 | try { 11 | return layer.layers().length === 0; 12 | } catch(e) { } 13 | }); 14 | ARTBOARD.deselectAllLayers(); 15 | emptyGroups.map(layer => layer.setIsSelected(true)); 16 | }; 17 | selectedEmptyGroups(); 18 | }; 19 | -------------------------------------------------------------------------------- /_versions/zenc.sketchplugin/Contents/Sketch/Layers/Resize-Group-Area.cocoascript: -------------------------------------------------------------------------------- 1 | // Fit group bounding box size 2 | 3 | var onRun = function(context) { 4 | @import '../sQuery/sQuery.js'; 5 | $("%groups%").each(function(){ 6 | $(this).MSLayer().resizeRoot(1); 7 | }); 8 | } 9 | -------------------------------------------------------------------------------- /_versions/zenc.sketchplugin/Contents/Sketch/Layers/Send-To-Artboard.cocoascript: -------------------------------------------------------------------------------- 1 | var onRun = function(context) { 2 | const getArtboardFromLayer = layer => { 3 | let parent = layer.parentGroup() 4 | if(parent.class() == 'MSArtboardGroup') { 5 | artboard = parent 6 | } else { 7 | getArtboardFromLayer(parent) 8 | } 9 | return artboard 10 | } 11 | 12 | // Put a layer in Artboard root 13 | function parentToRoot(layer) { 14 | if (layer.parentGroup().class() != 'MSArtboardGroup') { 15 | // Get the artboard 16 | const artboard = getArtboardFromLayer(layer); 17 | const parent = layer.parentGroup(); 18 | // Get layer absolute position 19 | const x = layer.absoluteRect().x() - artboard.absoluteRect().x(); 20 | const y = layer.absoluteRect().y() - artboard.absoluteRect().y(); 21 | // Parent to it 22 | artboard.addLayers([layer]); 23 | // Remove from previous parent 24 | parent.removeLayer(layer); 25 | // Position new layer 26 | layer.frame().setX(x); 27 | layer.frame().setY(y); 28 | } 29 | } 30 | 31 | context.selection.slice().map(layer => parentToRoot(layer)) 32 | 33 | } 34 | -------------------------------------------------------------------------------- /_versions/zenc.sketchplugin/Contents/Sketch/Layers/Snap-Layer-To-Eight.cocoascript: -------------------------------------------------------------------------------- 1 | // Round Int near to 8 mult 2 | // Ej: 15 -> 16 or 25 -> 24 3 | function roundIntToGrid(v, n) { 4 | return Math.round((v/n))*n 5 | } 6 | 7 | // Set layer position (x,y) to eight pixel grid 8 | function snapToEight(layer) { 9 | pixelGrid = 8 10 | newX = roundIntToGrid([[layer frame] x], pixelGrid) 11 | newY = roundIntToGrid([[layer frame] y], pixelGrid) 12 | [[layer frame] setX:newX] 13 | [[layer frame] setY:newY] 14 | } 15 | 16 | // Do it! 17 | snapToEight(context.selection[0]) 18 | -------------------------------------------------------------------------------- /_versions/zenc.sketchplugin/Contents/Sketch/Layers/Toggle-Click-Throught.cocoascript: -------------------------------------------------------------------------------- 1 | // Toggle click throught 2 | // @shortcut cmd shift d 3 | 4 | var onRun = function(context) { 5 | @import '../sQuery/sQuery.js'; 6 | $('%groups%').toggleClickThrought(); 7 | } 8 | 9 | -------------------------------------------------------------------------------- /_versions/zenc.sketchplugin/Contents/Sketch/Layers/Toggle-User-Guides.cocoascript: -------------------------------------------------------------------------------- 1 | // Tootle User Guides Layer 2 | // @shortcut cmd l 3 | 4 | var onRun = function(context) { 5 | // Import sQuery 6 | @import '../sQuery/sQuery.js'; 7 | 8 | // Toggle Guides layer visibility 9 | var guidesGroup = $('*').withName('Guides').MSLayer(); 10 | guidesGroup.setIsVisible(!guidesGroup.isVisible()); 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /_versions/zenc.sketchplugin/Contents/Sketch/Layers/Transform-Each.cocoascript: -------------------------------------------------------------------------------- 1 | // Transform Each 2 | 3 | var onRun = function(context) { 4 | 5 | @import '../sQuery/sQuery.js'; 6 | 7 | var size = context.document.askForUserInput_initialValue("Enter Size Multiplier", "100%"); 8 | var re = /\d*\.*\d/; 9 | var pValue = (parseFloat(re.exec(size)[0]))/100; 10 | 11 | var x, y, w, h, frame; 12 | $('%selected%').each(function(){ 13 | 14 | // Transform data 15 | frame = this.frame(); 16 | x = frame.x(); 17 | y = frame.y(); 18 | w = frame.width(); 19 | h = frame.height(); 20 | 21 | // Moving and scaling 22 | frame.width = w*pValue; 23 | frame.height = h*pValue; 24 | frame.x = x+(w-(w*(pValue)))/2 25 | frame.y = y+(h-(h*(pValue)))/2 26 | 27 | }); 28 | 29 | }; 30 | 31 | -------------------------------------------------------------------------------- /_versions/zenc.sketchplugin/Contents/Sketch/Layers/UnlockAllLayers.cocoascript: -------------------------------------------------------------------------------- 1 | var onRun = function(context) { 2 | @import '../sQuery/sQuery.js'; 3 | $("*").unlock(); 4 | } 5 | -------------------------------------------------------------------------------- /_versions/zenc.sketchplugin/Contents/Sketch/Preview/Preview-Artboard-Desktop.cocoascript: -------------------------------------------------------------------------------- 1 | var onRun = function(context){ 2 | 3 | function writeFile(filename, the_string) { 4 | var path =[@"" stringByAppendingString: filename]; 5 | var str = [@"" stringByAppendingString: the_string]; 6 | str.dataUsingEncoding_(NSUTF8StringEncoding).writeToFile_atomically_(path, true) 7 | } 8 | 9 | //var documentPath = "/" //[[doc fileURL] path].split([doc displayName] + ".sketch")[0]; 10 | 11 | // GLOBAL 12 | var doc = context.document; 13 | var previzFolder = "/.previz"; 14 | var path = NSHomeDirectory() + previzFolder; 15 | var artboardHeight = doc.currentPage().currentArtboard().frame().height(); 16 | var artboardWidth = doc.currentPage().currentArtboard().frame().width(); 17 | 18 | // PNG 19 | var currentArtboard = doc.currentPage().currentArtboard() 20 | var currentArtboardName = currentArtboard.name() 21 | var exportImageFilePath = path + "/" + currentArtboardName + ".png" 22 | [doc saveArtboardOrSlice:currentArtboard toFile:exportImageFilePath] 23 | 24 | // HTML 25 | var content = '\ 26 | \ 32 | \ 33 | \ 34 | \ 35 | \ 36 | Zen Previz\ 37 | \ 50 | \ 51 | \ 52 |
\ 53 | \ 54 | '; 55 | 56 | writeFile(path + "/previz.html", content); 57 | 58 | [doc showMessage: "Desktop Previz create" + path]; 59 | 60 | } 61 | -------------------------------------------------------------------------------- /_versions/zenc.sketchplugin/Contents/Sketch/Preview/Preview-Artboard-Mobile.cocoascript: -------------------------------------------------------------------------------- 1 | var onRun = function(context){ 2 | 3 | function writeFile(filename, the_string) { 4 | var path =[@"" stringByAppendingString: filename]; 5 | var str = [@"" stringByAppendingString: the_string]; 6 | str.dataUsingEncoding_(NSUTF8StringEncoding).writeToFile_atomically_(path, true) 7 | } 8 | 9 | //var documentPath = "/" //[[doc fileURL] path].split([doc displayName] + ".sketch")[0]; 10 | 11 | // GLOBAL 12 | var doc = context.document; 13 | var previzFolder = "/.previz"; 14 | var path = NSHomeDirectory() + previzFolder; 15 | var artboardHeight = doc.currentPage().currentArtboard().frame().height(); 16 | var artboardWidth = doc.currentPage().currentArtboard().frame().width(); 17 | 18 | // PNG 19 | var currentArtboard = doc.currentPage().currentArtboard() 20 | var currentArtboardName = currentArtboard.name() 21 | var exportImageFilePath = path + "/" + currentArtboardName + ".png" 22 | [doc saveArtboardOrSlice:currentArtboard toFile:exportImageFilePath] 23 | 24 | // HTML 25 | var content = '\ 26 | \ 27 | \ 28 | Presentacion\ 29 | \ 39 | \ 40 | \ 41 |
\ 42 | \ 43 |
\ 44 | \ 45 | ' 46 | 47 | writeFile(path + "/previz.html", content); 48 | [doc showMessage: "Mobile Previz create" + path]; 49 | 50 | } 51 | -------------------------------------------------------------------------------- /_versions/zenc.sketchplugin/Contents/Sketch/Typography/ModularScale.cocoascript: -------------------------------------------------------------------------------- 1 | // Modular Scale 2 | 3 | var onRun = function(context) { 4 | 5 | @import '../sQuery/sQuery.js'; 6 | 7 | 8 | 9 | }; 10 | 11 | -------------------------------------------------------------------------------- /_versions/zenc.sketchplugin/Contents/Sketch/_Wip/_Filter Shapes.cocoascript: -------------------------------------------------------------------------------- 1 | // Filter selection with shape layers 2 | // Set keyboard preferences (F2) 3 | 4 | @import "common.js" 5 | 6 | filterSelectionWithShapeLayers() 7 | -------------------------------------------------------------------------------- /_versions/zenc.sketchplugin/Contents/Sketch/_Wip/_Filter Texts.cocoascript: -------------------------------------------------------------------------------- 1 | // Filter selection with text layers 2 | // Set keyboard preferences (F1) 3 | 4 | @import "common.js" 5 | 6 | filterSelectionWithTextLayers() 7 | -------------------------------------------------------------------------------- /_versions/zenc.sketchplugin/Contents/Sketch/_Wip/_Fit Artobard Width.cocoascript: -------------------------------------------------------------------------------- 1 | // (alt cmd +) 2 | var layers = selection 3 | 4 | for (var i=0; i<[selection count]; ++i) { 5 | 6 | var layer = selection[i] 7 | 8 | if ([layer class] != "MSTextLayer" && [layer class] != "MSLayerGroup") { 9 | var frame = layer.frame() 10 | var parent = layer.parentGroup() 11 | 12 | frame.setX(0) 13 | frame.setWidth(parent.frame().width()) 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /_versions/zenc.sketchplugin/Contents/Sketch/_Wip/_Fit Group Size.cocoascript: -------------------------------------------------------------------------------- 1 | // Resizes the selected groups to fit around all of its sub-layers 2 | 3 | var sel = context.selection 4 | var len = sel.length(); 5 | var i; 6 | 7 | for(i=0;i layer.class() == MSLayerGroup); 16 | return this; 17 | } 18 | 19 | }(sQuery)); 20 | -------------------------------------------------------------------------------- /_versions/zenc.sketchplugin/Contents/Sketch/sQuery/plugins/sQuery.areShapes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * sQuery plugin - areShapes 1.0 3 | * Francis Vega 4 | * 5 | * @desc Filter elements and return just ShapeGroups 6 | * @return {sQuery} 7 | * @example 8 | * $('%selected%').areShapes().UISelect(); 9 | * @desc Filter the selection (in Sketchapp UI) and make a selection only 10 | * with shape layers. 11 | */ 12 | 13 | (function($){ 14 | 15 | $.fn.areShapes = function() { 16 | this.layers = this.layers.slice().filter(layer => layer.class() == MSShapeGroup); 17 | return this; 18 | } 19 | 20 | }(sQuery)); 21 | -------------------------------------------------------------------------------- /_versions/zenc.sketchplugin/Contents/Sketch/sQuery/plugins/sQuery.move.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * sQuery plugin - move 1.0 4 | * Francis Vega 5 | * 6 | * @desc Move query layers 7 | * @param {object} coords 8 | * @param {object} coords.x Position x value 9 | * @param {object} coords.y Position y value 10 | * @return {sQuery} 11 | * @example 12 | * $('%selected%').move({"x":100, "y":234}) 13 | * @desc Move "selected" layers by x100, y234 14 | */ 15 | 16 | (function($){ 17 | $.fn.move = function(coords, pixelFit = false) { 18 | 19 | let finalX, finalY; 20 | 21 | this.each(function() { 22 | 23 | if(pixelFit) { 24 | finalX = Math.round( this.absoluteRect().x() - this.parentRootForAbsoluteRect().rect().origin.x + coords.x ); 25 | finalY = Math.round( this.absoluteRect().y() - this.parentRootForAbsoluteRect().rect().origin.y + coords.y ); 26 | } else { 27 | finalX = this.absoluteRect().x() - this.parentRootForAbsoluteRect().rect().origin.x + coords.x; 28 | finalY = this.absoluteRect().y() - this.parentRootForAbsoluteRect().rect().origin.y + coords.y; 29 | } 30 | 31 | this.absoluteRect().x = finalX + this.parentRootForAbsoluteRect().rect().origin.x 32 | this.absoluteRect().y = finalY + this.parentRootForAbsoluteRect().rect().origin.y 33 | 34 | }); 35 | 36 | return this; 37 | 38 | } 39 | }(sQuery)); 40 | -------------------------------------------------------------------------------- /_versions/zenc.sketchplugin/Contents/Sketch/sQuery/plugins/sQuery.sendToRoot.js: -------------------------------------------------------------------------------- 1 | /** 2 | * sQuery plugin - sendToRoot 1.0 3 | * Francis Vega 4 | * 5 | * @desc Send layers "back" to Artboard 6 | * @return {sQuery} 7 | * @example 8 | * $('%selected%').sendToRoot(); 9 | */ 10 | 11 | (function($) { 12 | $.fn.sendToRoot = function() { 13 | // For each query 14 | this.each(function() { 15 | const parent = this.parentGroup(); 16 | if (parent.class() != MSArtboardGroup) { 17 | // Get the artboard 18 | const artboard = this.parentRootForAbsoluteRect(); 19 | // Get this absolute position 20 | const x = this.absoluteRect().x() - artboard.absoluteRect().x(); 21 | const y = this.absoluteRect().y() - artboard.absoluteRect().y(); 22 | // Parent to it 23 | artboard.addLayers([this]); 24 | // Remove from previous parent 25 | parent.removeLayer(this); 26 | // Position new this 27 | this.frame().setX(x); 28 | this.frame().setY(y); 29 | } 30 | }); 31 | return this; 32 | } 33 | }(sQuery)); 34 | -------------------------------------------------------------------------------- /_versions/zenc.sketchplugin/Contents/Sketch/sQuery/sQuery.js: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | Copyright (c) 2017 Francis Vega 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 5 | associated documentation files (the "Software"), to deal in the Software without restriction, 6 | including without limitation the rights to use, copy, modify, merge, publish, distribute, 7 | sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this 11 | permission notice shall be included in all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 14 | NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 15 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 16 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 18 | */ 19 | 20 | /* 21 | * 22 | * sQuery 0.1 23 | * 24 | */ 25 | 26 | let sQuery, $; 27 | 28 | (function(){ 29 | 30 | sQuery = $ = (selector, page, artboard) => new SQUERY(selector, page, artboard) 31 | 32 | /** 33 | * findObjectsByName 34 | * @param {string} name The name of the object (layer) 35 | * @param {scope} scope The scope (layers) of search 36 | * @return {MSArray} 37 | */ 38 | const findObjectsByName = (name, scope) => { 39 | const predicate = NSPredicate.predicateWithFormat("name == %@",name) 40 | return scope.filteredArrayUsingPredicate(predicate) 41 | } 42 | 43 | /** 44 | * findObjectsOfType 45 | * @param {string|classType} classType The name of the class type 46 | * @param {scope} scope The scope (layers) of search 47 | * @return {MSArray} 48 | */ 49 | const findObjectsOfType = (classType, scope) => { 50 | const predicate = NSPredicate.predicateWithFormat("self isKindOfClass: %@", classType) 51 | return scope.filteredArrayUsingPredicate(predicate) 52 | } 53 | 54 | /** 55 | * flattenArray 56 | * @param {array} arr The array to flatten 57 | * @return {array} return a one level deep array 58 | */ 59 | const flattenArray = arr => arr.reduce( 60 | (flat, toFlatten) => flat.concat( 61 | Array.isArray(toFlatten) ? flattenArray(toFlatten) : toFlatten), []) 62 | 63 | const SQUERY = function(selector, page, artboard) { 64 | 65 | if (typeof selector == "string") { 66 | switch(selector) { 67 | // All 68 | case "*": 69 | this.layers = context.document.currentPage().currentArtboard().children().slice().filter(layer => layer.class() != "MSArtboardGroup" && layer.class() != "MSRectangleShape") 70 | break 71 | 72 | case "%hierarchy%": 73 | this.layers = context.document.currentPage().currentArtboard().layers() 74 | break 75 | 76 | case "%pages%": 77 | this.layers = context.document.pages() 78 | break 79 | 80 | case "%artboards%": 81 | this.layers = context.document.currentPage().artboards() 82 | break 83 | 84 | case "%images%": 85 | this.layers = findObjectsOfType(MSBitmapLayer, context.document.currentPage().currentArtboard().children()) 86 | break 87 | 88 | case "%layers%": 89 | this.layers = context.document.currentPage().currentArtboard().children().slice().filter(layer => layer.class() != "MSArtboardGroup" && layer.class() != "MSRectangleShape" && layer.class() != "MSLayerGroup") 90 | break 91 | 92 | case "%shapes%": 93 | this.layers = findObjectsOfType(MSShapeGroup, context.document.currentPage().currentArtboard().children()) 94 | break 95 | 96 | case "%groups%": 97 | this.layers = findObjectsOfType(MSLayerGroup, context.document.currentPage().currentArtboard().children()) 98 | break 99 | 100 | case "%textLayers%": 101 | this.layers = findObjectsOfType(MSTextLayer, context.document.currentPage().currentArtboard().children()) 102 | break 103 | 104 | case "%selected%": 105 | this.layers = context.selection 106 | break 107 | 108 | // Default: Layer name. 109 | default: 110 | this.layers = findObjectsByName(selector, context.document.currentPage().currentArtboard().children()) 111 | break 112 | } 113 | } 114 | 115 | if (typeof selector == "object") { 116 | this.layers = [selector] 117 | } 118 | 119 | return this 120 | } 121 | 122 | /* @sQuery API */ 123 | 124 | sQuery.fn = SQUERY.prototype = { 125 | 126 | /** 127 | * Return an array with the queried layers 128 | * @return {array} 129 | */ 130 | sLayers: function() { 131 | return this.layers 132 | }, 133 | 134 | /** 135 | * Query text layers 136 | * @return {sQuery} 137 | */ 138 | texts: function() { 139 | this.layers = this.layers.slice().filter(layer => layer.class() == MSTextLayer) 140 | return this 141 | }, 142 | 143 | /** 144 | * Query groups 145 | * @return {sQuery} 146 | */ 147 | groups: function() { 148 | this.layers = this.layers.slice().filter(layer => layer.class() == MSLayerGroup) 149 | return this 150 | }, 151 | 152 | /** 153 | * Query pages 154 | * @return {sQuery} 155 | */ 156 | pages: function() { 157 | this.layers = this.layers.slice().filter(layer => layer.class() == MSPage) 158 | return this 159 | }, 160 | 161 | /** 162 | * Query artboards 163 | * @return {sQuery} 164 | */ 165 | artboards: function() { 166 | this.layers = this.layers.slice().filter(layer => layer.class() == MSArtboardGroup) 167 | return this 168 | }, 169 | 170 | /** 171 | * Query shape layers 172 | * @return {sQuery} 173 | */ 174 | shapes: function() { 175 | this.layers = this.layers.slice().filter(layer => layer.class() == MSShapeGroup) 176 | return this 177 | }, 178 | 179 | /** 180 | * images 181 | * @return {sQuery} 182 | */ 183 | images: function() { 184 | this.layers = this.layers.slice().filter(layer => layer.class() == MSBitmapLayer) 185 | return this 186 | }, 187 | 188 | /** 189 | * isLocked 190 | * @return {sQuery} 191 | */ 192 | isLocked: function() { 193 | this.layers = this.layers.slice().filter(layer => layer.isLocked()) 194 | return this 195 | }, 196 | 197 | /** 198 | * startsWith 199 | * @param {string} str 200 | * @return {sQuery} 201 | */ 202 | startsWith: function(str) { 203 | this.layers = this.layers.slice().filter(layer => layer.name().substr(0, str.length) == str) 204 | return this 205 | }, 206 | 207 | /** 208 | * endsWith 209 | * @param {string} str 210 | * @return {sQuery} 211 | */ 212 | endsWith: function(str) { 213 | this.layers = this.layers.slice().filter(layer => layer.name().substr(layer.name().length() - str.length) == str) 214 | return this 215 | }, 216 | 217 | /** 218 | * contains 219 | * @param {string} str 220 | * @return {sQuery} 221 | */ 222 | contains: function(str) { 223 | this.layers = this.layers.slice().filter(layer => layer.name().indexOf(str) != -1) 224 | return this 225 | }, 226 | 227 | /** 228 | * withName 229 | * @param {string} name 230 | * @return {sQuery} 231 | */ 232 | withName: function(name) { 233 | this.layers = this.layers.slice().filter(layer => layer.name() == name) 234 | return this 235 | }, 236 | 237 | /** 238 | * childs 239 | * @return {sQuery} 240 | */ 241 | childs: function() { 242 | this.layers = flattenArray( 243 | this.layers.slice().map(function(layer) { 244 | if (layer.class() == MSSymbolInstance) { 245 | const symbolMasterId = layer.symbolMaster().objectID() 246 | const symbolChilds = layer.symbolMaster().children().slice() 247 | const symbolChildsWithoutSymbolItself = symbolChilds.filter(symbolChildsLayer => symbolChildsLayer.objectID() != symbolMasterId) 248 | return symbolChildsWithoutSymbolItself 249 | } else { 250 | return layer.children().slice() 251 | } 252 | }) 253 | ) 254 | return this 255 | }, 256 | 257 | /** 258 | * hasClickThrought 259 | * @return {sQuery} 260 | */ 261 | hasClickThrought: function() { 262 | const groups = this.layers.slice().filter(layer => layer.class() == MSLayerGroup) 263 | this.layers = groups.filter(layer => layer.hasClickThrough()) 264 | return this 265 | }, 266 | 267 | /** 268 | * setHasClickThrough 269 | * @return {sQuery} 270 | */ 271 | setHasClickThrough: function(status = false) { 272 | const groups = this.layers.slice().filter(layer => layer.class() == MSLayerGroup) 273 | groups.map(layer => layer.setHasClickThrough(status)) 274 | return this 275 | }, 276 | 277 | /** 278 | * toggleClickThrought 279 | * @return {sQuery} 280 | */ 281 | toggleClickThrought: function() { 282 | const groups = this.layers.slice().filter(layer => layer.class() == MSLayerGroup) 283 | groups.map(layer => layer.setHasClickThrough(!layer.hasClickThrough())) 284 | return this 285 | }, 286 | 287 | /** 288 | * isEmpty 289 | * @return {sQuery} 290 | */ 291 | isEmpty: function() { 292 | const groups = this.layers.slice().filter(layer => layer.class() == MSLayerGroup) 293 | this.layers = groups.filter(layer => layer.layers().length === 0) 294 | return this 295 | }, 296 | 297 | /** 298 | * isVisible 299 | * @return {sQuery} 300 | */ 301 | isVisible: function() { 302 | this.layers = this.layers.slice().filter(layer => layer.isVisible() == 1) 303 | return this 304 | }, 305 | 306 | /** 307 | * isHidden 308 | * @return {sQuery} 309 | */ 310 | isHidden: function() { 311 | this.layers = this.layers.slice().filter(layer => layer.isVisible() == 0) 312 | return this 313 | }, 314 | 315 | /** 316 | * visibility 317 | * @param {bool} status 318 | * @return {sQuery} 319 | */ 320 | visibility: function(status) { 321 | this.layers.slice().map(layer => layer.setIsVisible(status)) 322 | return this 323 | }, 324 | 325 | /** 326 | * show 327 | * @return {sQuery} 328 | */ 329 | show: function() { 330 | this.layers.slice().map(layer => layer.setIsVisible(true)) 331 | return this 332 | }, 333 | 334 | /** 335 | * hide 336 | * @return {sQuery} 337 | */ 338 | hide: function() { 339 | this.layers.slice().map(layer => layer.setIsVisible(false)) 340 | return this 341 | }, 342 | 343 | /** 344 | * lock 345 | * @return {sQuery} 346 | */ 347 | lock: function() { 348 | this.layers.slice().map(layer => layer.setIsLocked(true)) 349 | return this 350 | }, 351 | 352 | /** 353 | * unlock 354 | * @return {sQuery} 355 | */ 356 | unlock: function() { 357 | this.layers.slice().map(layer => layer.setIsLocked(false)) 358 | return this 359 | }, 360 | 361 | /** 362 | * duplicate 363 | * @param {string} name 364 | * @return {sQuery} 365 | */ 366 | duplicate: function(name) { 367 | const duplicateLayers = this.layers.slice().map(layer => layer.duplicate()) 368 | duplicateLayers.map(layer => layer.name = name) 369 | this.layers = duplicateLayers 370 | return this 371 | }, 372 | 373 | /** 374 | * remove 375 | * @return {sQuery} 376 | */ 377 | remove: function() { 378 | this.layers.slice().map(layer => layer.removeFromParent()) 379 | }, 380 | 381 | /** 382 | * opacity 383 | * @param {number} val 384 | * @return {sQuery} 385 | */ 386 | opacity: function(val) { 387 | if (val) { 388 | this.layers.slice().map(layer => layer.style().contextSettings().opacity = val / 100) 389 | } else { 390 | return this.layers.slice().map(layer => layer.style().contextSettings().opacity()) 391 | } 392 | }, 393 | 394 | /** 395 | * absolutePosition 396 | * @return {sQuery} 397 | */ 398 | absolutePosition: function() { 399 | return this.layers.slice().map(layer => [layer.absoluteRect().x(), layer.absoluteRect().y()]) 400 | }, 401 | 402 | /** 403 | * relativePosition 404 | * @return {sQuery} 405 | */ 406 | relativePosition: function() { 407 | return this.layers.slice().map(layer => 408 | [ 409 | layer.absoluteRect().x() - layer.parentRootForAbsoluteRect().rect().origin.x, 410 | layer.absoluteRect().y() - layer.parentRootForAbsoluteRect().rect().origin.y 411 | ] 412 | ) 413 | }, 414 | 415 | /** 416 | * rename 417 | * @param {string} name 418 | * @return {sQuery} 419 | */ 420 | rename: function(name) { 421 | this.layers.slice().map(layer => layer.name = name) 422 | return this 423 | }, 424 | 425 | /** 426 | * UISelect 427 | * @return {sQuery} 428 | */ 429 | UISelect: function() { 430 | context.document.currentPage().deselectAllLayers() 431 | this.layers.slice().map(layer => layer.select_byExpandingSelection(true, true)) 432 | return this 433 | }, 434 | 435 | /** 436 | * Itera por cada uno de los elementos previamente seleccionados y devuelve el elemento. 437 | * @param {function} callback Una función a la que each llama por cada iteración. 438 | * @return {sQuery} 439 | */ 440 | each: function(callback) { 441 | for(let i=0, len=this.layers.length; i 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | // 2 | // ZEN Sketch Gulpfile 3 | // 2015 4 | // 5 | 6 | // Gulp itself 7 | var gulp = require('gulp'); 8 | 9 | // Directorios del proyecto 10 | // 11 | var dirs = { 12 | src: '', 13 | dst: '/sketchplugins', 14 | }; 15 | 16 | // Watch 17 | // 18 | gulp.task('watch', function(){ 19 | gulp.watch(dirs.src + 'zen.sketchplugin/**/*', ['copy']); 20 | }); 21 | 22 | 23 | // Copia los plugins en la carpeta de plugins de sketch mediante el softlink 24 | // /sketchplugins 25 | gulp.task('copy', function() { 26 | // Archivos en el raiz 27 | gulp.src(['zen.sketchplugin'], {cwd: dirs.src}) 28 | .pipe(gulp.dest(dirs.dst)); 29 | gulp.src(['zen.sketchplugin/**/*'], {cwd: dirs.src}) 30 | .pipe(gulp.dest(dirs.dst + "/zen.sketchplugin")); 31 | }); 32 | 33 | // TAREAS GULP 34 | gulp.task('default', ['watch']); 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zen-sketch", 3 | "version": "1.0.0", 4 | "description": "A very personal collection of Sketch plugins", 5 | "main": "common.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/FrancisVega/ZEN-Sketch.git" 12 | }, 13 | "author": "", 14 | "license": "ISC", 15 | "bugs": { 16 | "url": "https://github.com/FrancisVega/ZEN-Sketch/issues" 17 | }, 18 | "homepage": "https://github.com/FrancisVega/ZEN-Sketch#readme", 19 | "devDependencies": { 20 | "gulp": "^3.9.0", 21 | "gulp-rename": "^1.2.2", 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /zen.sketchplugin/Contents/Sketch/Layers/Append-BEM.cocoascript: -------------------------------------------------------------------------------- 1 | // Append BEM 2 | // @shortcut alt cmd r 3 | 4 | var onRun = function(context) { 5 | @import '../sQuery/sQuery.js'; 6 | 7 | $("%selected%").each(function(){ 8 | 9 | var layer = $(this).MSLayer(); 10 | var B = /^[^__]*/g.exec(layer.parentGroup().name())[0]; 11 | var EM = /__.*/g.exec(layer.name()); 12 | if (EM) { 13 | layer.name = B+EM; 14 | } else { 15 | layer.name = B+"__"+layer.name(); 16 | } 17 | 18 | }); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /zen.sketchplugin/Contents/Sketch/Layers/Make-Font-Artboard-Icons.cocoascript: -------------------------------------------------------------------------------- 1 | var onRun = function(context) { 2 | 3 | 4 | @import '../sQuery/sQuery.js'; 5 | 6 | const ICON_SIZE = 16; 7 | 8 | function pad(n, width, z) { 9 | z = z || '0'; 10 | n = n + ''; 11 | return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n; 12 | } 13 | 14 | function iconNameRename(glyphName, withIconName) { 15 | withIconName = withIconName || false; 16 | var artboards = $("%artboards%"); 17 | var iconName; 18 | //artboards.layers.reverse(); 19 | artboards.each(function(idx) { 20 | if (withIconName) { 21 | fullIconName = glyphName + this.layers()[0].name(); 22 | 23 | } else { 24 | fullIconName = glyphName + pad(idx, 3) + iconName; 25 | } 26 | this.name = fullIconName; 27 | }); 28 | } 29 | 30 | 31 | function reorderArtboards(itemsPerFile, offset) { 32 | var artboards = $("%artboards%"); 33 | var artboardWidth = ICON_SIZE; 34 | var artboardHeight = ICON_SIZE; 35 | var file = 0; 36 | var row = 0; 37 | //artboards.layers.reverse(); 38 | for(j=0; j<(artboards.layers.length); j++) { 39 | if(file>itemsPerFile-1) { row +=1; file=0; } 40 | artboards.layers[j].frame().x = (artboardWidth + offset) * file; 41 | artboards.layers[j].frame().y = (artboardHeight + offset) * row; 42 | file +=1; 43 | } 44 | } 45 | 46 | 47 | var CURRENTPAGE = context.document.currentPage(); 48 | var CURRENTARTBOARD = context.document.currentPage().currentArtboard(); 49 | 50 | $("%shapes%").each(function(){ 51 | 52 | var artboard = $(CURRENTPAGE).createArtboard("newArtboard", 0, 0, ICON_SIZE, ICON_SIZE); // <- sQuery 53 | artboard.addLayers([this]); 54 | CURRENTARTBOARD.removeLayer(this); 55 | this.frame().x = (ICON_SIZE-this.frame().width())/2; 56 | this.frame().y = (ICON_SIZE-this.frame().height())/2; 57 | 58 | }) 59 | 60 | CURRENTPAGE.removeLayer(CURRENTARTBOARD); 61 | reorderArtboards(10, 32); 62 | iconNameRename("", true); 63 | 64 | 65 | } 66 | -------------------------------------------------------------------------------- /zen.sketchplugin/Contents/Sketch/Layers/Remove-Empty-Groups.cocoascript: -------------------------------------------------------------------------------- 1 | // Remove empty groups 2 | // @shortcut cmd e 3 | var onRun = function(context) { 4 | const selectedEmptyGroups = () => { 5 | const DOC = context.document; 6 | const PAGE = DOC.currentPage(); 7 | const ARTBOARD = PAGE.currentArtboard(); 8 | const allLayers = ARTBOARD.children().slice(); 9 | const emptyGroups = allLayers.filter(layer => { 10 | try { 11 | return layer.layers().length === 0; 12 | } catch(e) { } 13 | }); 14 | ARTBOARD.deselectAllLayers(); 15 | emptyGroups.map(layer => layer.setIsSelected(true)); 16 | }; 17 | selectedEmptyGroups(); 18 | }; 19 | -------------------------------------------------------------------------------- /zen.sketchplugin/Contents/Sketch/Layers/Resize-Group-Area.cocoascript: -------------------------------------------------------------------------------- 1 | // Fit group bounding box size 2 | 3 | var onRun = function(context) { 4 | @import '../sQuery/sQuery.js'; 5 | $("%groups%").each(function(){ 6 | $(this).MSLayer().resizeRoot(1); 7 | }); 8 | } 9 | -------------------------------------------------------------------------------- /zen.sketchplugin/Contents/Sketch/Layers/Select-Childs-Layers.js: -------------------------------------------------------------------------------- 1 | // @format 2 | // Seleccionas las capas / grupos hijas 3 | // @shortcut alt cmd g 4 | 5 | var onRun = function(context) { 6 | 7 | const artboard = context.selection[0]; 8 | artboard.select_byExpandingSelection(0, 0); 9 | artboard.layers().slice().map((layer) => layer.select_byExpandingSelection(true, true)); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /zen.sketchplugin/Contents/Sketch/Layers/Send-To-Artboard.cocoascript: -------------------------------------------------------------------------------- 1 | var onRun = function(context) { 2 | const getArtboardFromLayer = layer => { 3 | let parent = layer.parentGroup() 4 | if(parent.class() == 'MSArtboardGroup') { 5 | artboard = parent 6 | } else { 7 | getArtboardFromLayer(parent) 8 | } 9 | return artboard 10 | } 11 | 12 | // Put a layer in Artboard root 13 | function parentToRoot(layer) { 14 | if (layer.parentGroup().class() != 'MSArtboardGroup') { 15 | // Get the artboard 16 | const artboard = getArtboardFromLayer(layer); 17 | const parent = layer.parentGroup(); 18 | // Get layer absolute position 19 | const x = layer.absoluteRect().x() - artboard.absoluteRect().x(); 20 | const y = layer.absoluteRect().y() - artboard.absoluteRect().y(); 21 | // Parent to it 22 | artboard.addLayers([layer]); 23 | // Remove from previous parent 24 | parent.removeLayer(layer); 25 | // Position new layer 26 | layer.frame().setX(x); 27 | layer.frame().setY(y); 28 | } 29 | } 30 | 31 | context.selection.slice().map(layer => parentToRoot(layer)) 32 | 33 | } 34 | -------------------------------------------------------------------------------- /zen.sketchplugin/Contents/Sketch/Layers/Snap-Layer-To-Eight.cocoascript: -------------------------------------------------------------------------------- 1 | // Round Int near to 8 mult 2 | // Ej: 15 -> 16 or 25 -> 24 3 | function roundIntToGrid(v, n) { 4 | return Math.round((v/n))*n 5 | } 6 | 7 | // Set layer position (x,y) to eight pixel grid 8 | function snapToEight(layer) { 9 | pixelGrid = 8 10 | newX = roundIntToGrid([[layer frame] x], pixelGrid) 11 | newY = roundIntToGrid([[layer frame] y], pixelGrid) 12 | [[layer frame] setX:newX] 13 | [[layer frame] setY:newY] 14 | } 15 | 16 | // Do it! 17 | snapToEight(context.selection[0]) 18 | -------------------------------------------------------------------------------- /zen.sketchplugin/Contents/Sketch/Layers/Toggle-Click-Throught.cocoascript: -------------------------------------------------------------------------------- 1 | // Toggle click throught 2 | // @shortcut cmd shift d 3 | 4 | var onRun = function(context) { 5 | @import '../sQuery/sQuery.js'; 6 | $('%groups%').toggleClickThrought(); 7 | } 8 | 9 | -------------------------------------------------------------------------------- /zen.sketchplugin/Contents/Sketch/Layers/Toggle-User-Guides.cocoascript: -------------------------------------------------------------------------------- 1 | // Tootle User Guides Layer 2 | // @shortcut cmd l 3 | 4 | var onRun = function(context) { 5 | // Import sQuery 6 | @import '../sQuery/sQuery.js'; 7 | 8 | // Toggle Guides layer visibility 9 | var guidesGroup = $('*').withName('Guides').MSLayer(); 10 | guidesGroup.setIsVisible(!guidesGroup.isVisible()); 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /zen.sketchplugin/Contents/Sketch/Layers/Transform-Each.cocoascript: -------------------------------------------------------------------------------- 1 | // Transform Each 2 | 3 | var onRun = function(context) { 4 | 5 | @import '../sQuery/sQuery.js'; 6 | 7 | var size = context.document.askForUserInput_initialValue("Enter Size Multiplier", "100%"); 8 | var re = /\d*\.*\d/; 9 | var pValue = (parseFloat(re.exec(size)[0]))/100; 10 | 11 | var x, y, w, h, frame; 12 | $('%selected%').each(function(){ 13 | 14 | // Transform data 15 | frame = this.frame(); 16 | x = frame.x(); 17 | y = frame.y(); 18 | w = frame.width(); 19 | h = frame.height(); 20 | 21 | // Moving and scaling 22 | frame.width = w*pValue; 23 | frame.height = h*pValue; 24 | frame.x = x+(w-(w*(pValue)))/2 25 | frame.y = y+(h-(h*(pValue)))/2 26 | 27 | }); 28 | 29 | }; 30 | 31 | -------------------------------------------------------------------------------- /zen.sketchplugin/Contents/Sketch/Layers/UnlockAllLayers.cocoascript: -------------------------------------------------------------------------------- 1 | var onRun = function(context) { 2 | @import '../sQuery/sQuery.js'; 3 | $("*").unlock(); 4 | } 5 | -------------------------------------------------------------------------------- /zen.sketchplugin/Contents/Sketch/Preview/Preview-Artboard-Desktop.cocoascript: -------------------------------------------------------------------------------- 1 | var onRun = function(context) { 2 | 3 | @import "common-preview.js"; 4 | 5 | const doc = context.document; 6 | 7 | const template = ` 8 | 9 | 10 | 11 | Zen Previz - {{htmlName}} 12 | 26 | 27 | 28 |
29 | 30 | 31 | 32 |
33 | 34 | `; 35 | 36 | const exportTemplate = exportArboardsWithTemplate ( context.selection, template ); 37 | 38 | if (exportTemplate) { 39 | [doc showMessage: "🖥 Desktop Previz"]; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /zen.sketchplugin/Contents/Sketch/Preview/Preview-Artboard-Mobile.cocoascript: -------------------------------------------------------------------------------- 1 | var onRun = function(context) { 2 | 3 | @import "common-preview.js"; 4 | 5 | const doc = context.document; 6 | 7 | const template = ` 8 | 9 | 10 | 11 | Zen Previz - {{htmlName}} 12 | 20 | 21 | 22 |
23 | 24 | 25 | 26 |
27 | 28 | `; 29 | 30 | const exportTemplate = exportArboardsWithTemplate ( context.selection, template ); 31 | 32 | if (exportTemplate) { 33 | [doc showMessage: "📱 Mobile Previz"]; 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /zen.sketchplugin/Contents/Sketch/Preview/Preview-Artboard-Simple.cocoascript: -------------------------------------------------------------------------------- 1 | var onRun = function(context) { 2 | 3 | @import "common-preview.js"; 4 | 5 | const doc = context.document; 6 | 7 | const template = ` 8 | 9 | 10 | 11 | Zen Previz - {{htmlName}} 12 | 21 | 22 | 23 |
24 | 25 | 26 | 27 |
28 | 29 | `; 30 | 31 | const exportTemplate = exportArboardsWithTemplate ( context.selection, template ); 32 | if (exportTemplate) { 33 | [doc showMessage: "🖥 Simple Previz"]; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /zen.sketchplugin/Contents/Sketch/Preview/Preview-Artboard-iphonex.cocoascript: -------------------------------------------------------------------------------- 1 | var onRun = function(context){ 2 | 3 | function writeFile(filename, the_string) { 4 | var path =[@"" stringByAppendingString: filename]; 5 | var str = [@"" stringByAppendingString: the_string]; 6 | str.dataUsingEncoding_(NSUTF8StringEncoding).writeToFile_atomically_(path, true) 7 | } 8 | 9 | //var documentPath = "/" //[[doc fileURL] path].split([doc displayName] + ".sketch")[0]; 10 | 11 | // GLOBAL 12 | var doc = context.document; 13 | var previzFolder = "/.previz"; 14 | var path = NSHomeDirectory() + previzFolder; 15 | var artboardHeight = doc.currentPage().currentArtboard().frame().height(); 16 | var artboardWidth = doc.currentPage().currentArtboard().frame().width(); 17 | 18 | // PNG 19 | var currentArtboard = doc.currentPage().currentArtboard() 20 | var currentArtboardName = currentArtboard.name() 21 | var exportImageFilePath = path + "/" + currentArtboardName + ".png" 22 | [doc saveArtboardOrSlice:currentArtboard toFile:exportImageFilePath] 23 | 24 | // HTML 25 | var content = '\ 26 | \ 27 | \ 28 | Presentacion\ 29 | \ 45 | \ 46 | \ 47 |
\ 48 | \ 49 | \ 50 |
\ 51 | \ 52 | ' 53 | 54 | writeFile(path + "/previz.html", content); 55 | [doc showMessage: "Mobile Previz create" + path]; 56 | 57 | } 58 | -------------------------------------------------------------------------------- /zen.sketchplugin/Contents/Sketch/Preview/common-preview.js: -------------------------------------------------------------------------------- 1 | // Ordena un array de capas según su posición horizontal, de menor a mayor. 2 | // Función para la función sort(). 3 | const sortByHorizontalPosition = (a, b) => a.frame().left() - b.frame().left(); 4 | 5 | const writeFile = (filename, the_string) => { 6 | const path =[@"" stringByAppendingString: filename]; 7 | const str = [@"" stringByAppendingString: the_string]; 8 | str.dataUsingEncoding_(NSUTF8StringEncoding).writeToFile_atomically_(path, true) 9 | } 10 | 11 | // Get slice 12 | const getSlice = artboard => { 13 | const sliceLayerAncestry = MSImmutableLayerAncestry.ancestryWithMSLayer(artboard); 14 | const rect = MSSliceTrimming.trimmedRectForLayerAncestry(sliceLayerAncestry); 15 | const slices = MSExportRequest.exportRequestsFromExportableLayer_inRect_useIDForName(artboard, rect, false); 16 | return slices[0]; 17 | } 18 | 19 | // Export slice 20 | const exportSlice = (doc, slice, filename) => { 21 | doc.saveArtboardOrSlice_toFile(slice, filename + "." + slice.format()); 22 | } 23 | 24 | // Copy to clipboard 25 | function copyToPasteboard ( string ) { 26 | const pasteboard = NSPasteboard.generalPasteboard(); 27 | pasteboard.clearContents(); 28 | pasteboard.setString_forType( NSMutableString.stringWithString( string ), NSPasteboardTypeString ); 29 | } 30 | 31 | const exportArboardsWithTemplate = ( artboards, template ) => { 32 | 33 | const previzFolder = "/.previz"; 34 | const path = NSHomeDirectory() + previzFolder; 35 | copyToPasteboard(`${path}/previz.html` ); 36 | 37 | 38 | let finalSelection; 39 | if (artboards.length == 1) { 40 | finalSelection = [context.document.currentPage().currentArtboard()]; 41 | } 42 | if (artboards.length > 1) { 43 | finalSelection = artboards; 44 | } 45 | if (artboards.length < 1) { 46 | [doc showMessage: "💩 Select at least one artboard"]; 47 | return false; 48 | } 49 | 50 | // Artboard 51 | finalSelection.slice().filter(ab => ab.class() == "MSArtboardGroup").sort(sortByHorizontalPosition) 52 | .map(function( artboard, idx, array ) { 53 | const artboardHeight = artboard.frame().height(); 54 | const artboardWidth = artboard.frame().width(); 55 | const currentArtboard = artboard; 56 | //const currentArtboardName = currentArtboard.name().replace(/[^a-zA-Z0-9]/g,'') 57 | 58 | if(idx == array.length - 1) { 59 | next = "" 60 | } else { 61 | next = idx + 1; 62 | } 63 | 64 | const htmlName = idx == 0 ? "previz.html" : "previz" + idx + ".html" 65 | const imageName = htmlName.split(".html")[0]; 66 | 67 | // Export 68 | const exportImageFilePath = path + "/" + imageName; 69 | exportSlice( context.document, getSlice(currentArtboard), exportImageFilePath ); 70 | 71 | const contentTemplate = template 72 | .replace(/{{htmlName}}/g, htmlName) 73 | .replace(/{{artboardWidth}}/g, artboardWidth + "px") 74 | .replace(/{{artboardHeight}}/g, artboardHeight + "px") 75 | .replace(/{{next}}/g, next) 76 | .replace(/{{imageName}}/g, imageName) 77 | .replace(/{{imageFormat}}/g, getSlice(currentArtboard).format()) 78 | 79 | writeFile(`${path}/${htmlName}`, contentTemplate); 80 | 81 | }) // End Map 82 | 83 | return true; 84 | } 85 | -------------------------------------------------------------------------------- /zen.sketchplugin/Contents/Sketch/Typography/ModularScale.cocoascript: -------------------------------------------------------------------------------- 1 | // Modular Scale 2 | 3 | var onRun = function(context) { 4 | 5 | @import '../sQuery/sQuery.js'; 6 | 7 | 8 | 9 | }; 10 | 11 | -------------------------------------------------------------------------------- /zen.sketchplugin/Contents/Sketch/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Zen Sketch", 3 | "description": "A very personal plugin collection for Sketch", 4 | "author": "Francis Vega", 5 | "email": "hisconer@gmail.com", 6 | "homepage": "https://github.com/FrancisVega/ZEN-Sketch", 7 | "version": 1.2, 8 | "identifier": "com.example.sketch.zen", 9 | "compatibleVersion": "40", 10 | "bundleVersion": 1, 11 | "appcast": "https://raw.githubusercontent.com/FrancisVega/ZEN-Sketch/master/appcast.xml", 12 | "commands": [ 13 | { 14 | "name": "Selec childs layers", 15 | "identifier": "selectChildsLayers", 16 | "handler": "onRun", 17 | "shortcut": "alt cmd g", 18 | "script": "Layers/Select-Childs-Layers.js" 19 | }, 20 | { 21 | "name": "Unlock All Layers", 22 | "identifier": "unlockalllayers", 23 | "handler": "onRun", 24 | "shortcut": "", 25 | "script": "Layers/UnlockAllLayers.cocoascript" 26 | }, 27 | { 28 | "name": "Transform Each", 29 | "identifier": "transformEach", 30 | "handler": "onRun", 31 | "shortcut": "alt cmd shift d", 32 | "script": "Layers/Transform-Each.cocoascript" 33 | }, 34 | { 35 | "name": "Make font grid icons", 36 | "identifier": "makeFontGridIcons", 37 | "handler": "onRun", 38 | "shortcut": "", 39 | "script": "Layers/Make-Font-Artboard-Icons.cocoascript" 40 | }, 41 | { 42 | "name": "Send to Artboard", 43 | "identifier": "sendToArtboard", 44 | "handler": "onRun", 45 | "shortcut": "", 46 | "script": "Layers/Send-To-Artboard.cocoascript" 47 | }, 48 | { 49 | "name": "Append BEM", 50 | "identifier": "appendBEM", 51 | "handler": "onRun", 52 | "shortcut": "alt cmd r", 53 | "script": "Layers/Append-BEM.cocoascript" 54 | }, 55 | { 56 | "name": "Remove Empty Groups", 57 | "identifier": "removeEmptyGroups", 58 | "handler": "onRun", 59 | "shortcut": "cmd e", 60 | "script": "Layers/Remove-Empty-Groups.cocoascript" 61 | }, 62 | { 63 | "name": "Resize Group Area", 64 | "identifier": "resizeGroupArea", 65 | "handler": "onRun", 66 | "shortcut": "", 67 | "script": "Layers/Resize-Group-Area.cocoascript" 68 | }, 69 | { 70 | "name": "Snap Layer To Eight", 71 | "identifier": "snapLayerToEight", 72 | "handler": "onRun", 73 | "shortcut": "", 74 | "script": "Layers/Snap-Layer-To-Eight.cocoascript" 75 | }, 76 | { 77 | "name": "Togge Click Throught", 78 | "identifier": "toggleClickThrought", 79 | "handler": "onRun", 80 | "shortcut": "cmd shift d", 81 | "script": "Layers/Toggle-Click-Throught.cocoascript" 82 | }, 83 | { 84 | "name": "Toggle User Guides", 85 | "identifier": "toggleUserGuides", 86 | "handler": "onRun", 87 | "shortcut": "cmd l", 88 | "script": "Layers/Toggle-User-Guides.cocoascript" 89 | }, 90 | { 91 | "name": "Preview Artboard (iPhone X)", 92 | "identifier": "previewArtboardiphonex", 93 | "shortcut": "alt cmd x", 94 | "handler": "onRun", 95 | "script": "Preview/Preview-Artboard-iphonex.cocoascript" 96 | }, 97 | { 98 | "name": "Preview Artboard (Simple)", 99 | "identifier": "previewArtboardSimple", 100 | "shortcut": "alt cmd l", 101 | "handler": "onRun", 102 | "script": "Preview/Preview-Artboard-Simple.cocoascript" 103 | }, 104 | { 105 | "name": "Preview Artboard (Desktop)", 106 | "identifier": "previewArtboardDesktop", 107 | "shortcut": "alt cmd ´", 108 | "handler": "onRun", 109 | "script": "Preview/Preview-Artboard-Desktop.cocoascript" 110 | }, 111 | { 112 | "name": "Preview Artboard (Mobile)", 113 | "identifier": "previewArtboardMobile", 114 | "shortcut": "alt cmd ñ", 115 | "handler": "onRun", 116 | "script": "Preview/Preview-Artboard-Mobile.cocoascript" 117 | }, 118 | { 119 | "name": "Live Query", 120 | "identifier": "liveQuery", 121 | "shortcut": "cmd ¡", 122 | "handler": "onRun", 123 | "script": "sQuery/liveQuery/liveQuery.js" 124 | } 125 | ], 126 | "menu": { 127 | "isRoot": false, 128 | "items": [ 129 | { 130 | "title":"Layers", 131 | "items":[ 132 | "selectChildsLayers", 133 | "unlockalllayers", 134 | "makeFontGridIcons", 135 | "sendToArtboard", 136 | "transformEach", 137 | "removeEmptyGroups", 138 | "appendBEM", 139 | "resizeGroupArea", 140 | "snapLayerToEight", 141 | "toggleClickThrought", 142 | "toggleUserGuides" 143 | ] 144 | }, 145 | { 146 | "title":"sQuery", 147 | "items":[ 148 | "liveQuery" 149 | ] 150 | }, 151 | { 152 | "title":"Preview", 153 | "items":[ 154 | "previewArtboardSimple", 155 | "previewArtboardDesktop", 156 | "previewArtboardMobile" 157 | ] 158 | } 159 | ] 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /zen.sketchplugin/Contents/Sketch/sQuery/liveQuery.js: -------------------------------------------------------------------------------- 1 | // liveQuery 2 | // @shortcut cmd ' 3 | 4 | var onRun = function(context) { 5 | @import '../sQuery/sQuery.js'; 6 | @import '../sQuery/plugins/sQuery.areGroups.js'; 7 | @import '../sQuery/plugins/sQuery.areShapes.js'; 8 | @import '../sQuery/plugins/sQuery.move.js'; 9 | @import '../sQuery/plugins/sQuery.parentRoot.js'; 10 | 11 | var doc = context.document; 12 | var sq = [doc askForUserInput:"sQuery?" initialValue:"*"]; 13 | 14 | var sel = context.selection; 15 | 16 | if (sel[0].class() == "MSArtboardGroup") { 17 | 18 | // Query 19 | if(sq == "*") { 20 | $("*").UISelect(); 21 | } 22 | 23 | // Query 24 | if(sq == "t" || sq == "text" || sq == "textlayers") { 25 | $("%textLayers%").UISelect(); 26 | } 27 | 28 | // Query 29 | if(sq == "v" || sq == "s" || sq == "shapes") { 30 | $("%shapes%").UISelect(); 31 | } 32 | 33 | // Query 34 | if(sq == "h") { 35 | $("%hierarchy%").UISelect(); 36 | } 37 | 38 | // Query 39 | if(sq == "g" || sq == "groups") { 40 | $("%groups%").UISelect(); 41 | } 42 | 43 | // Query 44 | if(sq == "i" || sq == "images" || sq == "img" || sq == "imgs") { 45 | $("%images%").UISelect(); 46 | } 47 | 48 | // Query 49 | if(sq == "l" || sq == "layers") { 50 | var shapes = $("%shapes%"); 51 | var images = $("%images%"); 52 | var text = $("%textLayers%"); 53 | //$("*").areShapes().areImages().areTextLayers().UISelect(); 54 | } 55 | 56 | } else { 57 | 58 | log("SELECCION") 59 | var SELECTED = $("%selected%"); 60 | 61 | // Query 62 | if(sq == "*") { } 63 | 64 | // Query 65 | if(sq == "t" || sq == "text" || sq == "textlayers") { 66 | SELECTED.filter(function(){ 67 | log("ok") 68 | return this.class() == "MSTextLayer"; 69 | }).UISelect(); 70 | } 71 | 72 | // Query 73 | if(sq == "v" || sq == "s" || sq == "shapes") { 74 | SELECTED.areShapes().UISelect(); 75 | } 76 | 77 | // Query 78 | if(sq == "h") { 79 | $("%hierarchy%").UISelect(); 80 | } 81 | 82 | // Query 83 | if(sq == "g" || sq == "groups") { 84 | $("%groups%").UISelect(); 85 | } 86 | 87 | // Query 88 | if(sq == "i" || sq == "images" || sq == "img" || sq == "imgs") { 89 | $("%images%").UISelect(); 90 | } 91 | 92 | // Query 93 | if(sq == "l" || sq == "layers") { 94 | var shapes = $("%shapes%"); 95 | var images = $("%images%"); 96 | var text = $("%textLayers%"); 97 | //$("*").areShapes().areImages().areTextLayers().UISelect(); 98 | } 99 | 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /zen.sketchplugin/Contents/Sketch/sQuery/liveQuery/_liveQuery.js: -------------------------------------------------------------------------------- 1 | // liveQuery 2 | // @shortcut cmd ' 3 | 4 | var onRun = function(context) { 5 | @import '../sQuery/sQuery.js'; 6 | @import '../sQuery/plugins/sQuery.areGroups.js'; 7 | @import '../sQuery/plugins/sQuery.areShapes.js'; 8 | @import '../sQuery/plugins/sQuery.move.js'; 9 | @import '../sQuery/plugins/sQuery.parentRoot.js'; 10 | @import '../sQuery/plugins/sQuery.removeEmptyGroupsRecursive.js'; 11 | 12 | var doc = context.document; 13 | var sq = [doc askForUserInput:"sQuery?" initialValue:"*"]; 14 | 15 | var sel = context.selection; 16 | 17 | if (sel[0].class() == "MSArtboardGroup") { 18 | 19 | // Query 20 | if(sq == "*") { 21 | $("*").UISelect(); 22 | } 23 | 24 | // Query 25 | if(sq == "t" || sq == "text" || sq == "textlayers") { 26 | $("%textLayers%").UISelect(); 27 | } 28 | 29 | // Query 30 | if(sq == "v" || sq == "s" || sq == "shapes") { 31 | $("%shapes%").UISelect(); 32 | } 33 | 34 | // Query 35 | if(sq == "h") { 36 | $("%hierarchy%").UISelect(); 37 | } 38 | 39 | // Query 40 | if(sq == "g" || sq == "groups") { 41 | $("%groups%").UISelect(); 42 | } 43 | 44 | // Query 45 | if(sq == "i" || sq == "images" || sq == "img" || sq == "imgs") { 46 | $("%images%").UISelect(); 47 | } 48 | 49 | // Query 50 | if(sq == "l" || sq == "layers") { 51 | var shapes = $("%shapes%"); 52 | var images = $("%images%"); 53 | var text = $("%textLayers%"); 54 | $("*").areShapes().areImages().areTextLayers().UISelect(); 55 | } 56 | 57 | } else { 58 | 59 | log("SELECCION") 60 | var SELECTED = $("%selected%"); 61 | 62 | // Query 63 | if(sq == "*") { } 64 | 65 | // Query 66 | if(sq == "t" || sq == "text" || sq == "textlayers") { 67 | SELECTED.filter(function(){ 68 | log("ok") 69 | return this.class() == "MSTextLayer"; 70 | }).UISelect(); 71 | } 72 | 73 | // Query 74 | if(sq == "v" || sq == "s" || sq == "shapes") { 75 | SELECTED.areShapes().UISelect(); 76 | } 77 | 78 | // Query 79 | if(sq == "h") { 80 | $("%hierarchy%").UISelect(); 81 | } 82 | 83 | // Query 84 | if(sq == "g" || sq == "groups") { 85 | $("%groups%").UISelect(); 86 | } 87 | 88 | // Query 89 | if(sq == "i" || sq == "images" || sq == "img" || sq == "imgs") { 90 | $("%images%").UISelect(); 91 | } 92 | 93 | // Query 94 | if(sq == "l" || sq == "layers") { 95 | var shapes = $("%shapes%"); 96 | var images = $("%images%"); 97 | var text = $("%textLayers%"); 98 | $("*").areShapes().areImages().areTextLayers().UISelect(); 99 | } 100 | 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /zen.sketchplugin/Contents/Sketch/sQuery/liveQuery/liveQuery.js: -------------------------------------------------------------------------------- 1 | // liveQuery 2 | // @shortcut cmd ¡ 3 | 4 | var onRun = function(context) { 5 | @import '../sQuery.js'; 6 | 7 | const doc = context.document; 8 | let sq = [doc askForUserInput:'sQuery?' initialValue:''].toLowerCase(); 9 | 10 | switch (sq) { 11 | case '': 12 | case 'shapes': 13 | case 'shape': 14 | case 's': 15 | case 'l': 16 | case 'v': 17 | $('%selected%').filter(function() { 18 | return this.class() == 'MSShapeGroup'; 19 | }).UISelect(); 20 | break; 21 | 22 | case 'group': 23 | case 'groups': 24 | case 'g': 25 | $('%selected%').filter(function() { 26 | return this.class() == 'MSLayerGroup'; 27 | }).UISelect(); 28 | break; 29 | 30 | case 'text': 31 | case 'texts': 32 | case 't': 33 | $('%selected%').filter(function() { 34 | return this.class() == 'MSTextLayer'; 35 | }).UISelect(); 36 | break; 37 | 38 | case 'image': 39 | case 'images': 40 | case 'i': 41 | $('%selected%').filter(function() { 42 | return this.class() == 'MSBitmapLayer'; 43 | }).UISelect(); 44 | break; 45 | 46 | default: 47 | $('%selected%').filter(function() { 48 | return this.class() == 'MSShapeGroup'; 49 | }).UISelect(); 50 | break; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /zen.sketchplugin/Contents/Sketch/sQuery/plugins/sQuery.areGroups.js: -------------------------------------------------------------------------------- 1 | /** 2 | * sQuery plugin - areGroups 1.0 3 | * Francis Vega 4 | * 5 | * @desc Filter elements and return just LayerGroups 6 | * @return {sQuery} 7 | * @example 8 | * $('%selected%').areGroups().UISelect(); 9 | * @desc Filter the selection (in Sketchapp UI) and make a selection only with groups. 10 | */ 11 | 12 | (function($){ 13 | 14 | $.fn.areGroups = function() { 15 | this.layers = this.layers.slice().filter(layer => layer.class() == MSLayerGroup); 16 | return this; 17 | } 18 | 19 | }(sQuery)); 20 | -------------------------------------------------------------------------------- /zen.sketchplugin/Contents/Sketch/sQuery/plugins/sQuery.areShapes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * sQuery plugin - areShapes 1.0 3 | * Francis Vega 4 | * 5 | * @desc Filter elements and return just ShapeGroups 6 | * @return {sQuery} 7 | * @example 8 | * $('%selected%').areShapes().UISelect(); 9 | * @desc Filter the selection (in Sketchapp UI) and make a selection only 10 | * with shape layers. 11 | */ 12 | 13 | (function($){ 14 | 15 | $.fn.areShapes = function() { 16 | this.layers = this.layers.slice().filter(layer => layer.class() == MSShapeGroup); 17 | return this; 18 | } 19 | 20 | }(sQuery)); 21 | -------------------------------------------------------------------------------- /zen.sketchplugin/Contents/Sketch/sQuery/plugins/sQuery.move.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * sQuery plugin - move 1.0 4 | * Francis Vega 5 | * 6 | * @desc Move query layers 7 | * @param {object} coords 8 | * @param {object} coords.x Position x value 9 | * @param {object} coords.y Position y value 10 | * @return {sQuery} 11 | * @example 12 | * $('%selected%').move({"x":100, "y":234}) 13 | * @desc Move "selected" layers by x100, y234 14 | */ 15 | 16 | (function($){ 17 | $.fn.move = function(coords, pixelFit = false) { 18 | 19 | let finalX, finalY; 20 | 21 | this.each(function() { 22 | 23 | if(pixelFit) { 24 | finalX = Math.round( this.absoluteRect().x() - this.parentRootForAbsoluteRect().rect().origin.x + coords.x ); 25 | finalY = Math.round( this.absoluteRect().y() - this.parentRootForAbsoluteRect().rect().origin.y + coords.y ); 26 | } else { 27 | finalX = this.absoluteRect().x() - this.parentRootForAbsoluteRect().rect().origin.x + coords.x; 28 | finalY = this.absoluteRect().y() - this.parentRootForAbsoluteRect().rect().origin.y + coords.y; 29 | } 30 | 31 | this.absoluteRect().x = finalX + this.parentRootForAbsoluteRect().rect().origin.x 32 | this.absoluteRect().y = finalY + this.parentRootForAbsoluteRect().rect().origin.y 33 | 34 | }); 35 | 36 | return this; 37 | 38 | } 39 | }(sQuery)); 40 | -------------------------------------------------------------------------------- /zen.sketchplugin/Contents/Sketch/sQuery/plugins/sQuery.sendToRoot.js: -------------------------------------------------------------------------------- 1 | /** 2 | * sQuery plugin - sendToRoot 1.0 3 | * Francis Vega 4 | * 5 | * @desc Send layers "back" to Artboard 6 | * @return {sQuery} 7 | * @example 8 | * $('%selected%').sendToRoot(); 9 | */ 10 | 11 | (function($) { 12 | $.fn.sendToRoot = function() { 13 | // For each query 14 | this.each(function() { 15 | const parent = this.parentGroup(); 16 | if (parent.class() != MSArtboardGroup) { 17 | // Get the artboard 18 | const artboard = this.parentRootForAbsoluteRect(); 19 | // Get this absolute position 20 | const x = this.absoluteRect().x() - artboard.absoluteRect().x(); 21 | const y = this.absoluteRect().y() - artboard.absoluteRect().y(); 22 | // Parent to it 23 | artboard.addLayers([this]); 24 | // Remove from previous parent 25 | parent.removeLayer(this); 26 | // Position new this 27 | this.frame().setX(x); 28 | this.frame().setY(y); 29 | } 30 | }); 31 | return this; 32 | } 33 | }(sQuery)); 34 | -------------------------------------------------------------------------------- /zen.sketchplugin/Contents/Sketch/sQuery/sQuery.js: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | Copyright (c) 2017 Francis Vega 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 5 | associated documentation files (the "Software"), to deal in the Software without restriction, 6 | including without limitation the rights to use, copy, modify, merge, publish, distribute, 7 | sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this 11 | permission notice shall be included in all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 14 | NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 15 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 16 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 18 | */ 19 | 20 | /* 21 | * 22 | * sQuery 0.1 23 | * 24 | */ 25 | 26 | let sQuery, $; 27 | 28 | (function(){ 29 | 30 | sQuery = $ = (selector, page, artboard) => new SQUERY(selector, page, artboard) 31 | 32 | /** 33 | * findObjectsByName 34 | * @param {string} name The name of the object (layer) 35 | * @param {scope} scope The scope (layers) of search 36 | * @return {MSArray} 37 | */ 38 | const findObjectsByName = (name, scope) => { 39 | const predicate = NSPredicate.predicateWithFormat("name == %@", name) 40 | return scope.filteredArrayUsingPredicate(predicate) 41 | } 42 | 43 | /** 44 | * findObjectsOfType 45 | * @param {string|classType} classType The name of the class type 46 | * @param {scope} scope The scope (layers) of search 47 | * @return {MSArray} 48 | */ 49 | const findObjectsOfType = (classType, scope) => scope.slice().filter(lyr => lyr.class() == classType) 50 | 51 | /** 52 | * flattenArray 53 | * @param {array} arr The array to flatten 54 | * @return {array} return a one level deep array 55 | */ 56 | const flattenArray = arr => arr.reduce( (flat, toFlatten) => 57 | flat.concat( Array.isArray(toFlatten) ? flattenArray(toFlatten) : toFlatten), [] 58 | ) 59 | 60 | const SQUERY = function(selector, page, artboard) { 61 | 62 | if (typeof selector == "string") { 63 | 64 | // Consts 65 | const DOC = context.document; 66 | const CURRENTPAGE = DOC.currentPage(); 67 | const CURRENTARTBOARD = CURRENTPAGE.currentArtboard(); 68 | 69 | switch(selector) { 70 | // All 71 | case "*": 72 | this.layers = CURRENTARTBOARD.children().slice().filter(layer => layer.class() != "MSArtboardGroup" && layer.class() != "MSRectangleShape") 73 | break 74 | 75 | case "%hierarchy%": 76 | this.layers = CURRENTARTBOARD.layers() 77 | break 78 | 79 | case "%pages%": 80 | this.layers = DOC.pages() 81 | break 82 | 83 | case "%artboards%": 84 | this.layers = CURRENTPAGE.artboards() 85 | break 86 | 87 | case "%images%": 88 | this.layers = findObjectsOfType(MSBitmapLayer, CURRENTARTBOARD.children()) 89 | break 90 | 91 | case "%layers%": 92 | this.layers = CURRENTARTBOARD.children().slice().filter(layer => layer.class() != "MSArtboardGroup" && layer.class() != "MSRectangleShape" && layer.class() != "MSLayerGroup") 93 | break 94 | 95 | case "%shapes%": 96 | this.layers = findObjectsOfType(MSShapeGroup, CURRENTARTBOARD.children()) 97 | break 98 | 99 | case "%groups%": 100 | this.layers = findObjectsOfType(MSLayerGroup, CURRENTARTBOARD.children()) 101 | break 102 | 103 | case "%textLayers%": 104 | this.layers = findObjectsOfType(MSTextLayer, CURRENTARTBOARD.children()) 105 | break 106 | 107 | case "%selected%": 108 | this.layers = context.selection 109 | break 110 | 111 | // Default: Layer name. 112 | default: 113 | this.layers = findObjectsByName(selector, CURRENTARTBOARD.children()) 114 | break 115 | } 116 | } 117 | 118 | if (typeof selector == "object") { 119 | this.layers = [selector] 120 | } 121 | 122 | return this 123 | } 124 | 125 | /* @sQuery API */ 126 | 127 | sQuery.fn = SQUERY.prototype = { 128 | 129 | /** 130 | * Return an array with the queried layers 131 | * @return {array} 132 | */ 133 | sLayers: function() { 134 | return this.layers 135 | }, 136 | 137 | /** 138 | * Query text layers 139 | * @return {sQuery} 140 | */ 141 | texts: function() { 142 | this.layers = this.layers.slice().filter(layer => layer.class() == MSTextLayer) 143 | return this 144 | }, 145 | 146 | /** 147 | * Query groups 148 | * @return {sQuery} 149 | */ 150 | groups: function() { 151 | this.layers = this.layers.slice().filter(layer => layer.class() == MSLayerGroup) 152 | return this 153 | }, 154 | 155 | /** 156 | * Query pages 157 | * @return {sQuery} 158 | */ 159 | pages: function() { 160 | this.layers = this.layers.slice().filter(layer => layer.class() == MSPage) 161 | return this 162 | }, 163 | 164 | /** 165 | * Query artboards 166 | * @return {sQuery} 167 | */ 168 | artboards: function() { 169 | this.layers = this.layers.slice().filter(layer => layer.class() == MSArtboardGroup) 170 | return this 171 | }, 172 | 173 | /** 174 | * Query shape layers 175 | * @return {sQuery} 176 | */ 177 | shapes: function() { 178 | this.layers = this.layers.slice().filter(layer => layer.class() == MSShapeGroup) 179 | return this 180 | }, 181 | 182 | /** 183 | * images 184 | * @return {sQuery} 185 | */ 186 | images: function() { 187 | this.layers = this.layers.slice().filter(layer => layer.class() == MSBitmapLayer) 188 | return this 189 | }, 190 | 191 | /** 192 | * isLocked 193 | * @return {sQuery} 194 | */ 195 | isLocked: function() { 196 | this.layers = this.layers.slice().filter(layer => layer.isLocked()) 197 | return this 198 | }, 199 | 200 | /** 201 | * startsWith 202 | * @param {string} str 203 | * @return {sQuery} 204 | */ 205 | startsWith: function(str) { 206 | this.layers = this.layers.slice().filter(layer => layer.name().substr(0, str.length) == str) 207 | return this 208 | }, 209 | 210 | /** 211 | * endsWith 212 | * @param {string} str 213 | * @return {sQuery} 214 | */ 215 | endsWith: function(str) { 216 | this.layers = this.layers.slice().filter(layer => layer.name().substr(layer.name().length() - str.length) == str) 217 | return this 218 | }, 219 | 220 | /** 221 | * contains 222 | * @param {string} str 223 | * @return {sQuery} 224 | */ 225 | contains: function(str) { 226 | this.layers = this.layers.slice().filter(layer => layer.name().indexOf(str) != -1) 227 | return this 228 | }, 229 | 230 | /** 231 | * withName 232 | * @param {string} name 233 | * @return {sQuery} 234 | */ 235 | withName: function(name) { 236 | this.layers = this.layers.slice().filter(layer => layer.name() == name) 237 | return this 238 | }, 239 | 240 | /** 241 | * childs 242 | * @return {sQuery} 243 | */ 244 | childs: function() { 245 | this.layers = flattenArray( 246 | this.layers.slice().map(function(layer) { 247 | if (layer.class() == MSSymbolInstance) { 248 | const symbolMasterId = layer.symbolMaster().objectID() 249 | const symbolChilds = layer.symbolMaster().children().slice() 250 | const symbolChildsWithoutSymbolItself = symbolChilds.filter(symbolChildsLayer => symbolChildsLayer.objectID() != symbolMasterId) 251 | return symbolChildsWithoutSymbolItself 252 | } else { 253 | return layer.children().slice() 254 | } 255 | }) 256 | ) 257 | return this 258 | }, 259 | 260 | /** 261 | * hasClickThrought 262 | * @return {sQuery} 263 | */ 264 | hasClickThrought: function() { 265 | const groups = this.layers.slice().filter(layer => layer.class() == MSLayerGroup) 266 | this.layers = groups.filter(layer => layer.hasClickThrough()) 267 | return this 268 | }, 269 | 270 | /** 271 | * setHasClickThrough 272 | * @return {sQuery} 273 | */ 274 | setHasClickThrough: function(status = false) { 275 | const groups = this.layers.slice().filter(layer => layer.class() == MSLayerGroup) 276 | groups.map(layer => layer.setHasClickThrough(status)) 277 | return this 278 | }, 279 | 280 | /** 281 | * toggleClickThrought 282 | * @return {sQuery} 283 | */ 284 | toggleClickThrought: function() { 285 | const groups = this.layers.slice().filter(layer => layer.class() == MSLayerGroup) 286 | groups.map(layer => layer.setHasClickThrough(!layer.hasClickThrough())) 287 | return this 288 | }, 289 | 290 | /** 291 | * isEmpty 292 | * @return {sQuery} 293 | */ 294 | isEmpty: function() { 295 | const groups = this.layers.slice().filter(layer => layer.class() == MSLayerGroup) 296 | this.layers = groups.filter(layer => layer.layers().length === 0) 297 | return this 298 | }, 299 | 300 | /** 301 | * isVisible 302 | * @return {sQuery} 303 | */ 304 | isVisible: function() { 305 | this.layers = this.layers.slice().filter(layer => layer.isVisible() == 1) 306 | return this 307 | }, 308 | 309 | /** 310 | * isHidden 311 | * @return {sQuery} 312 | */ 313 | isHidden: function() { 314 | this.layers = this.layers.slice().filter(layer => layer.isVisible() == 0) 315 | return this 316 | }, 317 | 318 | /** 319 | * visibility 320 | * @param {bool} status 321 | * @return {sQuery} 322 | */ 323 | visibility: function(status) { 324 | this.layers.slice().map(layer => layer.setIsVisible(status)) 325 | return this 326 | }, 327 | 328 | /** 329 | * show 330 | * @return {sQuery} 331 | */ 332 | show: function() { 333 | this.layers.slice().map(layer => layer.setIsVisible(true)) 334 | return this 335 | }, 336 | 337 | /** 338 | * hide 339 | * @return {sQuery} 340 | */ 341 | hide: function() { 342 | this.layers.slice().map(layer => layer.setIsVisible(false)) 343 | return this 344 | }, 345 | 346 | /** 347 | * lock 348 | * @return {sQuery} 349 | */ 350 | lock: function() { 351 | this.layers.slice().map(layer => layer.setIsLocked(true)) 352 | return this 353 | }, 354 | 355 | /** 356 | * unlock 357 | * @return {sQuery} 358 | */ 359 | unlock: function() { 360 | this.layers.slice().map(layer => layer.setIsLocked(false)) 361 | return this 362 | }, 363 | 364 | /** 365 | * duplicate 366 | * @param {string} name 367 | * @return {sQuery} 368 | */ 369 | duplicate: function(name) { 370 | const duplicateLayers = this.layers.slice().map(layer => layer.duplicate()) 371 | duplicateLayers.map(layer => layer.name = name) 372 | this.layers = duplicateLayers 373 | return this 374 | }, 375 | 376 | /** 377 | * remove 378 | * @return {sQuery} 379 | */ 380 | remove: function() { 381 | this.layers.slice().map(layer => layer.removeFromParent()) 382 | }, 383 | 384 | /** 385 | * opacity 386 | * @param {number} val 387 | * @return {sQuery} 388 | */ 389 | opacity: function(val) { 390 | if (val) { 391 | this.layers.slice().map(layer => layer.style().contextSettings().opacity = val / 100) 392 | } else { 393 | return this.layers.slice().map(layer => layer.style().contextSettings().opacity()) 394 | } 395 | }, 396 | 397 | /** 398 | * absolutePosition 399 | * @return {sQuery} 400 | */ 401 | absolutePosition: function() { 402 | return this.layers.slice().map(layer => [layer.absoluteRect().x(), layer.absoluteRect().y()]) 403 | }, 404 | 405 | /** 406 | * relativePosition 407 | * @return {sQuery} 408 | */ 409 | relativePosition: function() { 410 | return this.layers.slice().map(layer => 411 | [ 412 | layer.absoluteRect().x() - layer.parentRootForAbsoluteRect().rect().origin.x, 413 | layer.absoluteRect().y() - layer.parentRootForAbsoluteRect().rect().origin.y 414 | ] 415 | ) 416 | }, 417 | 418 | /** 419 | * rename 420 | * @param {string} name 421 | * @return {sQuery} 422 | */ 423 | rename: function(name) { 424 | this.layers.slice().map(layer => layer.name = name) 425 | return this 426 | }, 427 | 428 | /** 429 | * UISelect 430 | * @return {sQuery} 431 | */ 432 | UISelect: function() { 433 | context.selection[0].select_byExpandingSelection(0, 0) 434 | this.layers.slice().map(layer => layer.select_byExpandingSelection(true, true)) 435 | return this 436 | }, 437 | 438 | /** 439 | * Itera por cada uno de los elementos previamente seleccionados y devuelve el elemento. 440 | * @param {function} callback Una función a la que each llama por cada iteración. 441 | * @return {sQuery} 442 | */ 443 | each: function(callback) { 444 | for(let i=0, len=this.layers.length; i