├── coscript ├── .gitignore ├── doc └── images │ └── intro.gif ├── example └── ChatAppFreebie │ ├── Readme.md │ └── documents │ ├── Page 1 - 00220 │ ├── Chat Freebie - 3e6e0 │ │ ├── Chat - 2422b │ │ │ ├── Chat - ca269 │ │ │ │ ├── RIGHT 1. Avatar - d8723 │ │ │ │ │ ├── bdff320.png │ │ │ │ │ └── oval.json │ │ │ │ ├── Group 2 - 9eed7 │ │ │ │ │ ├── RIGHT 1. Avatar - 201cc │ │ │ │ │ │ ├── 52acc3b.png │ │ │ │ │ │ └── oval.json │ │ │ │ │ ├── group.json │ │ │ │ │ ├── Combined Shape - 1f6e4 │ │ │ │ │ │ ├── Rectangle 29 - 77018 │ │ │ │ │ │ │ └── rectangle.json │ │ │ │ │ │ ├── RIGHT 1. Chat Bubble - 8cccb │ │ │ │ │ │ │ └── rectangle.json │ │ │ │ │ │ └── combinedShape.json │ │ │ │ │ ├── Albert Larson - 0b1a3 │ │ │ │ │ │ └── text.json │ │ │ │ │ ├── RIGHT 1. Seen Text - b6e75 │ │ │ │ │ │ └── text.json │ │ │ │ │ └── lorem text - 822c9 │ │ │ │ │ │ └── text.json │ │ │ │ ├── Group 2 Copy - 44a2d │ │ │ │ │ ├── RIGHT 1. Avatar - 56219 │ │ │ │ │ │ ├── ebf73ef.png │ │ │ │ │ │ └── oval.json │ │ │ │ │ ├── group.json │ │ │ │ │ ├── Combined Shape - a53b9 │ │ │ │ │ │ ├── Rectangle 29 - 67824 │ │ │ │ │ │ │ └── rectangle.json │ │ │ │ │ │ ├── RIGHT 1. Chat Bubble - b8ed3 │ │ │ │ │ │ │ └── rectangle.json │ │ │ │ │ │ └── combinedShape.json │ │ │ │ │ ├── Daniel Santos - 1b991 │ │ │ │ │ │ └── text.json │ │ │ │ │ ├── RIGHT 1. Seen Text - 47a2b │ │ │ │ │ │ └── text.json │ │ │ │ │ └── lorem text - 4aa67 │ │ │ │ │ │ └── text.json │ │ │ │ ├── Group 2 Copy 2 - 21e0a │ │ │ │ │ ├── RIGHT 1. Avatar - eff24 │ │ │ │ │ │ ├── 0953348.png │ │ │ │ │ │ └── oval.json │ │ │ │ │ ├── group.json │ │ │ │ │ ├── Combined Shape - 11274 │ │ │ │ │ │ ├── Rectangle 29 - 1d177 │ │ │ │ │ │ │ └── rectangle.json │ │ │ │ │ │ ├── RIGHT 1. Chat Bubble - 496c5 │ │ │ │ │ │ │ └── rectangle.json │ │ │ │ │ │ └── combinedShape.json │ │ │ │ │ ├── Kyle Brown - 96afe │ │ │ │ │ │ └── text.json │ │ │ │ │ ├── RIGHT 1. Seen Text - 35b70 │ │ │ │ │ │ └── text.json │ │ │ │ │ └── lorem text - b44e0 │ │ │ │ │ │ └── text.json │ │ │ │ ├── group.json │ │ │ │ ├── Combined Shape - 7da6f │ │ │ │ │ ├── Rectangle 29 - e6821 │ │ │ │ │ │ └── rectangle.json │ │ │ │ │ ├── RIGHT 1. Chat Bubble - 33b5a │ │ │ │ │ │ └── rectangle.json │ │ │ │ │ └── combinedShape.json │ │ │ │ ├── Bryan Walters - bc585 │ │ │ │ │ └── text.json │ │ │ │ ├── RIGHT 1. Seen Text - dd954 │ │ │ │ │ └── text.json │ │ │ │ └── lorem text - a8189 │ │ │ │ │ └── text.json │ │ │ ├── group.json │ │ │ ├── Add - 0f9cc │ │ │ │ ├── group.json │ │ │ │ ├── Group - 16c23 │ │ │ │ │ ├── group.json │ │ │ │ │ ├── Rectangle 27 - e74c0 │ │ │ │ │ │ └── rectangle.json │ │ │ │ │ └── Rectangle 27 - fa4be │ │ │ │ │ │ └── rectangle.json │ │ │ │ └── Rectangle 27 - d1ce8 │ │ │ │ │ └── rectangle.json │ │ │ ├── BG - dc734 │ │ │ │ ├── group.json │ │ │ │ └── Rectangle 28 - 34fed │ │ │ │ │ └── rectangle.json │ │ │ ├── Top - 45f5e │ │ │ │ ├── group.json │ │ │ │ ├── more - 65862 │ │ │ │ │ ├── group.json │ │ │ │ │ └── Shape - ecce4 │ │ │ │ │ │ ├── combinedShape.json │ │ │ │ │ │ ├── Path - 55e89 │ │ │ │ │ │ └── path.json │ │ │ │ │ │ ├── Path - 23d19 │ │ │ │ │ │ └── path.json │ │ │ │ │ │ └── Path - 7edf1 │ │ │ │ │ │ └── path.json │ │ │ │ ├── RIGHT Title - 4d357 │ │ │ │ │ ├── group.json │ │ │ │ │ └── Chat Group - 417d1 │ │ │ │ │ │ └── text.json │ │ │ │ └── RIGHT TOP Gray - 72f06 │ │ │ │ │ └── rectangle.json │ │ │ └── Type - 1c6b5 │ │ │ │ ├── group.json │ │ │ │ ├── favorite - 7deec │ │ │ │ ├── group.json │ │ │ │ └── Shape - 17715 │ │ │ │ │ ├── shapePath.json │ │ │ │ │ └── Path - 6b7b5 │ │ │ │ │ └── path.json │ │ │ │ ├── Rectangle 30 - a532b │ │ │ │ └── rectangle.json │ │ │ │ ├── RIGHT Buttom Chatbar - 7c3a9 │ │ │ │ └── rectangle.json │ │ │ │ └── Type Something... - 9acce │ │ │ │ └── text.json │ │ ├── Conversations - 302c1 │ │ │ ├── Conv 1 - 4b3bd │ │ │ │ ├── LEFT 1. Avatar - eedb9 │ │ │ │ │ ├── e69430b.png │ │ │ │ │ └── oval.json │ │ │ │ ├── group.json │ │ │ │ ├── LEFT Chat 1 - 06f6e │ │ │ │ │ ├── group.json │ │ │ │ │ ├── Barry Pope - 3fd7f │ │ │ │ │ │ └── text.json │ │ │ │ │ └── Please go away… - c07dc │ │ │ │ │ │ └── text.json │ │ │ │ ├── LEFT Chat Background 1 - f83a0 │ │ │ │ │ └── rectangle.json │ │ │ │ └── 11 Feb 2016 - 7c98f │ │ │ │ │ └── text.json │ │ │ ├── Conv 2 - 8544f │ │ │ │ ├── LEFT 1. Avatar - 42d86 │ │ │ │ │ ├── c84e46f.png │ │ │ │ │ └── oval.json │ │ │ │ ├── group.json │ │ │ │ ├── LEFT Chat 1 - a2301 │ │ │ │ │ ├── group.json │ │ │ │ │ ├── Ryan Pittman - dd578 │ │ │ │ │ │ └── text.json │ │ │ │ │ └── At a meta level, des - 4f16a │ │ │ │ │ │ └── text.json │ │ │ │ ├── LEFT Chat Background 1 - 90963 │ │ │ │ │ └── rectangle.json │ │ │ │ └── 21 Jul 2016 - 14ad0 │ │ │ │ │ └── text.json │ │ │ ├── Conv 3 - 12538 │ │ │ │ ├── LEFT 1. Avatar - afa33 │ │ │ │ │ ├── ebf73ef.png │ │ │ │ │ └── oval.json │ │ │ │ ├── group.json │ │ │ │ ├── LEFT Chat 1 - a96a2 │ │ │ │ │ ├── group.json │ │ │ │ │ ├── Larry Park - 3a3b6 │ │ │ │ │ │ └── text.json │ │ │ │ │ └── The only important t - 3162e │ │ │ │ │ │ └── text.json │ │ │ │ ├── LEFT Chat Background 1 - 30a61 │ │ │ │ │ └── rectangle.json │ │ │ │ └── 13 Feb 2016 - 4aa06 │ │ │ │ │ └── text.json │ │ │ ├── Conv 4 - b4e8f │ │ │ │ ├── LEFT 1. Avatar - eb61e │ │ │ │ │ ├── bdff320.png │ │ │ │ │ └── oval.json │ │ │ │ ├── group.json │ │ │ │ ├── LEFT Chat 1 - e5f8b │ │ │ │ │ ├── group.json │ │ │ │ │ ├── Leon Conner - 987b2 │ │ │ │ │ │ └── text.json │ │ │ │ │ └── Design is a plan for - 45d32 │ │ │ │ │ │ └── text.json │ │ │ │ ├── LEFT Chat Background 1 - 745cb │ │ │ │ │ └── rectangle.json │ │ │ │ └── 03 Mar 2016 - bb0af │ │ │ │ │ └── text.json │ │ │ ├── Conv 5 - 3a9f7 │ │ │ │ ├── LEFT 1. Avatar - b31e8 │ │ │ │ │ ├── 52acc3b.png │ │ │ │ │ └── oval.json │ │ │ │ ├── group.json │ │ │ │ ├── LEFT Chat 1 - 732fd │ │ │ │ │ ├── group.json │ │ │ │ │ ├── Jeremy Long - 2b13e │ │ │ │ │ │ └── text.json │ │ │ │ │ └── Computers are to des - 2d0dd │ │ │ │ │ │ └── text.json │ │ │ │ ├── LEFT Chat Background 1 - 2f121 │ │ │ │ │ └── rectangle.json │ │ │ │ └── 02 Nov 2016 - bf3be │ │ │ │ │ └── text.json │ │ │ ├── Conv 6 - 758f0 │ │ │ │ ├── LEFT 1. Avatar - acdff │ │ │ │ │ ├── 0953348.png │ │ │ │ │ └── oval.json │ │ │ │ ├── group.json │ │ │ │ ├── LEFT Chat 1 - 83c54 │ │ │ │ │ ├── group.json │ │ │ │ │ ├── Adam Floyd - c0e42 │ │ │ │ │ │ └── text.json │ │ │ │ │ └── Design is the consci - f832a │ │ │ │ │ │ └── text.json │ │ │ │ ├── LEFT Chat Background 1 - 6fba0 │ │ │ │ │ └── rectangle.json │ │ │ │ └── 09 Nov 2016 - b95ec │ │ │ │ │ └── text.json │ │ │ ├── group.json │ │ │ └── Conversations - 9f24e │ │ │ │ ├── group.json │ │ │ │ ├── LEFT TOP Green - 0340e │ │ │ │ └── rectangle.json │ │ │ │ └── Recent Conversations - 06201 │ │ │ │ └── text.json │ │ ├── BG - 108af │ │ │ ├── group.json │ │ │ └── Rectangle 28 - 73492 │ │ │ │ └── rectangle.json │ │ └── artboard.json │ └── page.json │ └── Symbols - d5118 │ └── page.json ├── src ├── layers │ ├── symbolLayer.js │ ├── pathLayer.js │ ├── groupLayer.js │ ├── pageLayer.js │ ├── artboardLayer.js │ ├── symbolMasterLayer.js │ ├── imageLayer.js │ ├── layerMixin.js │ ├── textLayer.js │ ├── general.js │ └── shapeGroupLayer.js ├── index.js ├── file.js ├── layer.js ├── layerUtil.js ├── sketch.js ├── util.js └── importer.js ├── package.json ├── LICENSE ├── Readme.md └── main.js /coscript: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skahack/open-sketch/HEAD/coscript -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /documents 3 | bundle.js 4 | node_modules 5 | tmp 6 | .local.vimrc 7 | -------------------------------------------------------------------------------- /doc/images/intro.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skahack/open-sketch/HEAD/doc/images/intro.gif -------------------------------------------------------------------------------- /example/ChatAppFreebie/Readme.md: -------------------------------------------------------------------------------- 1 | Sketch file is here 2 | https://dribbble.com/shots/2719193-Chat-App-Freebie 3 | -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/RIGHT 1. Avatar - d8723/bdff320.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skahack/open-sketch/HEAD/example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/RIGHT 1. Avatar - d8723/bdff320.png -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 1 - 4b3bd/LEFT 1. Avatar - eedb9/e69430b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skahack/open-sketch/HEAD/example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 1 - 4b3bd/LEFT 1. Avatar - eedb9/e69430b.png -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 2 - 8544f/LEFT 1. Avatar - 42d86/c84e46f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skahack/open-sketch/HEAD/example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 2 - 8544f/LEFT 1. Avatar - 42d86/c84e46f.png -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 3 - 12538/LEFT 1. Avatar - afa33/ebf73ef.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skahack/open-sketch/HEAD/example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 3 - 12538/LEFT 1. Avatar - afa33/ebf73ef.png -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 4 - b4e8f/LEFT 1. Avatar - eb61e/bdff320.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skahack/open-sketch/HEAD/example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 4 - b4e8f/LEFT 1. Avatar - eb61e/bdff320.png -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 5 - 3a9f7/LEFT 1. Avatar - b31e8/52acc3b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skahack/open-sketch/HEAD/example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 5 - 3a9f7/LEFT 1. Avatar - b31e8/52acc3b.png -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 6 - 758f0/LEFT 1. Avatar - acdff/0953348.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skahack/open-sketch/HEAD/example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 6 - 758f0/LEFT 1. Avatar - acdff/0953348.png -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 - 9eed7/RIGHT 1. Avatar - 201cc/52acc3b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skahack/open-sketch/HEAD/example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 - 9eed7/RIGHT 1. Avatar - 201cc/52acc3b.png -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 Copy - 44a2d/RIGHT 1. Avatar - 56219/ebf73ef.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skahack/open-sketch/HEAD/example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 Copy - 44a2d/RIGHT 1. Avatar - 56219/ebf73ef.png -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 Copy 2 - 21e0a/RIGHT 1. Avatar - eff24/0953348.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skahack/open-sketch/HEAD/example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 Copy 2 - 21e0a/RIGHT 1. Avatar - eff24/0953348.png -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/page.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "0CA6050E-B331-4A83-98BF-B58977168DAF", 3 | "type": "page", 4 | "className": "MSPage", 5 | "name": "Page 1", 6 | "index": 2, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 0.000px", 10 | "width: 1280.000px", 11 | "height: 800.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Symbols - d5118/page.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "EFB4E2A5-3B26-4CAB-9811-C2578B97168B", 3 | "type": "page", 4 | "className": "MSPage", 5 | "name": "Symbols", 6 | "index": 1, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 100.000px", 10 | "width: 2120.000px", 11 | "height: 24.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/BG - 108af/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "ECFACC0A-2F29-4676-9528-4BD0C411C9F5", 3 | "type": "group", 4 | "className": "MSLayerGroup", 5 | "name": "BG", 6 | "index": 3, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 0.000px", 10 | "width: 1280.000px", 11 | "height: 800.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "1CC08084-0196-46D5-B860-90F7DB10C59C", 3 | "type": "group", 4 | "className": "MSLayerGroup", 5 | "name": "Chat", 6 | "index": 1, 7 | "styles": [ 8 | "top: 40.000px", 9 | "left: 540.000px", 10 | "width: 702.000px", 11 | "height: 720.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/artboard.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "31EC0D2A-DD2A-45F0-96DF-47A5FE2B8823", 3 | "type": "artboard", 4 | "className": "MSArtboardGroup", 5 | "name": "Chat Freebie", 6 | "index": 1, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 0.000px", 10 | "width: 1280.000px", 11 | "height: 800.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Add - 0f9cc/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "25D71C35-BACD-4770-ADFF-3D9A7C334D01", 3 | "type": "group", 4 | "className": "MSLayerGroup", 5 | "name": "Add", 6 | "index": 1, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 630.000px", 10 | "width: 72.000px", 11 | "height: 72.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/BG - dc734/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "857106ED-5865-4562-9698-8D992B49BCF7", 3 | "type": "group", 4 | "className": "MSLayerGroup", 5 | "name": "BG", 6 | "index": 5, 7 | "styles": [ 8 | "top: 72.000px", 9 | "left: 0.000px", 10 | "width: 702.000px", 11 | "height: 648.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Top - 45f5e/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "ACDA2A2D-B278-4D8E-A8CD-CA0B3481574E", 3 | "type": "group", 4 | "className": "MSLayerGroup", 5 | "name": "Top", 6 | "index": 2, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 0.000px", 10 | "width: 702.000px", 11 | "height: 72.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "4224530B-8C34-4579-BED6-434F83A29C70", 3 | "type": "group", 4 | "className": "MSLayerGroup", 5 | "name": "Chat", 6 | "index": 4, 7 | "styles": [ 8 | "top: 104.000px", 9 | "left: 24.000px", 10 | "width: 655.000px", 11 | "height: 500.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Type - 1c6b5/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "C6735BBA-223D-4F8A-986B-295E5C0BA256", 3 | "type": "group", 4 | "className": "MSLayerGroup", 5 | "name": "Type", 6 | "index": 3, 7 | "styles": [ 8 | "top: 646.000px", 9 | "left: 24.000px", 10 | "width: 654.000px", 11 | "height: 50.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "B7028A6B-8A41-4403-8125-48FF8AECB6A7", 3 | "type": "group", 4 | "className": "MSLayerGroup", 5 | "name": "Conversations", 6 | "index": 2, 7 | "styles": [ 8 | "top: 40.000px", 9 | "left: 40.000px", 10 | "width: 500.000px", 11 | "height: 720.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Add - 0f9cc/Group - 16c23/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "733CD08A-A1F7-44A3-9BC6-9BB04E91C5AA", 3 | "type": "group", 4 | "className": "MSLayerGroup", 5 | "name": "Group", 6 | "index": 1, 7 | "styles": [ 8 | "top: 26.000px", 9 | "left: 26.000px", 10 | "width: 20.000px", 11 | "height: 20.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Top - 45f5e/more - 65862/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "75BB2A0E-2696-43B6-A98E-02DBDE9F7CA2", 3 | "type": "group", 4 | "className": "MSLayerGroup", 5 | "name": "more", 6 | "index": 1, 7 | "styles": [ 8 | "top: 24.000px", 9 | "left: 595.000px", 10 | "width: 6.000px", 11 | "height: 24.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 1 - 4b3bd/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "1171492A-369A-487E-BA79-547F2F129444", 3 | "type": "group", 4 | "className": "MSLayerGroup", 5 | "name": "Conv 1", 6 | "index": 1, 7 | "styles": [ 8 | "top: 72.000px", 9 | "left: 0.000px", 10 | "width: 500.000px", 11 | "height: 108.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 2 - 8544f/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "C43D7B1B-4B09-4EEB-A880-FF31DB06BCB5", 3 | "type": "group", 4 | "className": "MSLayerGroup", 5 | "name": "Conv 2", 6 | "index": 2, 7 | "styles": [ 8 | "top: 180.000px", 9 | "left: 0.000px", 10 | "width: 500.000px", 11 | "height: 108.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 3 - 12538/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "24AA980A-355D-457F-91C1-F1C8942F97AD", 3 | "type": "group", 4 | "className": "MSLayerGroup", 5 | "name": "Conv 3", 6 | "index": 3, 7 | "styles": [ 8 | "top: 288.000px", 9 | "left: 0.000px", 10 | "width: 500.000px", 11 | "height: 108.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 4 - b4e8f/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "71F823C6-641D-4755-A3F0-1637EC783978", 3 | "type": "group", 4 | "className": "MSLayerGroup", 5 | "name": "Conv 4", 6 | "index": 4, 7 | "styles": [ 8 | "top: 396.000px", 9 | "left: 0.000px", 10 | "width: 500.000px", 11 | "height: 108.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 5 - 3a9f7/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "A59F7390-B312-4F82-B83A-4FC245275E26", 3 | "type": "group", 4 | "className": "MSLayerGroup", 5 | "name": "Conv 5", 6 | "index": 5, 7 | "styles": [ 8 | "top: 504.000px", 9 | "left: 0.000px", 10 | "width: 500.000px", 11 | "height: 108.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 6 - 758f0/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "1D1DFA3D-76D8-4AF0-A9B3-3BD04B213253", 3 | "type": "group", 4 | "className": "MSLayerGroup", 5 | "name": "Conv 6", 6 | "index": 6, 7 | "styles": [ 8 | "top: 612.000px", 9 | "left: 0.000px", 10 | "width: 500.000px", 11 | "height: 108.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 - 9eed7/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "7D03F56E-17AA-445B-B742-57A9F05A71F7", 3 | "type": "group", 4 | "className": "MSLayerGroup", 5 | "name": "Group 2", 6 | "index": 8, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 0.000px", 10 | "width: 572.000px", 11 | "height: 102.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Top - 45f5e/RIGHT Title - 4d357/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "91B35D54-ACAD-4334-A6B2-E5CDF22F03BB", 3 | "type": "group", 4 | "className": "MSLayerGroup", 5 | "name": "RIGHT Title", 6 | "index": 2, 7 | "styles": [ 8 | "top: 26.400px", 9 | "left: 24.400px", 10 | "width: 81.000px", 11 | "height: 19.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Type - 1c6b5/favorite - 7deec/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "0F314A3C-42D4-4E3E-8CB5-3AF4085B0C22", 3 | "type": "group", 4 | "className": "MSLayerGroup", 5 | "name": "favorite", 6 | "index": 1, 7 | "styles": [ 8 | "top: 18.000px", 9 | "left: 620.000px", 10 | "width: 18.000px", 11 | "height: 16.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conversations - 9f24e/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "4692802C-C386-446C-BC53-35D2DF10F6DE", 3 | "type": "group", 4 | "className": "MSLayerGroup", 5 | "name": "Conversations", 6 | "index": 7, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 0.000px", 10 | "width: 500.000px", 11 | "height: 72.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 Copy - 44a2d/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "D4732083-CD5C-4AA9-A250-A24592C2DE9D", 3 | "type": "group", 4 | "className": "MSLayerGroup", 5 | "name": "Group 2 Copy", 6 | "index": 7, 7 | "styles": [ 8 | "top: 281.000px", 9 | "left: 0.000px", 10 | "width: 572.000px", 11 | "height: 102.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 Copy 2 - 21e0a/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "46289A72-3F7E-4542-B3BD-5F607EA0D6E3", 3 | "type": "group", 4 | "className": "MSLayerGroup", 5 | "name": "Group 2 Copy 2", 6 | "index": 6, 7 | "styles": [ 8 | "top: 398.000px", 9 | "left: 0.000px", 10 | "width: 572.000px", 11 | "height: 102.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 1 - 4b3bd/LEFT Chat 1 - 06f6e/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "74F702F2-9C69-4682-960D-E3DD81C98609", 3 | "type": "group", 4 | "className": "MSLayerGroup", 5 | "name": "LEFT Chat 1", 6 | "index": 3, 7 | "styles": [ 8 | "top: 37.000px", 9 | "left: 108.000px", 10 | "width: 114.000px", 11 | "height: 38.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 2 - 8544f/LEFT Chat 1 - a2301/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "A954BA8C-1F9A-4113-802B-A036B50E4FD6", 3 | "type": "group", 4 | "className": "MSLayerGroup", 5 | "name": "LEFT Chat 1", 6 | "index": 3, 7 | "styles": [ 8 | "top: 37.000px", 9 | "left: 108.000px", 10 | "width: 231.000px", 11 | "height: 38.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 3 - 12538/LEFT Chat 1 - a96a2/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "FD8DEF58-08E3-4CBA-AA7D-A492628DEAB7", 3 | "type": "group", 4 | "className": "MSLayerGroup", 5 | "name": "LEFT Chat 1", 6 | "index": 3, 7 | "styles": [ 8 | "top: 37.000px", 9 | "left: 108.000px", 10 | "width: 214.000px", 11 | "height: 38.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 4 - b4e8f/LEFT Chat 1 - e5f8b/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "FEC09AD7-4580-4A38-ADB2-A8EB9CD4CDB8", 3 | "type": "group", 4 | "className": "MSLayerGroup", 5 | "name": "LEFT Chat 1", 6 | "index": 3, 7 | "styles": [ 8 | "top: 37.000px", 9 | "left: 108.000px", 10 | "width: 205.000px", 11 | "height: 38.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 5 - 3a9f7/LEFT Chat 1 - 732fd/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "FB32081C-CA0A-4CB7-9B0F-D52505065679", 3 | "type": "group", 4 | "className": "MSLayerGroup", 5 | "name": "LEFT Chat 1", 6 | "index": 3, 7 | "styles": [ 8 | "top: 37.000px", 9 | "left: 108.000px", 10 | "width: 197.000px", 11 | "height: 38.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 6 - 758f0/LEFT Chat 1 - 83c54/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "07C62B75-0565-43DE-881F-DB7447EF2E6C", 3 | "type": "group", 4 | "className": "MSLayerGroup", 5 | "name": "LEFT Chat 1", 6 | "index": 3, 7 | "styles": [ 8 | "top: 37.000px", 9 | "left: 108.000px", 10 | "width: 215.000px", 11 | "height: 38.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/BG - 108af/Rectangle 28 - 73492/rectangle.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "A656F384-F72D-425B-AC1B-AA6048F3B107", 3 | "type": "rectangle", 4 | "className": "MSShapeGroup", 5 | "name": "Rectangle 28", 6 | "index": 1, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 0.000px", 10 | "width: 1280.000px", 11 | "height: 800.000px", 12 | "background: #1F76DC" 13 | ] 14 | } -------------------------------------------------------------------------------- /src/layers/symbolLayer.js: -------------------------------------------------------------------------------- 1 | var _ = require('../util'); 2 | var GeneralLayer = require('./general'); 3 | 4 | function SymbolLayer(layer) { 5 | GeneralLayer.call(this, layer); 6 | } 7 | 8 | SymbolLayer.prototype = Object.create(GeneralLayer.prototype); 9 | SymbolLayer.prototype.constructor = SymbolLayer; 10 | 11 | SymbolLayer.prototype.symbolId = function(){ 12 | return ('' + this._layer.symbolID()); 13 | }; 14 | 15 | module.exports = SymbolLayer; 16 | -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Add - 0f9cc/Rectangle 27 - d1ce8/rectangle.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "1C10E9BA-8091-4B8C-86EC-FFBDBAFA7B05", 3 | "type": "rectangle", 4 | "className": "MSShapeGroup", 5 | "name": "Rectangle 27", 6 | "index": 2, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 0.000px", 10 | "width: 72.000px", 11 | "height: 72.000px", 12 | "background: #36C166" 13 | ] 14 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Combined Shape - 7da6f/Rectangle 29 - e6821/rectangle.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "0D1457A5-778B-483A-B357-95F3933948C4", 3 | "type": "rectangle", 4 | "className": "MSRectangleShape", 5 | "name": "Rectangle 29", 6 | "index": 1, 7 | "styles": [ 8 | "top: 12.719px", 9 | "left: 4.495px", 10 | "width: 10.253px", 11 | "height: 16.959px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/BG - dc734/Rectangle 28 - 34fed/rectangle.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "AAA170B0-40BB-4A7B-BF69-04A6DF43D503", 3 | "type": "rectangle", 4 | "className": "MSShapeGroup", 5 | "name": "Rectangle 28", 6 | "index": 1, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 0.000px", 10 | "width: 702.000px", 11 | "height: 648.000px", 12 | "background: #F1F1F5" 13 | ] 14 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Top - 45f5e/RIGHT TOP Gray - 72f06/rectangle.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "A1914AE8-D221-497C-94A7-9F1F6F4FFE54", 3 | "type": "rectangle", 4 | "className": "MSShapeGroup", 5 | "name": "RIGHT TOP Gray", 6 | "index": 3, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 0.000px", 10 | "width: 702.000px", 11 | "height: 72.000px", 12 | "background: #FBFDFF" 13 | ] 14 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Type - 1c6b5/Rectangle 30 - a532b/rectangle.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "94F59CA1-110A-4A7A-A73E-6956A37439F2", 3 | "type": "rectangle", 4 | "className": "MSShapeGroup", 5 | "name": "Rectangle 30", 6 | "index": 2, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 603.000px", 10 | "width: 1.000px", 11 | "height: 50.000px", 12 | "background: #D7D7E4" 13 | ] 14 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Type - 1c6b5/favorite - 7deec/Shape - 17715/shapePath.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "F0FBED85-0854-4E66-B932-48E89F866575", 3 | "type": "shapePath", 4 | "className": "MSShapeGroup", 5 | "name": "Shape", 6 | "index": 1, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 0.000px", 10 | "width: 18.000px", 11 | "height: 16.000px", 12 | "background: #979797" 13 | ] 14 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Top - 45f5e/more - 65862/Shape - ecce4/combinedShape.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "84DBE114-61D0-4978-AC68-DF9F8F8D87AF", 3 | "type": "combinedShape", 4 | "className": "MSShapeGroup", 5 | "name": "Shape", 6 | "index": 1, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 0.000px", 10 | "width: 6.000px", 11 | "height: 24.000px", 12 | "background: #979797" 13 | ] 14 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Add - 0f9cc/Group - 16c23/Rectangle 27 - e74c0/rectangle.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "2B3DB464-CB13-4FD5-82C2-6238D12D23A7", 3 | "type": "rectangle", 4 | "className": "MSShapeGroup", 5 | "name": "Rectangle 27", 6 | "index": 2, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 9.000px", 10 | "width: 2.000px", 11 | "height: 20.000px", 12 | "background: #FFFFFF" 13 | ] 14 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Add - 0f9cc/Group - 16c23/Rectangle 27 - fa4be/rectangle.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "86EC5803-5543-49F2-B789-A7137ACD1469", 3 | "type": "rectangle", 4 | "className": "MSShapeGroup", 5 | "name": "Rectangle 27", 6 | "index": 1, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 9.000px", 10 | "width: 2.000px", 11 | "height: 20.000px", 12 | "background: #FFFFFF" 13 | ] 14 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Combined Shape - 7da6f/RIGHT 1. Chat Bubble - 33b5a/rectangle.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "EF103094-21F1-424B-94AF-9BC501B808CC", 3 | "type": "rectangle", 4 | "className": "MSRectangleShape", 5 | "name": "RIGHT 1. Chat Bubble", 6 | "index": 2, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 6.033px", 10 | "width: 366.161px", 11 | "height: 149.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 - 9eed7/Combined Shape - 1f6e4/Rectangle 29 - 77018/rectangle.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "0F4BC26A-955F-4F40-AB69-91FC0B53B8A9", 3 | "type": "rectangle", 4 | "className": "MSRectangleShape", 5 | "name": "Rectangle 29", 6 | "index": 1, 7 | "styles": [ 8 | "top: 10.499px", 9 | "left: 2.899px", 10 | "width: 14.000px", 11 | "height: 14.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 Copy - 44a2d/Combined Shape - a53b9/Rectangle 29 - 67824/rectangle.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "40E8BEA1-7140-4A42-B342-5C31F574B400", 3 | "type": "rectangle", 4 | "className": "MSRectangleShape", 5 | "name": "Rectangle 29", 6 | "index": 1, 7 | "styles": [ 8 | "top: 10.499px", 9 | "left: 2.899px", 10 | "width: 14.000px", 11 | "height: 14.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 Copy 2 - 21e0a/Combined Shape - 11274/Rectangle 29 - 1d177/rectangle.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "458BE18F-E16C-4B6D-9B42-3D0F96501EC2", 3 | "type": "rectangle", 4 | "className": "MSRectangleShape", 5 | "name": "Rectangle 29", 6 | "index": 1, 7 | "styles": [ 8 | "top: 10.499px", 9 | "left: 2.899px", 10 | "width: 14.000px", 11 | "height: 14.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 1 - 4b3bd/LEFT 1. Avatar - eedb9/oval.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "31BA9EBB-43D5-40DE-A56A-DD19BF1043AF", 3 | "type": "oval", 4 | "className": "MSShapeGroup", 5 | "name": "LEFT 1. Avatar", 6 | "index": 2, 7 | "styles": [ 8 | "top: 24.000px", 9 | "left: 24.000px", 10 | "width: 60.000px", 11 | "height: 60.000px", 12 | "background-image: url(e69430b.png)" 13 | ] 14 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 2 - 8544f/LEFT 1. Avatar - 42d86/oval.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "6BD32AEC-BD40-4416-AEA9-FAD25FC6715E", 3 | "type": "oval", 4 | "className": "MSShapeGroup", 5 | "name": "LEFT 1. Avatar", 6 | "index": 2, 7 | "styles": [ 8 | "top: 24.000px", 9 | "left: 24.000px", 10 | "width: 60.000px", 11 | "height: 60.000px", 12 | "background-image: url(c84e46f.png)" 13 | ] 14 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 3 - 12538/LEFT 1. Avatar - afa33/oval.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "6FBDF7BA-6BDE-43C1-928E-B3BFC778F28F", 3 | "type": "oval", 4 | "className": "MSShapeGroup", 5 | "name": "LEFT 1. Avatar", 6 | "index": 2, 7 | "styles": [ 8 | "top: 24.000px", 9 | "left: 24.000px", 10 | "width: 60.000px", 11 | "height: 60.000px", 12 | "background-image: url(ebf73ef.png)" 13 | ] 14 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 4 - b4e8f/LEFT 1. Avatar - eb61e/oval.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "381FAFFF-6DA6-4707-8633-3B67E1A8C01C", 3 | "type": "oval", 4 | "className": "MSShapeGroup", 5 | "name": "LEFT 1. Avatar", 6 | "index": 2, 7 | "styles": [ 8 | "top: 24.000px", 9 | "left: 24.000px", 10 | "width: 60.000px", 11 | "height: 60.000px", 12 | "background-image: url(bdff320.png)" 13 | ] 14 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 5 - 3a9f7/LEFT 1. Avatar - b31e8/oval.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "F8CFB498-3155-4D85-AC8F-269ED1558486", 3 | "type": "oval", 4 | "className": "MSShapeGroup", 5 | "name": "LEFT 1. Avatar", 6 | "index": 2, 7 | "styles": [ 8 | "top: 24.000px", 9 | "left: 24.000px", 10 | "width: 60.000px", 11 | "height: 60.000px", 12 | "background-image: url(52acc3b.png)" 13 | ] 14 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 6 - 758f0/LEFT 1. Avatar - acdff/oval.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "21B3545B-57D7-4BEC-A733-D724E1B3F42D", 3 | "type": "oval", 4 | "className": "MSShapeGroup", 5 | "name": "LEFT 1. Avatar", 6 | "index": 2, 7 | "styles": [ 8 | "top: 24.000px", 9 | "left: 24.000px", 10 | "width: 60.000px", 11 | "height: 60.000px", 12 | "background-image: url(0953348.png)" 13 | ] 14 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conversations - 9f24e/LEFT TOP Green - 0340e/rectangle.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "0F89CC5F-5AD3-43D2-823A-20024F279A13", 3 | "type": "rectangle", 4 | "className": "MSShapeGroup", 5 | "name": "LEFT TOP Green", 6 | "index": 2, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 0.000px", 10 | "width: 500.000px", 11 | "height: 72.000px", 12 | "background: #3D3B50" 13 | ] 14 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 - 9eed7/Combined Shape - 1f6e4/RIGHT 1. Chat Bubble - 8cccb/rectangle.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "7357A7A2-4EEE-4E2C-9B03-BF1FBE8780EF", 3 | "type": "rectangle", 4 | "className": "MSRectangleShape", 5 | "name": "RIGHT 1. Chat Bubble", 6 | "index": 2, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 5.000px", 10 | "width: 500.000px", 11 | "height: 101.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 Copy - 44a2d/Combined Shape - a53b9/RIGHT 1. Chat Bubble - b8ed3/rectangle.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "DDCD8AAA-C270-4E81-AEA6-ABF415F7D55D", 3 | "type": "rectangle", 4 | "className": "MSRectangleShape", 5 | "name": "RIGHT 1. Chat Bubble", 6 | "index": 2, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 5.000px", 10 | "width: 500.000px", 11 | "height: 101.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 1 - 4b3bd/LEFT Chat Background 1 - f83a0/rectangle.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "236490E2-D6BB-4E65-9387-87508E11064D", 3 | "type": "rectangle", 4 | "className": "MSShapeGroup", 5 | "name": "LEFT Chat Background 1", 6 | "index": 4, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 0.000px", 10 | "width: 500.000px", 11 | "height: 108.000px", 12 | "background: #413F55" 13 | ] 14 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 2 - 8544f/LEFT Chat Background 1 - 90963/rectangle.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "9734BEFD-7D1F-4B7D-BA0A-EA67CBFFD494", 3 | "type": "rectangle", 4 | "className": "MSShapeGroup", 5 | "name": "LEFT Chat Background 1", 6 | "index": 4, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 0.000px", 10 | "width: 500.000px", 11 | "height: 108.000px", 12 | "background: #413F55" 13 | ] 14 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 3 - 12538/LEFT Chat Background 1 - 30a61/rectangle.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "1858A50F-CCFF-42CD-AE14-0B3BDD93576C", 3 | "type": "rectangle", 4 | "className": "MSShapeGroup", 5 | "name": "LEFT Chat Background 1", 6 | "index": 4, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 0.000px", 10 | "width: 500.000px", 11 | "height: 108.000px", 12 | "background: #413F55" 13 | ] 14 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 4 - b4e8f/LEFT Chat Background 1 - 745cb/rectangle.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "5D5C29D9-D8D3-45FA-9FCE-17F088280343", 3 | "type": "rectangle", 4 | "className": "MSShapeGroup", 5 | "name": "LEFT Chat Background 1", 6 | "index": 4, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 0.000px", 10 | "width: 500.000px", 11 | "height: 108.000px", 12 | "background: #413F55" 13 | ] 14 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 5 - 3a9f7/LEFT Chat Background 1 - 2f121/rectangle.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "07F9719E-46F0-40AC-9E53-10133069A4CE", 3 | "type": "rectangle", 4 | "className": "MSShapeGroup", 5 | "name": "LEFT Chat Background 1", 6 | "index": 4, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 0.000px", 10 | "width: 500.000px", 11 | "height: 108.000px", 12 | "background: #413F55" 13 | ] 14 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 6 - 758f0/LEFT Chat Background 1 - 6fba0/rectangle.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "5AE530F2-6D44-4BAB-AC63-3BEEA73FEB89", 3 | "type": "rectangle", 4 | "className": "MSShapeGroup", 5 | "name": "LEFT Chat Background 1", 6 | "index": 4, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 0.000px", 10 | "width: 500.000px", 11 | "height: 108.000px", 12 | "background: #413F55" 13 | ] 14 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 Copy 2 - 21e0a/Combined Shape - 11274/RIGHT 1. Chat Bubble - 496c5/rectangle.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "F69ED6D8-737D-4C76-8B84-18CDFCE3F1C8", 3 | "type": "rectangle", 4 | "className": "MSRectangleShape", 5 | "name": "RIGHT 1. Chat Bubble", 6 | "index": 2, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 5.000px", 10 | "width: 500.000px", 11 | "height: 101.000px" 12 | ] 13 | } -------------------------------------------------------------------------------- /src/layers/pathLayer.js: -------------------------------------------------------------------------------- 1 | var _ = require('../util'); 2 | var layerUtil = require('../layerUtil'); 3 | var GeneralLayer = require('./general'); 4 | 5 | function PathLayer(layer) { 6 | GeneralLayer.call(this, layer); 7 | } 8 | 9 | PathLayer.prototype = Object.create(GeneralLayer.prototype); 10 | PathLayer.prototype.constructor = PathLayer; 11 | 12 | PathLayer.prototype.path = function(){ 13 | return "" + this._layer.bezierPath().svgPathAttribute(); 14 | }; 15 | 16 | module.exports = PathLayer; 17 | -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Combined Shape - 7da6f/combinedShape.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "15E74DBE-A15F-45D0-93E5-A2FD818A4DE3", 3 | "type": "combinedShape", 4 | "className": "MSShapeGroup", 5 | "name": "Combined Shape", 6 | "index": 4, 7 | "styles": [ 8 | "top: 117.000px", 9 | "left: 216.760px", 10 | "width: 372.194px", 11 | "height: 149.000px", 12 | "border-radius: 4px;", 13 | "background: #FFFFFF" 14 | ] 15 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/RIGHT 1. Avatar - d8723/oval.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "272DB7FD-B108-4095-B6AF-6A3A2CD2034A", 3 | "type": "oval", 4 | "className": "MSShapeGroup", 5 | "name": "RIGHT 1. Avatar", 6 | "index": 5, 7 | "styles": [ 8 | "top: 117.000px", 9 | "left: 611.000px", 10 | "width: 43.200px", 11 | "height: 43.200px", 12 | "background-image: url(bdff320.png)", 13 | "border: 2px solid #FDFDFE" 14 | ] 15 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Type - 1c6b5/RIGHT Buttom Chatbar - 7c3a9/rectangle.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "D3FCD900-FDC3-4344-9F23-393311575FC1", 3 | "type": "rectangle", 4 | "className": "MSShapeGroup", 5 | "name": "RIGHT Buttom Chatbar", 6 | "index": 4, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 0.000px", 10 | "width: 654.000px", 11 | "height: 50.000px", 12 | "background: #FFFFFF", 13 | "border: 1px solid #D7D7E4" 14 | ] 15 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 - 9eed7/Combined Shape - 1f6e4/combinedShape.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "6CBC3041-0D67-40E2-A272-9B1361818560", 3 | "type": "combinedShape", 4 | "className": "MSShapeGroup", 5 | "name": "Combined Shape", 6 | "index": 4, 7 | "styles": [ 8 | "top: 0.400px", 9 | "left: 67.000px", 10 | "width: 505.000px", 11 | "height: 101.000px", 12 | "border-radius: 4px;", 13 | "background: #FFFFFF" 14 | ] 15 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 - 9eed7/RIGHT 1. Avatar - 201cc/oval.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "BB05A0DB-8CF9-44A9-BD03-D1FC488278AA", 3 | "type": "oval", 4 | "className": "MSShapeGroup", 5 | "name": "RIGHT 1. Avatar", 6 | "index": 5, 7 | "styles": [ 8 | "top: 0.400px", 9 | "left: 0.400px", 10 | "width: 43.200px", 11 | "height: 43.200px", 12 | "background-image: url(52acc3b.png)", 13 | "border: 2px solid #FDFDFE" 14 | ] 15 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 Copy - 44a2d/Combined Shape - a53b9/combinedShape.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "A06B04B1-947C-4AD2-B912-504995C73209", 3 | "type": "combinedShape", 4 | "className": "MSShapeGroup", 5 | "name": "Combined Shape", 6 | "index": 4, 7 | "styles": [ 8 | "top: 0.400px", 9 | "left: 67.000px", 10 | "width: 505.000px", 11 | "height: 101.000px", 12 | "border-radius: 4px;", 13 | "background: #FFFFFF" 14 | ] 15 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 Copy - 44a2d/RIGHT 1. Avatar - 56219/oval.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "B0589396-9CD2-4403-B29A-8B4DB4FD54E5", 3 | "type": "oval", 4 | "className": "MSShapeGroup", 5 | "name": "RIGHT 1. Avatar", 6 | "index": 5, 7 | "styles": [ 8 | "top: 0.400px", 9 | "left: 0.400px", 10 | "width: 43.200px", 11 | "height: 43.200px", 12 | "background-image: url(ebf73ef.png)", 13 | "border: 2px solid #FDFDFE" 14 | ] 15 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 Copy 2 - 21e0a/Combined Shape - 11274/combinedShape.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "D84F1240-012D-443A-A573-3EC2E7B33E2A", 3 | "type": "combinedShape", 4 | "className": "MSShapeGroup", 5 | "name": "Combined Shape", 6 | "index": 4, 7 | "styles": [ 8 | "top: 0.400px", 9 | "left: 67.000px", 10 | "width: 505.000px", 11 | "height: 101.000px", 12 | "border-radius: 4px;", 13 | "background: #FFFFFF" 14 | ] 15 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 Copy 2 - 21e0a/RIGHT 1. Avatar - eff24/oval.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "D58FC8F6-99F6-4E9B-A864-F43C52DE7BAC", 3 | "type": "oval", 4 | "className": "MSShapeGroup", 5 | "name": "RIGHT 1. Avatar", 6 | "index": 5, 7 | "styles": [ 8 | "top: 0.400px", 9 | "left: 0.400px", 10 | "width: 43.200px", 11 | "height: 43.200px", 12 | "background-image: url(0953348.png)", 13 | "border: 2px solid #FDFDFE" 14 | ] 15 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Top - 45f5e/more - 65862/Shape - ecce4/Path - 55e89/path.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "4CFB8C5A-8978-459C-95E0-E079A74400AA", 3 | "type": "path", 4 | "className": "MSShapePathLayer", 5 | "name": "Path", 6 | "index": 3, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 0.000px", 10 | "width: 6.000px", 11 | "height: 6.000px" 12 | ], 13 | "path": "d=\"M3,6 C4.65,6 6,4.65 6,3 C6,1.35 4.65,0 3,0 C1.35,0 0,1.35 0,3 C0,4.65 1.35,6 3,6 L3,6 Z\"" 14 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Top - 45f5e/more - 65862/Shape - ecce4/Path - 23d19/path.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "2C1B9089-5244-4038-8732-A31B7F624719", 3 | "type": "path", 4 | "className": "MSShapePathLayer", 5 | "name": "Path", 6 | "index": 2, 7 | "styles": [ 8 | "top: 9.000px", 9 | "left: 0.000px", 10 | "width: 6.000px", 11 | "height: 6.000px" 12 | ], 13 | "path": "d=\"M3,9 C1.35,9 0,10.35 0,12 C0,13.65 1.35,15 3,15 C4.65,15 6,13.65 6,12 C6,10.35 4.65,9 3,9 L3,9 Z\"" 14 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Top - 45f5e/more - 65862/Shape - ecce4/Path - 7edf1/path.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "E013FFD8-3101-4549-9185-48F201691025", 3 | "type": "path", 4 | "className": "MSShapePathLayer", 5 | "name": "Path", 6 | "index": 1, 7 | "styles": [ 8 | "top: 18.000px", 9 | "left: 0.000px", 10 | "width: 6.000px", 11 | "height: 6.000px" 12 | ], 13 | "path": "d=\"M3,18 C1.35,18 0,19.35 0,21 C0,22.65 1.35,24 3,24 C4.65,24 6,22.65 6,21 C6,19.35 4.65,18 3,18 L3,18 Z\"" 14 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Bryan Walters - bc585/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "DACBBD7C-2E91-4806-8675-43BAE11F6F31", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "Bryan Walters", 6 | "index": 2, 7 | "styles": [ 8 | "top: 130.000px", 9 | "left: 232.000px", 10 | "width: 115.000px", 11 | "height: 16.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 14px;", 14 | "color: #C2C2C2;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "content: 'Bryan Walters'" 18 | ] 19 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Type - 1c6b5/favorite - 7deec/Shape - 17715/Path - 6b7b5/path.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "892BDFC3-A9D8-485A-A005-1C239C07179D", 3 | "type": "path", 4 | "className": "MSShapePathLayer", 5 | "name": "Path", 6 | "index": 1, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 0.000px", 10 | "width: 18.000px", 11 | "height: 16.000px" 12 | ], 13 | "path": "d=\"M9,3 C8.33,1.268 6.453,0 4.5,0 C1.957,0 0,1.932 0,4.5 C0,8.029 3.793,10.758 9,16 C14.207,10.758 18,8.029 18,4.5 C18,1.932 16.043,0 13.5,0 C11.545,0 9.67,1.268 9,3 Z\"" 14 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 1 - 4b3bd/11 Feb 2016 - 7c98f/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "6ADFDE16-0CF8-4B5A-B516-096EA6947B8A", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "11 Feb 2016", 6 | "index": 1, 7 | "styles": [ 8 | "top: 46.000px", 9 | "left: 395.000px", 10 | "width: 78.000px", 11 | "height: 16.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 14px;", 14 | "color: #9A98B1;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "content: '11 Feb 2016'" 18 | ] 19 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 2 - 8544f/21 Jul 2016 - 14ad0/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "6F62238F-FE8A-4598-BF37-C7A90F3092FA", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "21 Jul 2016", 6 | "index": 1, 7 | "styles": [ 8 | "top: 46.000px", 9 | "left: 395.000px", 10 | "width: 74.000px", 11 | "height: 16.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 14px;", 14 | "color: #9A98B1;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "content: '21 Jul 2016'" 18 | ] 19 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 3 - 12538/13 Feb 2016 - 4aa06/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "82A431A0-A901-48BB-B791-5B0BF6AC3582", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "13 Feb 2016", 6 | "index": 1, 7 | "styles": [ 8 | "top: 46.000px", 9 | "left: 395.000px", 10 | "width: 78.000px", 11 | "height: 16.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 14px;", 14 | "color: #9A98B1;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "content: '13 Feb 2016'" 18 | ] 19 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 4 - b4e8f/03 Mar 2016 - bb0af/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "FB68E8AD-4EB9-4B45-8E53-3469ADEC5843", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "03 Mar 2016", 6 | "index": 1, 7 | "styles": [ 8 | "top: 46.000px", 9 | "left: 395.000px", 10 | "width: 80.000px", 11 | "height: 16.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 14px;", 14 | "color: #9A98B1;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "content: '03 Mar 2016'" 18 | ] 19 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 5 - 3a9f7/02 Nov 2016 - bf3be/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "0B730BB3-7116-47DF-9851-ABF363BB8DA9", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "02 Nov 2016", 6 | "index": 1, 7 | "styles": [ 8 | "top: 46.000px", 9 | "left: 395.000px", 10 | "width: 80.000px", 11 | "height: 16.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 14px;", 14 | "color: #9A98B1;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "content: '02 Nov 2016'" 18 | ] 19 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 6 - 758f0/09 Nov 2016 - b95ec/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "B8203768-7052-4C34-826F-68378595E74A", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "09 Nov 2016", 6 | "index": 1, 7 | "styles": [ 8 | "top: 46.000px", 9 | "left: 395.000px", 10 | "width: 80.000px", 11 | "height: 16.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 14px;", 14 | "color: #9A98B1;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "content: '09 Nov 2016'" 18 | ] 19 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/RIGHT 1. Seen Text - dd954/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "00243431-8999-4989-A08D-D54B4E8F5E64", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "RIGHT 1. Seen Text", 6 | "index": 1, 7 | "styles": [ 8 | "top: 132.000px", 9 | "left: 505.000px", 10 | "width: 62.000px", 11 | "height: 13.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 11px;", 14 | "color: #C4C4C4;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "content: '23:14 Today'" 18 | ] 19 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Top - 45f5e/RIGHT Title - 4d357/Chat Group - 417d1/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "4C0BE27A-4FD0-4110-9525-FE63162797B6", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "Chat Group", 6 | "index": 1, 7 | "styles": [ 8 | "top: -0.000px", 9 | "left: 0.000px", 10 | "width: 81.000px", 11 | "height: 19.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 16px;", 14 | "color: #000000;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "content: 'Chat Group'" 18 | ] 19 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 - 9eed7/Albert Larson - 0b1a3/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "022AFC91-65D4-47C4-BC57-1A8BF0722F57", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "Albert Larson", 6 | "index": 2, 7 | "styles": [ 8 | "top: 12.000px", 9 | "left: 90.600px", 10 | "width: 115.000px", 11 | "height: 16.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 14px;", 14 | "color: #C2C2C2;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "content: 'Albert Larson'" 18 | ] 19 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 Copy 2 - 21e0a/Kyle Brown - 96afe/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "7534BE10-D4EF-4D45-8C96-7753FE554C7D", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "Kyle Brown", 6 | "index": 2, 7 | "styles": [ 8 | "top: 12.000px", 9 | "left: 90.600px", 10 | "width: 115.000px", 11 | "height: 16.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 14px;", 14 | "color: #C2C2C2;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "content: 'Kyle Brown'" 18 | ] 19 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Type - 1c6b5/Type Something... - 9acce/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "3228694F-7649-458A-B54D-A5EA299B568B", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "Type Something...", 6 | "index": 3, 7 | "styles": [ 8 | "top: 17.000px", 9 | "left: 24.000px", 10 | "width: 114.000px", 11 | "height: 16.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 14px;", 14 | "color: #BFBFBF;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "content: 'Type Something...'" 18 | ] 19 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 Copy - 44a2d/Daniel Santos - 1b991/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "3B4247C2-BDE2-4E7D-9E93-7F7F25228116", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "Daniel Santos", 6 | "index": 2, 7 | "styles": [ 8 | "top: 12.000px", 9 | "left: 90.600px", 10 | "width: 115.000px", 11 | "height: 16.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 14px;", 14 | "color: #C2C2C2;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "content: 'Daniel Santos'" 18 | ] 19 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 1 - 4b3bd/LEFT Chat 1 - 06f6e/Barry Pope - 3fd7f/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "E49F7E65-A526-4169-9A14-A2050681B886", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "Barry Pope", 6 | "index": 1, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 0.000px", 10 | "width: 79.000px", 11 | "height: 19.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 16px;", 14 | "color: #FFFFFF;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "content: 'Barry Pope'" 18 | ] 19 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 3 - 12538/LEFT Chat 1 - a96a2/Larry Park - 3a3b6/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "4E209581-CE63-4334-BD1D-F1601C82C959", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "Larry Park", 6 | "index": 1, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 0.000px", 10 | "width: 74.000px", 11 | "height: 19.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 16px;", 14 | "color: #FFFFFF;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "content: 'Larry Park'" 18 | ] 19 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 6 - 758f0/LEFT Chat 1 - 83c54/Adam Floyd - c0e42/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "E153104D-33AF-4779-B180-5A2BE23261FA", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "Adam Floyd", 6 | "index": 1, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 0.000px", 10 | "width: 86.000px", 11 | "height: 19.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 16px;", 14 | "color: #FFFFFF;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "content: 'Adam Floyd'" 18 | ] 19 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 - 9eed7/RIGHT 1. Seen Text - b6e75/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "52AB11B6-BD1D-4EE0-86FB-BFC521344D3A", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "RIGHT 1. Seen Text", 6 | "index": 1, 7 | "styles": [ 8 | "top: 12.000px", 9 | "left: 498.000px", 10 | "width: 62.000px", 11 | "height: 13.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 11px;", 14 | "color: #C4C4C4;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "content: '23:14 Today'" 18 | ] 19 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 2 - 8544f/LEFT Chat 1 - a2301/Ryan Pittman - dd578/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "87C630AC-C8AB-43AF-B53D-43CFB9D6434E", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "Ryan Pittman", 6 | "index": 1, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 0.000px", 10 | "width: 96.000px", 11 | "height: 19.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 16px;", 14 | "color: #FFFFFF;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "content: 'Ryan Pittman'" 18 | ] 19 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 4 - b4e8f/LEFT Chat 1 - e5f8b/Leon Conner - 987b2/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "BF246D87-FC67-4706-8D6B-C9B8E6A9177C", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "Leon Conner", 6 | "index": 1, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 0.000px", 10 | "width: 91.000px", 11 | "height: 19.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 16px;", 14 | "color: #FFFFFF;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "content: 'Leon Conner'" 18 | ] 19 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 5 - 3a9f7/LEFT Chat 1 - 732fd/Jeremy Long - 2b13e/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "38891888-1BDA-4C25-BBB3-D659A8D16700", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "Jeremy Long", 6 | "index": 1, 7 | "styles": [ 8 | "top: 0.000px", 9 | "left: 0.000px", 10 | "width: 94.000px", 11 | "height: 19.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 16px;", 14 | "color: #FFFFFF;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "content: 'Jeremy Long'" 18 | ] 19 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 Copy - 44a2d/RIGHT 1. Seen Text - 47a2b/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "76B9C2C1-15D9-457E-8784-B9D382EB697A", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "RIGHT 1. Seen Text", 6 | "index": 1, 7 | "styles": [ 8 | "top: 12.000px", 9 | "left: 498.000px", 10 | "width: 62.000px", 11 | "height: 13.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 11px;", 14 | "color: #C4C4C4;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "content: '23:14 Today'" 18 | ] 19 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 Copy 2 - 21e0a/RIGHT 1. Seen Text - 35b70/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "01F66953-C0EA-4A77-8693-495A46B5A374", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "RIGHT 1. Seen Text", 6 | "index": 1, 7 | "styles": [ 8 | "top: 12.000px", 9 | "left: 498.000px", 10 | "width: 62.000px", 11 | "height: 13.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 11px;", 14 | "color: #C4C4C4;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "content: '23:14 Today'" 18 | ] 19 | } -------------------------------------------------------------------------------- /src/layers/groupLayer.js: -------------------------------------------------------------------------------- 1 | var _ = require('../util'); 2 | var mixin = require('./layerMixin'); 3 | var GeneralLayer = require('./general'); 4 | 5 | function GroupLayer(layer) { 6 | GeneralLayer.call(this, layer); 7 | } 8 | 9 | GroupLayer.prototype = Object.create(GeneralLayer.prototype); 10 | GroupLayer.prototype.constructor = GroupLayer; 11 | 12 | GroupLayer.prototype.styles = function(){ 13 | var re = GeneralLayer.prototype.styles.call(this); 14 | re = re.concat(this.cssAttributes()); 15 | re = re.concat(this.shadow()); 16 | return re; 17 | }; 18 | 19 | GroupLayer.prototype.shadow = mixin.exportShadow; 20 | 21 | module.exports = GroupLayer; 22 | -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conversations - 9f24e/Recent Conversations - 06201/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "3DDFE671-C6A3-408E-BAB5-D3CB9F295549", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "Recent Conversations", 6 | "index": 1, 7 | "styles": [ 8 | "top: 27.000px", 9 | "left: 172.000px", 10 | "width: 157.000px", 11 | "height: 19.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 16px;", 14 | "color: #FFFFFF;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "content: 'Recent Conversations'" 18 | ] 19 | } -------------------------------------------------------------------------------- /src/layers/pageLayer.js: -------------------------------------------------------------------------------- 1 | var GeneralLayer = require('./general'); 2 | 3 | function PageLayer(layer) { 4 | GeneralLayer.call(this, layer); 5 | } 6 | 7 | PageLayer.prototype = Object.create(GeneralLayer.prototype); 8 | PageLayer.prototype.constructor = PageLayer; 9 | 10 | PageLayer.prototype.bounds = function(){ 11 | var b = this._layer.contentBounds(); 12 | return { 13 | origin: { 14 | x: parseFloat(b.origin.x).toFixed(3), 15 | y: parseFloat(b.origin.y).toFixed(3) 16 | }, 17 | size: { 18 | width: parseFloat(b.size.width).toFixed(3), 19 | height: parseFloat(b.size.height).toFixed(3) 20 | } 21 | }; 22 | }; 23 | 24 | module.exports = PageLayer; 25 | -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 1 - 4b3bd/LEFT Chat 1 - 06f6e/Please go away… - c07dc/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "0DE75BEB-D4E6-431B-BD62-9248DDDA4F09", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "Please go away…", 6 | "index": 2, 7 | "styles": [ 8 | "top: 23.000px", 9 | "left: 0.000px", 10 | "width: 114.000px", 11 | "height: 15.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 13px;", 14 | "color: #9A98B1;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "letter-spacing: 1.000px", 18 | "content: 'Please go away…'" 19 | ] 20 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 5 - 3a9f7/LEFT Chat 1 - 732fd/Computers are to des - 2d0dd/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "427C9C96-D9E3-4660-8930-F3FD9FBB928D", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "Computers are to des", 6 | "index": 2, 7 | "styles": [ 8 | "top: 23.000px", 9 | "left: 0.000px", 10 | "width: 197.000px", 11 | "height: 15.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 13px;", 14 | "color: #9A98B1;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "letter-spacing: 1.000px", 18 | "content: 'Computers are to design as…'" 19 | ] 20 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 2 - 8544f/LEFT Chat 1 - a2301/At a meta level, des - 4f16a/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "A8FFD225-8963-4E1E-B49C-F11189776BB7", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "At a meta level, des", 6 | "index": 2, 7 | "styles": [ 8 | "top: 23.000px", 9 | "left: 0.000px", 10 | "width: 231.000px", 11 | "height: 15.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 13px;", 14 | "color: #9A98B1;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "letter-spacing: 1.000px", 18 | "content: 'At a meta level, design connects…'" 19 | ] 20 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 3 - 12538/LEFT Chat 1 - a96a2/The only important t - 3162e/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "0D00ED61-CA82-4B40-AF49-138004F9E4ED", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "The only important t", 6 | "index": 2, 7 | "styles": [ 8 | "top: 23.000px", 9 | "left: 0.000px", 10 | "width: 214.000px", 11 | "height: 15.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 13px;", 14 | "color: #9A98B1;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "letter-spacing: 1.000px", 18 | "content: 'The only important thing about.'" 19 | ] 20 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 4 - b4e8f/LEFT Chat 1 - e5f8b/Design is a plan for - 45d32/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "F7432021-391B-47D2-B78A-E965CEAC38B3", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "Design is a plan for", 6 | "index": 2, 7 | "styles": [ 8 | "top: 23.000px", 9 | "left: 0.000px", 10 | "width: 205.000px", 11 | "height: 15.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 13px;", 14 | "color: #9A98B1;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "letter-spacing: 1.000px", 18 | "content: 'Design is a plan for arranging.'" 19 | ] 20 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Conversations - 302c1/Conv 6 - 758f0/LEFT Chat 1 - 83c54/Design is the consci - f832a/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "3BE7502D-B24E-4CCA-A0A6-C7C82E03DB39", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "Design is the consci", 6 | "index": 2, 7 | "styles": [ 8 | "top: 23.000px", 9 | "left: 0.000px", 10 | "width: 215.000px", 11 | "height: 15.000px", 12 | "font-family: .AppleSystemUIFont;", 13 | "font-size: 13px;", 14 | "color: #9A98B1;", 15 | "text-behaviour: auto", 16 | "text-align: left", 17 | "letter-spacing: 1.000px", 18 | "content: 'Design is the conscious effort…'" 19 | ] 20 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | var _ = require('./util'); 2 | var File = require('./file'); 3 | var Sketch = require('./sketch'); 4 | 5 | var global = global || {}; 6 | 7 | function main(type, path) { 8 | global.path = path; 9 | 10 | _.log(">>> Run", new Date); 11 | 12 | var sketch = new Sketch(path); 13 | _.log(sketch.getApplicationPath(), sketch.getVersion(), sketch.getBundleVersion()); 14 | 15 | var doc = sketch.getActiveDocument(); 16 | _.log("Document:", doc.displayName()); 17 | 18 | _.log("path:", path); 19 | File.createDirectory(_.joinPath(path, 'documents')); 20 | 21 | if (type == 'export') { 22 | sketch.export(); 23 | } else { 24 | sketch.import(); 25 | } 26 | } 27 | 28 | main('%TYPE%', '%CURRENT_PATH%'); 29 | -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 - 9eed7/lorem text - 822c9/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "3856E061-D634-42E8-817A-95461A1EEA96", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "lorem text", 6 | "index": 3, 7 | "styles": [ 8 | "top: 45.700px", 9 | "left: 90.600px", 10 | "width: 469.400px", 11 | "height: 44.000px", 12 | "font-family: Helvetica;", 13 | "font-size: 13px;", 14 | "color: #262626;", 15 | "line-height: 22px;", 16 | "text-behaviour: fixed", 17 | "text-align: left", 18 | "letter-spacing: 0.300px", 19 | "content: 'Good design keeps the user happy, the manufacturer in the black and the aesthete unoffended.'" 20 | ] 21 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 Copy - 44a2d/lorem text - 4aa67/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "1BC84390-E52C-426E-A2A2-E9877A474E9D", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "lorem text", 6 | "index": 3, 7 | "styles": [ 8 | "top: 45.700px", 9 | "left: 90.600px", 10 | "width: 469.400px", 11 | "height: 44.000px", 12 | "font-family: Helvetica;", 13 | "font-size: 13px;", 14 | "color: #262626;", 15 | "line-height: 22px;", 16 | "text-behaviour: fixed", 17 | "text-align: left", 18 | "letter-spacing: 0.300px", 19 | "content: 'The design process, at its best, integrates the aspirations of art, science, and culture.'" 20 | ] 21 | } -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/Group 2 Copy 2 - 21e0a/lorem text - b44e0/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "37D6F38A-1CA1-42CB-A95F-B143C69D8118", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "lorem text", 6 | "index": 3, 7 | "styles": [ 8 | "top: 45.700px", 9 | "left: 90.600px", 10 | "width: 469.400px", 11 | "height: 44.000px", 12 | "font-family: Helvetica;", 13 | "font-size: 13px;", 14 | "color: #262626;", 15 | "line-height: 22px;", 16 | "text-behaviour: fixed", 17 | "text-align: left", 18 | "letter-spacing: 0.300px", 19 | "content: 'Truly elegant design incorporates top-notch functionality into a simple, uncluttered form.'" 20 | ] 21 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "open-sketch", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "main.js", 6 | "scripts": { 7 | "build": "browserify src/index.js | sed -E 's/\\[function/[ function/g' | sed -E 's/`([^`]*)`/\\1/g' > bundle.js", 8 | "start": "npm run build && ./coscript main.js", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/SKAhack/open-sketch.git" 14 | }, 15 | "author": "", 16 | "license": "MIT", 17 | "bugs": { 18 | "url": "https://github.com/SKAhack/open-sketch/issues" 19 | }, 20 | "homepage": "https://github.com/SKAhack/open-sketch#readme", 21 | "devDependencies": { 22 | "browserify": "^13.1.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /example/ChatAppFreebie/documents/Page 1 - 00220/Chat Freebie - 3e6e0/Chat - 2422b/Chat - ca269/lorem text - a8189/text.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectId": "DB82EDFC-6B01-4023-8343-11EF236799D8", 3 | "type": "text", 4 | "className": "MSTextLayer", 5 | "name": "lorem text", 6 | "index": 3, 7 | "styles": [ 8 | "top: 162.000px", 9 | "left: 232.000px", 10 | "width: 335.000px", 11 | "height: 88.000px", 12 | "font-family: Helvetica;", 13 | "font-size: 13px;", 14 | "color: #262626;", 15 | "line-height: 22px;", 16 | "text-behaviour: fixed", 17 | "text-align: left", 18 | "letter-spacing: 0.300px", 19 | "content: 'I've always held to the belief that the practice of creating compelling graphic design occurs not by employing the principals of a democracy, but rather, that of a monarchy.'" 20 | ] 21 | } -------------------------------------------------------------------------------- /src/layers/artboardLayer.js: -------------------------------------------------------------------------------- 1 | var _ = require('../util'); 2 | var GeneralLayer = require('./general'); 3 | 4 | function ArtboardLayer(layer) { 5 | GeneralLayer.call(this, layer); 6 | } 7 | 8 | ArtboardLayer.prototype = Object.create(GeneralLayer.prototype); 9 | ArtboardLayer.prototype.constructor = ArtboardLayer; 10 | 11 | ArtboardLayer.prototype.styles = function(){ 12 | var re = GeneralLayer.prototype.styles.call(this); 13 | re = re.concat(this.artboardCssBackground()); 14 | return re; 15 | }; 16 | 17 | ArtboardLayer.prototype.artboardCssBackground = function(){ 18 | var re = new Array(); 19 | 20 | if (this._layer.hasBackgroundColor()) { 21 | var bgColor = this._layer.backgroundColor(); 22 | re.push('background: ' + _.colorToString(bgColor)); 23 | } 24 | 25 | return re; 26 | }; 27 | 28 | module.exports = ArtboardLayer; 29 | -------------------------------------------------------------------------------- /src/layers/symbolMasterLayer.js: -------------------------------------------------------------------------------- 1 | var _ = require('../util'); 2 | var GeneralLayer = require('./general'); 3 | 4 | function SymbolLayer(layer) { 5 | GeneralLayer.call(this, layer); 6 | } 7 | 8 | SymbolLayer.prototype = Object.create(GeneralLayer.prototype); 9 | SymbolLayer.prototype.constructor = SymbolLayer; 10 | 11 | SymbolLayer.prototype.symbolId = function(){ 12 | return ('' + this._layer.symbolID()); 13 | }; 14 | 15 | SymbolLayer.prototype.styles = function(){ 16 | var re = GeneralLayer.prototype.styles.call(this); 17 | re = re.concat(this.background()); 18 | return re; 19 | }; 20 | 21 | SymbolLayer.prototype.background = function(){ 22 | var re = new Array(); 23 | 24 | if (this._layer.hasBackgroundColor()) { 25 | var bgColor = this._layer.backgroundColor(); 26 | re.push('background: ' + _.colorToString(bgColor)); 27 | } 28 | 29 | return re; 30 | }; 31 | 32 | module.exports = SymbolLayer; 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Hiroki Sato 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Open Sketch 2 | 3 | ❗️❗️ This project is deprecated since Sketch 43. ❗️❗️ 4 | 5 | Please read http://sketchplugins.com/d/87-new-file-format-in-sketch-43. 6 | 7 | Open Sketch is sketch to json exporter and json to sketch importer. 8 | 9 | ![Intro](doc/images/intro.gif) 10 | [Chat App Freebie https://dribbble.com/shots/2719193-Chat-App-Freebie](https://dribbble.com/shots/2719193-Chat-App-Freebie) 11 | 12 | :baby_chick: experimental project 13 | 14 | ## Motivation 15 | 16 | Design file can not be use the features of version control system (e.g. diff, merge and collaboration). 17 | 18 | ## Usage 19 | 20 | 1, clone this repository. 21 | 22 | ```sh 23 | $ git clone https://github.com/SKAhack/open-sketch.git 24 | $ cd path/to/open-sketch # pointing at cloned directory 25 | ``` 26 | 27 | 2, npm install. 28 | 29 | ```sh 30 | $ npm install 31 | ``` 32 | 33 | 3, Open your Sketch file. 34 | 35 | 4, Export and Import. 36 | 37 | ```sh 38 | $ npm start 39 | ``` 40 | 41 | 5, Done. Sketch file export to `documents` directory. 42 | 43 | ## Related projects 44 | 45 | - https://kactus.io/ 46 | - https://www.goabstract.com/ 47 | 48 | ## License 49 | MIT 50 | -------------------------------------------------------------------------------- /src/layers/imageLayer.js: -------------------------------------------------------------------------------- 1 | var _ = require('../util'); 2 | var mixin = require('./layerMixin'); 3 | var layerUtil = require('../layerUtil'); 4 | var GeneralLayer = require('./general'); 5 | 6 | function ImageLayer(layer) { 7 | GeneralLayer.call(this, layer); 8 | } 9 | 10 | ImageLayer.prototype = Object.create(GeneralLayer.prototype); 11 | ImageLayer.prototype.constructor = ImageLayer; 12 | 13 | ImageLayer.prototype.styles = function(){ 14 | var re = GeneralLayer.prototype.styles.call(this); 15 | re = re.concat(this.cssAttributes()); 16 | re = re.concat(this.blur()); 17 | re = re.concat(this.shadow()); 18 | return re; 19 | }; 20 | 21 | ImageLayer.prototype.blur = mixin.exportBlur; 22 | ImageLayer.prototype.shadow = mixin.exportShadow; 23 | 24 | ImageLayer.prototype.cssBackgrounds = function(){ 25 | var re = new Array(); 26 | var image = layerUtil.findImage(this.savedImages, this._layer.image()); 27 | re.push('background-image: url(' + _.imageName(image) + ')'); 28 | return re; 29 | }; 30 | 31 | ImageLayer.prototype.images = function(){ 32 | var re = new Array(); 33 | var image = this._layer.image(); 34 | re.push({ 35 | name: _.imageId(image), 36 | image: image.image() 37 | }); 38 | return re; 39 | }; 40 | 41 | module.exports = ImageLayer; 42 | -------------------------------------------------------------------------------- /src/file.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | createDirectory: function(path){ 3 | NSFileManager.defaultManager().createDirectoryAtPath_withIntermediateDirectories_attributes_error(path, !0, {}, null); 4 | }, 5 | 6 | readFileContents: function(path){ 7 | return NSString.stringWithContentsOfFile_encoding_error(path, NSUTF8StringEncoding, nil); 8 | }, 9 | 10 | writeFileContents: function(path, str){ 11 | NSString.stringWithString(str).writeToFile_atomically_encoding_error(path, !0, NSUTF8StringEncoding, nil); 12 | }, 13 | 14 | renameFile: function(from, to){ 15 | NSFileManager.defaultManager().moveItemAtPath_toPath_error(from, to, null); 16 | }, 17 | 18 | removeDirectory: function(path){ 19 | NSFileManager.defaultManager().removeItemAtPath_error(path, null); 20 | }, 21 | 22 | /** 23 | * @param {String} image 24 | * @param {MSImageData} image 25 | */ 26 | saveImage: function(path, image){ 27 | var tiffData = image.TIFFRepresentation(); 28 | var p = NSBitmapImageRep.imageRepWithData(tiffData); 29 | var data = p.representationUsingType_properties(NSPNGFileType, null); 30 | data.writeToFile_atomically(path, !0); 31 | }, 32 | 33 | jsonFilePaths: function(path){ 34 | var filename; 35 | var ds = NSFileManager.defaultManager().enumeratorAtPath(path); 36 | var paths = []; 37 | while (filename = ds.nextObject()) { 38 | if (filename.pathExtension() == 'json') { 39 | paths.push(filename); 40 | } 41 | } 42 | 43 | return paths; 44 | } 45 | }; 46 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | function getFileContents(path) { 2 | return NSString.stringWithContentsOfFile_encoding_error(path, NSUTF8StringEncoding, nil) 3 | } 4 | 5 | function getCurrentPath() { 6 | return NSProcessInfo.processInfo().arguments()[0].stringByDeletingLastPathComponent(); 7 | } 8 | 9 | function splitLines(str) { 10 | return str.match(/[^\r\n]+/g) 11 | } 12 | 13 | function getMergedObject() { 14 | var obj = {}, 15 | i = 0, 16 | il = arguments.length, 17 | key; 18 | for (; i < il; i++) { 19 | for (key in arguments[i]) { 20 | if (arguments[i].hasOwnProperty(key)) { 21 | obj[key] = arguments[i][key]; 22 | } 23 | } 24 | } 25 | return obj; 26 | }; 27 | 28 | function main() { 29 | var appIdentifier = 'com.bohemiancoding.sketch3'; 30 | var pluginSession = "SketchPlugin-" + Date.now(); 31 | 32 | print("Running..."); 33 | 34 | var origPluginCode = getFileContents('bundle.js'); 35 | origPluginCode = origPluginCode.replace(/%CURRENT_PATH%/, getCurrentPath()); 36 | 37 | // Find the Sketch application 38 | sketch = COScript.applicationForIdentifier(appIdentifier) 39 | 40 | if (!sketch) { 41 | throw Error("Cannot find Sketch for " + appIdentifier) 42 | } 43 | 44 | // Parse the output and see what we get 45 | var pluginCode; 46 | var log; 47 | print('Exporting...') 48 | pluginCode = origPluginCode.replace(/%TYPE%/, 'export'); 49 | log = sketch.delegate().runPluginScript_name(pluginCode, pluginSession); 50 | print(log) 51 | 52 | print('Importing...') 53 | pluginCode = origPluginCode.replace(/%TYPE%/, 'import'); 54 | sketch.delegate().openTemplateAtPath(null); 55 | log = sketch.delegate().runPluginScript_name(pluginCode, pluginSession); 56 | print(log) 57 | } 58 | 59 | main() 60 | -------------------------------------------------------------------------------- /src/layer.js: -------------------------------------------------------------------------------- 1 | var _ = require('./util'); 2 | var layerUtil = require('./layerUtil'); 3 | 4 | var GeneralLayer = require('./layers/general'); 5 | var PageLayer = require('./layers/pageLayer'); 6 | var ArtboardLayer = require('./layers/artboardLayer'); 7 | var TextLayer = require('./layers/textLayer'); 8 | var ImageLayer = require('./layers/imageLayer'); 9 | var ShapeGroupLayer = require('./layers/shapeGroupLayer'); 10 | var PathLayer = require('./layers/pathLayer'); 11 | var SymbolMasterLayer = require('./layers/symbolMasterLayer'); 12 | var SymbolLayer = require('./layers/symbolLayer'); 13 | var GroupLayer = require('./layers/groupLayer'); 14 | 15 | function Layer() { } 16 | 17 | Layer.getLayers = function(layers){ 18 | var re = new Array(); 19 | for (var i = 0; i < layers.length; i++) { 20 | re.push(Layer.getLayer(layers[i])); 21 | } 22 | return re; 23 | }; 24 | 25 | Layer.getLayer = function(layer){ 26 | var type = layerUtil.getType(layer); 27 | if (type === 'page') { 28 | return new PageLayer(layer); 29 | } else if (type === 'artboard') { 30 | return new ArtboardLayer(layer); 31 | } else if (type === 'symbolMaster') { 32 | return new SymbolMasterLayer(layer); 33 | } else if (type === 'symbol') { 34 | return new SymbolLayer(layer); 35 | } else if (type === 'text') { 36 | return new TextLayer(layer); 37 | } else if (type === 'image') { 38 | return new ImageLayer(layer); 39 | } else if (_.isContains(['oval', 'rectangle', 'shapePath', 'combinedShape'], type)) { 40 | return new ShapeGroupLayer(layer); 41 | } else if (type === 'path') { 42 | return new PathLayer(layer); 43 | } else if (type === 'group') { 44 | return new GroupLayer(layer); 45 | } else { 46 | return new GeneralLayer(layer); 47 | } 48 | }; 49 | 50 | module.exports = Layer; 51 | -------------------------------------------------------------------------------- /src/layers/layerMixin.js: -------------------------------------------------------------------------------- 1 | var _ = require('../util'); 2 | 3 | module.exports = { 4 | exportBlur: function() { 5 | var re = new Array(); 6 | if (!this._layer.styleGeneric) { 7 | return re; 8 | } 9 | 10 | var blur = this._layer.styleGeneric().blur(); 11 | if (blur.isEnabled()) { 12 | var rad = blur.radius(); 13 | var angle = blur.motionAngle(); 14 | var type = _.blurTypeToString(blur.type()); 15 | 16 | if (_.isContains(['gaussian', 'background'], type)) { 17 | re.push('filter: ' + type + '-blur(' + rad + 'px)'); 18 | } else { 19 | re.push('filter: ' + type + '-blur(' + rad + 'px ' + angle + 'deg)'); 20 | } 21 | } 22 | return re; 23 | }, 24 | 25 | exportShadow: function() { 26 | var re = new Array(); 27 | var ss = new Array(); 28 | 29 | var shadows = this._layer.style().shadows(); 30 | for (var i = 0; i < shadows.length; i++) { 31 | var shadow = shadows[i]; 32 | ss.push('' + 33 | shadow.offsetX() + 'px ' + 34 | shadow.offsetY() + 'px ' + 35 | shadow.blurRadius() + 'px ' + 36 | shadow.spread() + 'px ' + 37 | _.colorToString(shadow.color()) + 38 | (shadow.isEnabled() ? '' : ' none')); 39 | } 40 | 41 | shadows = this._layer.style().innerShadows(); 42 | for (var i = 0; i < shadows.length; i++) { 43 | var shadow = shadows[i]; 44 | ss.push('inset ' + 45 | shadow.offsetX() + 'px ' + 46 | shadow.offsetY() + 'px ' + 47 | shadow.blurRadius() + 'px ' + 48 | shadow.spread() + 'px ' + 49 | _.colorToString(shadow.color()) + 50 | (shadow.isEnabled() ? '' : ' none')); 51 | } 52 | 53 | if (ss.length > 0) { 54 | re.push('box-shadow: ' + ss.join(', ')); 55 | } 56 | 57 | return re; 58 | } 59 | }; 60 | -------------------------------------------------------------------------------- /src/layerUtil.js: -------------------------------------------------------------------------------- 1 | var _ = require('./util'); 2 | 3 | module.exports = { 4 | getType: function(layer){ 5 | var layers = new Array(); 6 | if (layer.layers) { 7 | layers = _.toArray(layer.layers()); 8 | } 9 | 10 | if (layer.className() == "MSPage") { 11 | return 'page'; 12 | } else if (layer.className() == "MSArtboardGroup") { 13 | return 'artboard'; 14 | } else if (layer.className() == "MSLayerGroup") { 15 | return 'group'; 16 | } else if (layer.className() == "MSTextLayer") { 17 | return 'text'; 18 | } else if (layer.className() == "MSSliceLayer") { 19 | return 'slice'; 20 | } else if (layer.className() == "MSBitmapLayer") { 21 | return 'image'; 22 | } else if (layer.className() == "MSShapeGroup" && layers.length === 1) { 23 | if (layers[0].className() == "MSOvalShape") { 24 | return 'oval'; 25 | } else if (layers[0].className() == "MSRectangleShape") { 26 | return 'rectangle'; 27 | } else if (layers[0].className() == "MSShapePathLayer") { 28 | return 'shapePath'; 29 | } else { 30 | } 31 | } else if (layer.className() == "MSShapeGroup" && layers.length > 1) { 32 | return 'combinedShape'; 33 | } else if (layer.className() == "MSOvalShape") { 34 | return 'oval'; 35 | } else if (layer.className() == "MSRectangleShape") { 36 | return 'rectangle'; 37 | } else if (layer.className() == "MSShapePathLayer") { 38 | return 'path'; 39 | } else if (layer.className() == "MSSymbolInstance") { 40 | return 'symbol'; 41 | } else if (layer.className() == "MSSymbolMaster") { 42 | return 'symbolMaster'; 43 | } 44 | return 'layer'; 45 | }, 46 | 47 | findImage: function(savedImages, image) { 48 | for (var i = 0; i < savedImages.length; i++) { 49 | if (savedImages[i].name == _.imageId(image)) { 50 | return savedImages[i]; 51 | } 52 | } 53 | return null; 54 | } 55 | }; 56 | -------------------------------------------------------------------------------- /src/layers/textLayer.js: -------------------------------------------------------------------------------- 1 | var mixin = require('./layerMixin'); 2 | var GeneralLayer = require('./general'); 3 | var _ = require('../util'); 4 | 5 | function TextLayer(layer) { 6 | GeneralLayer.call(this, layer); 7 | } 8 | 9 | TextLayer.prototype = Object.create(GeneralLayer.prototype); 10 | TextLayer.prototype.constructor = TextLayer; 11 | 12 | TextLayer.prototype.styles = function() { 13 | var re = GeneralLayer.prototype.styles.call(this); 14 | re = re.concat(this.cssAttributes()); 15 | re = re.concat(this.cssText()); 16 | re = re.concat(this.blur()); 17 | re = re.concat(this.shadow()); 18 | return re; 19 | }; 20 | 21 | TextLayer.prototype.blur = mixin.exportBlur; 22 | 23 | TextLayer.prototype.shadow = mixin.exportShadow; 24 | 25 | TextLayer.prototype.cssText = function(){ 26 | var re = new Array(); 27 | if (this.className() == "MSTextLayer") { 28 | // Text Behaviour: 0:auto 1:fixed 29 | re.push("text-behaviour: " + (this._layer.textBehaviour() == 0 ? 'auto' : 'fixed')); 30 | 31 | // Text Align 32 | var textAlign; 33 | if (this._layer.textAlignment() == 0) { 34 | textAlign = 'left'; 35 | } else if (this._layer.textAlignment() == 1) { 36 | textAlign = 'right'; 37 | } else if (this._layer.textAlignment() == 2) { 38 | textAlign = 'center'; 39 | } 40 | re.push("text-align: " + textAlign); 41 | 42 | // Letter Spacing 43 | if (this._layer.characterSpacing() && this._layer.characterSpacing() > 0) { 44 | re.push("letter-spacing: " + parseFloat(this._layer.characterSpacing()).toFixed(3) + 'px'); 45 | } 46 | 47 | // Content 48 | re.push("content: '" + this.stringValue() + "'"); 49 | 50 | var attrs = this._layer.styleAttributes(); 51 | 52 | // Underline 53 | if (attrs.NSUnderline > 0) { 54 | re.push("text-decoration: " + _.underlineNumberToString(attrs.NSUnderline)); 55 | } 56 | 57 | // Line through 58 | if (attrs.NSStrikethrough > 0) { 59 | re.push("text-decoration: line-through"); 60 | } 61 | } 62 | return re; 63 | }; 64 | 65 | module.exports = TextLayer; 66 | -------------------------------------------------------------------------------- /src/sketch.js: -------------------------------------------------------------------------------- 1 | var _ = require('./util'); 2 | var File = require('./file'); 3 | var Layer = require('./layer'); 4 | var Importer = require('./importer'); 5 | 6 | function Sketch(path) { 7 | this.path = path; 8 | } 9 | 10 | Sketch.prototype.getApplication = function(){ 11 | this.app = this.app || NSApplication.sharedApplication(); 12 | return this.app; 13 | }; 14 | 15 | Sketch.prototype.getApplicationPath = function(){ 16 | return NSBundle.mainBundle().bundlePath(); 17 | }; 18 | 19 | Sketch.prototype.getVersion = function(){ 20 | var t = NSBundle.mainBundle().infoDictionary(); 21 | return t.CFBundleShortVersionString; 22 | }; 23 | 24 | Sketch.prototype.getBundleVersion = function(){ 25 | var t = NSBundle.mainBundle().infoDictionary(); 26 | return t.CFBundleVersion; 27 | }; 28 | 29 | Sketch.prototype.getActiveDocument = function(){ 30 | var app = this.getApplication(); 31 | var t = _.toArray(app.orderedDocuments()); 32 | var r = t.filter(function(app){ 33 | return !app.window().isMiniaturized(); 34 | }); 35 | 36 | if (r.length > 0) return r[0]; 37 | if (t.length > 0) return t[0]; 38 | throw Error("Could not find open Sketch document"); 39 | }; 40 | 41 | Sketch.prototype.export = function(){ 42 | var doc = this.getActiveDocument(); 43 | var pages = Layer.getLayers(doc.pages()); 44 | var path = _.joinPath(this.path, 'documents'); 45 | _.log("Page length:", pages.length); 46 | 47 | File.removeDirectory(path); 48 | // export pages 49 | for (var i = 0; i < pages.length; i++) { 50 | exportLayer(path, pages[i], pages.length - i, {}); 51 | } 52 | }; 53 | 54 | Sketch.prototype.import = function(){ 55 | var importer = new Importer(this.getActiveDocument(), this.path); 56 | importer.import(); 57 | }; 58 | 59 | function exportLayer(path, layer, index, parent) { 60 | if (parent.type == 'shapePath' && layer.path() === '') { 61 | return; 62 | } 63 | 64 | if (parent.type == 'oval' && layer.path() === '') { 65 | return; 66 | } 67 | 68 | if (parent.type == 'rectangle' && layer.path() === '') { 69 | return; 70 | } 71 | 72 | var path = _.joinPath(path, layer.dirName()); 73 | File.createDirectory(path); 74 | 75 | saveImages(layer, path); 76 | 77 | var json = { 78 | objectId: layer.id(), 79 | type: layer.type(), 80 | className: layer.className(), 81 | name: layer.name(), 82 | index: index, 83 | styles: layer.styles() 84 | }; 85 | var shapePath = layer.path(); 86 | if (shapePath !== '') { 87 | json.path = shapePath; 88 | } 89 | if (layer.symbolId()) { 90 | json.symbolId = layer.symbolId(); 91 | } 92 | File.writeFileContents(_.joinPath(path, layer.type() + '.json'), _.getJSON(json)); 93 | 94 | var layers = Layer.getLayers(layer.layers()); 95 | layer.setLayers(layers); 96 | 97 | if (layers.length > 0) { 98 | for (var i = 0; i < layers.length; i++) { 99 | exportLayer(path, layers[i], layers.length - i, json); 100 | } 101 | } 102 | } 103 | 104 | function saveImages(layer, path) { 105 | var images = layer.images(); 106 | for (var i = 0; i < images.length; i++) { 107 | var fromPath = _.joinPath(path, images[i].name); 108 | File.saveImage(fromPath, images[i].image); 109 | images[i].sha1 = _.shasum(fromPath); 110 | var toPath = _.joinPath(path, _.imageName(images[i])); 111 | File.renameFile(fromPath, toPath); 112 | } 113 | layer.setSavedImages(images); 114 | } 115 | 116 | module.exports = Sketch; 117 | -------------------------------------------------------------------------------- /src/layers/general.js: -------------------------------------------------------------------------------- 1 | var _ = require('../util'); 2 | var layerUtil = require('../layerUtil'); 3 | 4 | function GeneralLayer(layer) { 5 | this._layer = layer; 6 | } 7 | 8 | GeneralLayer.prototype.id = function(){ 9 | return '' + this._layer.objectID(); 10 | }; 11 | 12 | GeneralLayer.prototype.shortId = function(){ 13 | return ('' + this._layer.objectID().sha1()).substr(0, 5); 14 | }; 15 | 16 | GeneralLayer.prototype.symbolId = function(){ 17 | return null; 18 | }; 19 | 20 | GeneralLayer.prototype.dirName = function(){ 21 | return (this.name() + " - " + this.shortId()).replace(new RegExp('/'), ":"); 22 | }; 23 | 24 | GeneralLayer.prototype.name = function(){ 25 | return '' + this._layer.name(); 26 | }; 27 | 28 | GeneralLayer.prototype.className = function(){ 29 | return '' + this._layer.className(); 30 | }; 31 | 32 | GeneralLayer.prototype.stringValue = function(){ 33 | return '' + this._layer.stringValue(); 34 | }; 35 | 36 | GeneralLayer.prototype.layers = function(){ 37 | var re = new Array(); 38 | if (this._layer.layers) { 39 | re = this._layer.layers(); 40 | } 41 | 42 | return re; 43 | }; 44 | 45 | GeneralLayer.prototype.setLayers = function(layers){ 46 | this._layers = layers; 47 | }; 48 | 49 | GeneralLayer.prototype.type = function(){ 50 | return layerUtil.getType(this._layer); 51 | }; 52 | 53 | GeneralLayer.prototype.bounds = function(){ 54 | var b = this._layer.frame(); 55 | return { 56 | origin: { 57 | x: parseFloat(b.x()).toFixed(3), 58 | y: parseFloat(b.y()).toFixed(3) 59 | }, 60 | size: { 61 | width: parseFloat(b.width()).toFixed(3), 62 | height: parseFloat(b.height()).toFixed(3) 63 | } 64 | }; 65 | }; 66 | 67 | GeneralLayer.prototype.styles = function(){ 68 | var re = []; 69 | 70 | if (!this._layer.isVisible()) { 71 | re.push("display: none"); 72 | } 73 | 74 | if (this._layer.isLocked()) { 75 | re.push("lock: true"); 76 | } 77 | 78 | var bounds = this.bounds(); 79 | re.push('top: ' + bounds.origin.y + 'px'); 80 | re.push('left: ' + bounds.origin.x + 'px'); 81 | re.push('width: ' + bounds.size.width + 'px'); 82 | re.push('height: ' + bounds.size.height + 'px'); 83 | 84 | re = re.concat(this.rotation()); 85 | re = re.concat(this.blendMode()); 86 | 87 | return re; 88 | }; 89 | 90 | GeneralLayer.prototype.rotation = function(){ 91 | var re = new Array(); 92 | var rotation = this._layer.rotation(); 93 | if (rotation > 0) { 94 | re.push('transform: rotate(' + rotation + 'deg)'); 95 | } 96 | return re; 97 | }; 98 | 99 | GeneralLayer.prototype.cssAttributes = function(){ 100 | var re = new Array(); 101 | var styles = this._layer.CSSAttributes(); 102 | 103 | for (var i = 0; i < styles.length; i++) { 104 | var s = "" + styles[i]; 105 | if ((new RegExp('^\/\\* .* \\*\/')).test(s) || 106 | (new RegExp('^background:')).test(s) || 107 | (new RegExp('^border:')).test(s) || 108 | (new RegExp('^letter-spacing:')).test(s) || 109 | (new RegExp('^box-shadow:')).test(s) || 110 | (new RegExp("linear-gradient")).test(s)) { 111 | continue; 112 | } 113 | re.push(s); 114 | } 115 | 116 | re = re.concat(this.cssBackgrounds()); 117 | re = re.concat(this.cssBorders()); 118 | 119 | return re; 120 | }; 121 | 122 | GeneralLayer.prototype.cssBackgrounds = function(){ 123 | return new Array(); 124 | }; 125 | 126 | GeneralLayer.prototype.cssBorders = function(){ 127 | var re = new Array(); 128 | 129 | if (!this._layer.styleGeneric().hasEnabledBorder()) { 130 | return re; 131 | } 132 | 133 | var borders = this._layer.style().borders(); 134 | for (var i = 0; i < borders.length; i++) { 135 | var color = borders[i].color(); 136 | var s = "border: " + borders[i].thickness() + 'px solid ' + _.colorToString(color); 137 | if (!borders[i].isEnabled()) { 138 | s += ' none' 139 | } 140 | re.push(s); 141 | } 142 | 143 | return re; 144 | }; 145 | 146 | GeneralLayer.prototype.blendMode = function(){ 147 | var re = new Array(); 148 | 149 | if (this._layer.style) { 150 | var mode = this._layer.style().contextSettings().blendMode(); 151 | if (mode > 0) { 152 | re.push('blend-mode: ' + _.blendModeNumberToString(mode)); 153 | } 154 | } 155 | 156 | return re; 157 | }; 158 | 159 | GeneralLayer.prototype.images = function(){ 160 | return new Array(); 161 | }; 162 | 163 | GeneralLayer.prototype.setSavedImages = function(images){ 164 | this.savedImages = images; 165 | }; 166 | 167 | GeneralLayer.prototype.path = function(){ 168 | return ""; 169 | }; 170 | 171 | module.exports = GeneralLayer; 172 | -------------------------------------------------------------------------------- /src/layers/shapeGroupLayer.js: -------------------------------------------------------------------------------- 1 | var _ = require('../util'); 2 | var mixin = require('./layerMixin'); 3 | var layerUtil = require('../layerUtil'); 4 | var GeneralLayer = require('./general'); 5 | 6 | function ShapeGroupLayer(layer) { 7 | GeneralLayer.call(this, layer); 8 | } 9 | 10 | ShapeGroupLayer.prototype = Object.create(GeneralLayer.prototype); 11 | ShapeGroupLayer.prototype.constructor = ShapeGroupLayer; 12 | 13 | ShapeGroupLayer.prototype.styles = function(){ 14 | var re = GeneralLayer.prototype.styles.call(this); 15 | 16 | if (this.className() != "MSShapeGroup") { 17 | re = re.concat(this.booleanOperation()); 18 | return re; 19 | } 20 | 21 | re = re.concat(this.cssAttributes()); 22 | re = re.concat(this.blur()); 23 | re = re.concat(this.shadow()); 24 | if (this._layer.hasClippingMask() === 1) { 25 | re.push('mask: initial;'); 26 | } 27 | 28 | return re; 29 | }; 30 | 31 | ShapeGroupLayer.prototype.booleanOperation = function(){ 32 | var re = new Array(); 33 | var operationStr = ''; 34 | var operation = this._layer.booleanOperation(); 35 | if (operation < 0) { 36 | return re; 37 | } 38 | 39 | if (operation === 0) { 40 | operationStr = 'union'; 41 | } else if (operation === 1) { 42 | operationStr = 'subtract'; 43 | } else if (operation === 2) { 44 | operationStr = 'intersect'; 45 | } else if (operation === 3) { 46 | operationStr = 'difference'; 47 | } 48 | 49 | re.push('boolean-operation: ' + operationStr); 50 | 51 | return re; 52 | }; 53 | 54 | ShapeGroupLayer.prototype.blur = mixin.exportBlur; 55 | 56 | ShapeGroupLayer.prototype.shadow = mixin.exportShadow; 57 | 58 | ShapeGroupLayer.prototype.cssBackgrounds = function(){ 59 | var re = new Array(); 60 | 61 | if (this.className() != "MSShapeGroup") { 62 | return re; 63 | } 64 | 65 | var s; 66 | var fills = this._layer.styleGeneric().fills(); 67 | var type = { 68 | backgrounds: [], 69 | backgroundImages: [], 70 | linearGradient: [], 71 | }; 72 | for (var i = 0; i < fills.length; i++) { 73 | s = undefined; 74 | 75 | if (fills[i].fillType() == 0) { 76 | s = '' + _.colorToString(fills[i].color()); 77 | } else if (fills[i].fillType() == 1) { 78 | s = 'linear-gradient(' 79 | + getGradientString(fills[i].gradient(), fills[i].CSSAttributeString()) + ')'; 80 | } else if (fills[i].fillType() == 4) { 81 | var image = layerUtil.findImage(this.savedImages, fills[i].image()); 82 | s = 'url(' +_.imageName(image) + ')'; 83 | } 84 | 85 | if (s) { 86 | var fillStyle = fills[i].contextSettings(); 87 | var blendMode = fillStyle.blendMode(); 88 | var opacity = fillStyle.opacity(); 89 | 90 | if (blendMode > 0) { 91 | s += ' blend-mode(' + _.blendModeNumberToString(blendMode) + ')'; 92 | } 93 | 94 | if (opacity < 1) { 95 | s += ' opacity(' + opacity + ')'; 96 | } 97 | 98 | if (!fills[i].isEnabled()) { 99 | s += ' none'; 100 | } 101 | 102 | if (fills[i].fillType() == 0) { 103 | type.backgrounds.push(s); 104 | } else if (fills[i].fillType() == 1) { 105 | type.backgroundImages.push(s); 106 | } else if (fills[i].fillType() == 4) { 107 | type.backgroundImages.push(s); 108 | } 109 | } 110 | } 111 | 112 | if (type.backgrounds.length > 0) { 113 | re.push('background: ' + type.backgrounds.join(', ')); 114 | } 115 | if (type.backgroundImages.length > 0) { 116 | re.push('background-image: ' + type.backgroundImages.join(', ')); 117 | } 118 | 119 | return re; 120 | }; 121 | 122 | ShapeGroupLayer.prototype.images = function(){ 123 | var re = new Array(); 124 | 125 | if (this.className() != "MSShapeGroup") { 126 | return re; 127 | } 128 | 129 | var fills = this._layer.styleGeneric().fills(); 130 | for (var i = 0; i < fills.length; i++) { 131 | if (fills[i].fillType() == 4) { 132 | var image = fills[i].image(); 133 | re.push({ 134 | name: _.imageId(image), 135 | image: image.image() 136 | }); 137 | } 138 | } 139 | 140 | return re; 141 | }; 142 | 143 | /** 144 | * @param {MSGradient} gradient 145 | */ 146 | function getGradientString(gradient, css) { 147 | var from = gradient.from(); 148 | var to = gradient.to(); 149 | 150 | var res = []; 151 | res.push(from.x + ' ' + from.y + ' ' + to.x + ' ' + to.y); 152 | _.toArray(gradient.stops()).forEach(function(s){ 153 | var color = s.color(); 154 | res.push(_.colorToString(color) + ' ' + s.position()); 155 | }); 156 | return res.join(', '); 157 | } 158 | 159 | module.exports = ShapeGroupLayer; 160 | -------------------------------------------------------------------------------- /src/util.js: -------------------------------------------------------------------------------- 1 | function stripSlashes(t) { 2 | return t.replace(/\/$/, ""); 3 | } 4 | 5 | var blurMap = { 6 | 0: 'gaussian', 7 | 1: 'motion', 8 | 2: 'zoom', 9 | 3: 'background', 10 | }; 11 | 12 | var booleanOperationMap = { 13 | 0: 'union', 14 | 1: 'subtract', 15 | 2: 'intersect', 16 | 3: 'difference' 17 | }; 18 | 19 | var blendModeMap = { 20 | 0: 'normal', 21 | 1: 'darken', 22 | 2: 'multiply', 23 | 3: 'colorBurn', 24 | 4: 'lighten', 25 | 5: 'screen', 26 | 6: 'colorDodge', 27 | 7: 'overlay', 28 | 8: 'softLight', 29 | 9: 'hardLight', 30 | 10: 'difference', 31 | 11: 'exclusion', 32 | 12: 'hue', 33 | 13: 'saturation', 34 | 14: 'color', 35 | 15: 'lumiosity' 36 | }; 37 | 38 | var underlineMap = { 39 | 0: 'normal', 40 | 1: 'underline', 41 | 9: 'double-underline' 42 | }; 43 | 44 | module.exports = { 45 | log: function(){ 46 | var t = Array.prototype.slice.call(arguments); 47 | print(t.join(" ")); 48 | }, 49 | 50 | isNull: function(obj){ 51 | return obj == null || Object.prototype.toString.call(obj) == '[object Null]'; 52 | }, 53 | 54 | toArray: function(t){ 55 | for (var n = t.count(), r = [], e = 0; n > e; e++) { 56 | r.push(t.objectAtIndex(e)); 57 | } 58 | return r; 59 | }, 60 | 61 | joinPath: function(){ 62 | return Array.prototype.slice.call(arguments).map(stripSlashes).join("/"); 63 | }, 64 | 65 | getJSON: function(t){ 66 | return JSON.stringify(t, null, " "); 67 | }, 68 | 69 | dataToHexdecimal: function(data){ 70 | return data.sha1AsString(); 71 | }, 72 | 73 | sha1: function(str){ 74 | var s = NSString.alloc().initWithString(str); 75 | return s.sha1(); 76 | }, 77 | 78 | /** 79 | * @see sketch.js saveImages 80 | */ 81 | imageName: function(image) { 82 | return image.sha1.substr(0,7) + '.png'; 83 | }, 84 | 85 | /** 86 | * @param {MSImageData} image 87 | */ 88 | imageId: function(image) { 89 | return '' + image.sha1().sha1AsString(); 90 | }, 91 | 92 | system: function(path, args){ 93 | if (!args) { 94 | args = []; 95 | } 96 | var task = NSTask.alloc().init(); 97 | task.launchPath = path; 98 | task.arguments = args; 99 | var stdout = NSPipe.pipe(); 100 | task.standardOutput = stdout; 101 | task.launch(); 102 | task.waitUntilExit(); 103 | var data = stdout.fileHandleForReading().readDataToEndOfFile(); 104 | 105 | return NSString.alloc().initWithData_encoding(data, NSUTF8StringEncoding); 106 | }, 107 | 108 | shasum: function(path) { 109 | var out = '' + this.system('/usr/bin/shasum', [path]); 110 | return out.substr(0, 40); 111 | }, 112 | 113 | /** 114 | * @param {MSColor} color 115 | */ 116 | colorToString: function(color) { 117 | var c = color.RGBADictionary(); 118 | var color = MSImmutableColor.colorWithRed_green_blue_alpha(c.r, c.g, c.b, c.a); 119 | return '' + color.stringValueWithAlpha(true); 120 | }, 121 | 122 | stringToColor: function(str) { 123 | return MSImmutableColor.colorWithSVGString(str); 124 | }, 125 | 126 | isContains: function(array, item) { 127 | for (var i = 0; i < array.length; i++) { 128 | if (array[i] === item) { 129 | return true; 130 | } 131 | } 132 | return false; 133 | }, 134 | 135 | booleanOperationToNumber: function(str) { 136 | var keys = Object.keys(booleanOperationMap); 137 | for (var i = 0; i < keys.length; i++) { 138 | var key = keys[i]; 139 | if (booleanOperationMap[key] === str) { 140 | return i; 141 | } 142 | } 143 | throw new Error('Unknow booleanOperation type. type=' + str); 144 | }, 145 | 146 | blurTypeToString: function(num) { 147 | if (blurMap[num]) { 148 | return blurMap[num]; 149 | } 150 | throw new Error('Unknow blur type. type=' + str); 151 | }, 152 | 153 | blurTypeToNumber: function(str) { 154 | var keys = Object.keys(blurMap); 155 | for (var i = 0; i < keys.length; i++) { 156 | var key = keys[i]; 157 | if (blurMap[key] === str) { 158 | return i; 159 | } 160 | } 161 | throw new Error('Unknow blur type. type=' + str); 162 | }, 163 | 164 | blendModeNumberToString: function(num) { 165 | if (blendModeMap[num]) { 166 | return blendModeMap[num]; 167 | } 168 | throw new Error('Unknow blendMode type. type=' + num); 169 | }, 170 | 171 | blendModeToNumber: function(str) { 172 | var keys = Object.keys(blendModeMap); 173 | for (var i = 0; i < keys.length; i++) { 174 | var key = keys[i]; 175 | if (blendModeMap[key] === str) { 176 | return i; 177 | } 178 | } 179 | throw new Error('Unknow blendMode type. type=' + str); 180 | }, 181 | 182 | underlineNumberToString: function(num) { 183 | if (underlineMap[num]) { 184 | return underlineMap[num]; 185 | } 186 | throw new Error('Unknow underline type. type=' + num); 187 | }, 188 | 189 | underlineToNumber: function(str) { 190 | var keys = Object.keys(underlineMap); 191 | for (var i = 0; i < keys.length; i++) { 192 | var key = keys[i]; 193 | if (underlineMap[key] === str) { 194 | return parseInt(key); 195 | } 196 | } 197 | throw new Error('Unknow underline type. type=' + str); 198 | } 199 | }; 200 | -------------------------------------------------------------------------------- /src/importer.js: -------------------------------------------------------------------------------- 1 | var _ = require('./util'); 2 | var File = require('./file'); 3 | 4 | var Re = { 5 | color: "#[0-9A-F]{6}|rgba\\([0-9,.]+\\)" 6 | }; 7 | 8 | var positionRegex = new RegExp('^(top|left|width|height): ([\\d.-]+)px'); 9 | var borderRegex = new RegExp('^border: ([0-9.]+)px solid (#[0-9A-F]{6}|rgba\\([0-9,.]+\\))( none)?;?'); 10 | var borderRadiusRegex = new RegExp('^border-radius: ([\\d.]+)px;?'); 11 | var contentRegex = new RegExp("^content: '((.|[\n])*)'"); 12 | var fontFamilyRegex = new RegExp("^font-family: ([^;]*);?"); 13 | var fontSizeRegex = new RegExp("^font-size: (\\d+)px;?"); 14 | var colorRegex = new RegExp("^color: (#[0-9A-F]{6});?"); 15 | var opacityRegex = new RegExp("^opacity: ([0-9.]+);?"); 16 | var blendModeRegex = new RegExp("^blend-mode: ([a-z]+);?"); 17 | var backgroundRegex = new RegExp("^background: (" + Re.color + ")( blend-mode\\([a-z]+\\))?( none)?"); 18 | var backgroundImageRegexParamPart = "(url|linear-gradient|radial-gradient)\\(.+?(rgba\\(.+?\\).+?)*\\)( blend-mode\\([a-z]+\\))?( opacity\\([\\d.]+\\))?( none)?"; 19 | var backgroundImageRegex = new RegExp("^background-image: (" + backgroundImageRegexParamPart + "(, )?)+"); 20 | var lineHeightRegex = new RegExp("^line-height: ([\\d.]+)px;?"); 21 | var textAlignRegex = new RegExp("^text-align: (left|right|center)"); 22 | var letterSpacingRegex = new RegExp("^letter-spacing: ([0-9.]+)px"); 23 | var textBehaviourRegex = new RegExp("^text-behaviour: (auto|fixed)"); 24 | var transformRotateRegex = new RegExp("^transform: rotate\\(([\\d.]+)deg\\);?"); 25 | var booleanOperationRegex = new RegExp("^boolean-operation: (union|subtract|intersect|difference);?"); 26 | var blurType1Regex = new RegExp("^filter: (gaussian|background)-blur\\(([\\d.]+)px\\);?"); 27 | var blurType2Regex = new RegExp("^filter: (motion|zoom)-blur\\(([\\d.]+)px ([\\d.]+)deg\\);?"); 28 | var shadowRegex = new RegExp("^box-shadow: ((inset )?(0 |[\\d.]+px ){4}(#[0-9A-F]{6}|rgba\\([0-9,.]+\\))( none)?(, )?)+"); 29 | var displayRegex = new RegExp("^display: none"); 30 | var lockRegex = new RegExp("^lock: true"); 31 | var maskRegex = new RegExp("^mask: initial"); 32 | var textDecarationRegex = new RegExp("^text-decoration: (underline|double-underline|line-through)"); 33 | var backgroundBlendModeRegex = new RegExp("blend-mode\\(([a-z]+)\\)"); 34 | var backgroundOpacityRegex = new RegExp("opacity\\(([\\d.]+)\\)"); 35 | 36 | /** 37 | * @param {MSDocument} doc 38 | * @param {String} path - current working path 39 | */ 40 | function Importer(doc, path) { 41 | this.doc = doc; 42 | this.path = path; 43 | } 44 | 45 | Importer.prototype.import = function() { 46 | var blankPage = this.doc.currentPage(); 47 | 48 | var path = _.joinPath(this.path, 'documents'); 49 | var jsons = File.jsonFilePaths(path); 50 | var tree = jsonTree(jsons, path); 51 | 52 | jsons.sort(compareJsonFilePath.bind(null, tree)); 53 | 54 | for (var i = 0; i < jsons.length; i++) { 55 | var parent = parentPos(jsons[i], tree); 56 | var current = currentPos(jsons[i], tree); 57 | var json = current.json; 58 | current.path = _.joinPath(path, currentPath(jsons[i])); 59 | 60 | if (json.type == 'page') { 61 | this.importPage(json, parent, current); 62 | } else if (json.type == 'artboard') { 63 | this.importArtboard(json, parent, current); 64 | } else if (json.type == "symbolMaster") { 65 | this.importSymbolMaster(json, parent, current); 66 | } else if (json.type == "symbol") { 67 | this.importSymbol(json, parent, current); 68 | } else if (json.type == "group") { 69 | this.importGroup(json, parent, current); 70 | } else if (json.type == "rectangle") { 71 | this.importRectangle(json, parent, current); 72 | } else if (json.type == "oval") { 73 | this.importOval(json, parent, current); 74 | } else if (json.type == "text") { 75 | this.importText(json, parent, current); 76 | } else if (json.type == "image") { 77 | this.importImage(json, parent, current); 78 | } else if (json.type == "shapePath") { 79 | this.importShapePath(json, parent, current); 80 | } else if (json.type == "combinedShape") { 81 | this.importCombinedShape(json, parent, current); 82 | } else if (json.type == "path") { 83 | this.importPath(json, parent, current); 84 | } 85 | } 86 | 87 | this.doc.removePage(blankPage); 88 | }; 89 | 90 | Importer.prototype.importPage = function(json, parent, current) { 91 | var page = this.doc.addBlankPage(); 92 | page.objectID = json.objectId; 93 | page.setName(json.name); 94 | current.object = page; 95 | }; 96 | 97 | Importer.prototype.importArtboard = function(json, parent, current) { 98 | var artboard = MSArtboardGroup.alloc().init(); 99 | artboard.objectID = json.objectId; 100 | artboard.setName(json.name); 101 | var s = parseStyle(json.styles); 102 | artboard.setRect(CGRectMake(s.left, s.top, s.width, s.height)); 103 | if (s.background) { 104 | artboard.hasBackgroundColor = true; 105 | artboard.backgroundColor = _.stringToColor(s.background[0].color); 106 | } 107 | parent.object.addLayer(artboard); 108 | current.object = artboard; 109 | }; 110 | 111 | Importer.prototype.importSymbolMaster = function(json, parent, current) { 112 | var s = parseStyle(json.styles); 113 | var symbol = MSSymbolMaster.alloc().initWithFrame(CGRectMake(s.left, s.top, s.width, s.height)); 114 | symbol.objectID = json.objectId; 115 | symbol.setName(json.name); 116 | symbol.symbolID = json.symbolId; 117 | if (s.background) { 118 | symbol.hasBackgroundColor = true; 119 | symbol.backgroundColor = _.stringToColor(s.background[0].color); 120 | } 121 | if (parent) { 122 | parent.object.addLayer(symbol); 123 | } 124 | current.object = symbol; 125 | }; 126 | 127 | Importer.prototype.importSymbol = function(json, parent, current) { 128 | var s = parseStyle(json.styles); 129 | var symbol = MSSymbolInstance.alloc().init(); 130 | symbol.objectID = json.objectId; 131 | symbol.setName(json.name); 132 | symbol.symbolID = json.symbolId; 133 | symbol.setRect(CGRectMake(s.left, s.top, s.width, s.height)); 134 | parent.object.addLayer(symbol); 135 | current.object = symbol; 136 | }; 137 | 138 | Importer.prototype.importGroup = function(json, parent, current) { 139 | if (!_.isNull(parent) && !parent.object) { 140 | return; 141 | } 142 | 143 | var s = parseStyle(json.styles); 144 | var group = MSLayerGroup.alloc().init(); 145 | group.objectID = json.objectId; 146 | group.frame = MSRect.rectWithRect(CGRectMake(s.left, s.top, s.width, s.height)); 147 | group.setName(json.name); 148 | 149 | if (s.display) { 150 | group.isVisible = false; 151 | } 152 | 153 | if (s.lock) { 154 | group.isLocked = true; 155 | } 156 | 157 | if (s.opacity) { 158 | group.style().contextSettings().opacity = parseFloat(s.opacity); 159 | } 160 | 161 | if (s.blendMode) { 162 | group.style().contextSettings().blendMode = _.blendModeToNumber(s.blendMode); 163 | } 164 | 165 | if (s.rotation) { 166 | group.rotation = s.rotation; 167 | } 168 | 169 | if (s.shadow) { 170 | setShadow(group, s.shadow); 171 | } 172 | 173 | parent.object.addLayer(group); 174 | current.object = group; 175 | }; 176 | 177 | Importer.prototype.importOval = function(json, parent, current) { 178 | if (parent.json.type !== 'combinedShape') { 179 | this._importShape(MSOvalShape, json, parent, current); 180 | return; 181 | } 182 | 183 | if (!_.isNull(parent) && !parent.object) { 184 | return; 185 | } 186 | 187 | var s = parseStyle(json.styles); 188 | var layer = MSOvalShape.alloc().init(); 189 | layer.objectID = json.objectId; 190 | layer.frame = MSRect.rectWithRect(CGRectMake(s.left, s.top, s.width, s.height)); 191 | 192 | if (s.rotation) { 193 | layer.rotation = s.rotation; 194 | } 195 | 196 | if (s.booleanOperation) { 197 | layer.booleanOperation = _.booleanOperationToNumber(s.booleanOperation); 198 | } 199 | 200 | if (s.blur) { 201 | setBlur(layer, s.blur); 202 | } 203 | 204 | layer.setName(json.name); 205 | parent.object.addLayer(layer); 206 | }; 207 | 208 | Importer.prototype.importRectangle = function(json, parent, current) { 209 | if (parent.json.type !== 'combinedShape') { 210 | this._importShape(MSRectangleShape, json, parent, current); 211 | return; 212 | } 213 | 214 | if (!_.isNull(parent) && !parent.object) { 215 | return; 216 | } 217 | 218 | var s = parseStyle(json.styles); 219 | var layer = MSRectangleShape.alloc().init(); 220 | layer.objectID = json.objectId; 221 | layer.frame = MSRect.rectWithRect(CGRectMake(s.left, s.top, s.width, s.height)); 222 | 223 | if (s.rotation) { 224 | layer.rotation = s.rotation; 225 | } 226 | 227 | if (s.booleanOperation) { 228 | layer.booleanOperation = _.booleanOperationToNumber(s.booleanOperation); 229 | } 230 | 231 | if (s.blur) { 232 | setBlur(layer, s.blur); 233 | } 234 | 235 | layer.setName(json.name); 236 | parent.object.addLayer(layer); 237 | }; 238 | 239 | Importer.prototype.importShapePath = function(json, parent, current) { 240 | this._importShape(MSShapePathLayer, json, parent, current); 241 | }; 242 | 243 | Importer.prototype.importCombinedShape = function(json, parent, current) { 244 | this._importShape(MSShapePathLayer, json, parent, current); 245 | }; 246 | 247 | Importer.prototype._importShape = function(type, json, parent, current) { 248 | if (!_.isNull(parent) && !parent.object) { 249 | return; 250 | } 251 | 252 | var s = parseStyle(json.styles); 253 | 254 | var group; 255 | if (type !== MSShapePathLayer) { 256 | var shape = type.alloc().init(); 257 | shape.frame = MSRect.rectWithRect(CGRectMake(s.left, s.top, s.width, s.height)); 258 | 259 | if (s.borderRadius) { 260 | shape.cornerRadiusFloat = s.borderRadius; 261 | } 262 | 263 | group = MSShapeGroup.shapeWithPath(shape); 264 | } else { 265 | group = MSShapeGroup.alloc().init(); 266 | group.frame = MSRect.rectWithRect(CGRectMake(s.left, s.top, s.width, s.height)); 267 | } 268 | group.objectID = json.objectId; 269 | 270 | if (s.borders) { 271 | for (var i = 0; i < s.borders.length; i++) { 272 | var bs = s.borders[i]; 273 | var border = MSStyleBorder.alloc().init(); 274 | border.thickness = bs.thickness; 275 | border.color = _.stringToColor(bs.color); 276 | if (bs.none) { 277 | border.isEnabled = false; 278 | } 279 | group.style().addStyleBorder(border); 280 | } 281 | } 282 | 283 | if (s.backgroundImage) { 284 | for (var i = 0; i < s.backgroundImage.length; i++) { 285 | var bgImage = s.backgroundImage[i]; 286 | var imagePath = _.joinPath(current.path, bgImage.image); 287 | var image = NSImage.alloc().initWithContentsOfFile(imagePath); 288 | var imageData = MSImageData.alloc().initWithImage_convertColorSpace(image, false); 289 | 290 | var fill = MSStyleFill.alloc().init(); 291 | fill.fillType = 4; 292 | fill.image = imageData; 293 | if (bgImage.none) { 294 | fill.isEnabled = false; 295 | } 296 | if (bgImage.blendMode) { 297 | fill.contextSettings().blendMode = _.blendModeToNumber(bgImage.blendMode); 298 | } 299 | if (bgImage.opacity) { 300 | fill.contextSettings().opacity = bgImage.opacity; 301 | } 302 | group.style().addStyleFill(fill); 303 | } 304 | } 305 | 306 | if (s.background) { 307 | for (var i = 0; i < s.background.length; i++) { 308 | var bg = s.background[i]; 309 | var fill = MSStyleFill.alloc().init(); 310 | fill.color = _.stringToColor(bg.color); 311 | if (bg.none) { 312 | fill.isEnabled = false; 313 | } 314 | if (bg.blendMode) { 315 | fill.contextSettings().blendMode = _.blendModeToNumber(bg.blendMode); 316 | } 317 | group.style().addStyleFill(fill); 318 | } 319 | } 320 | 321 | 322 | if (s.linearGradient) { 323 | for (var i = 0; i < s.linearGradient.length; i++) { 324 | var linearGradient = s.linearGradient[i]; 325 | var fill = MSStyleFill.alloc().init(); 326 | fill.fillType = 1; 327 | var stops = linearGradient.stops.map(function(stop){ 328 | return MSGradientStop.alloc().initWithPosition_color(stop.length, _.stringToColor(stop.color)); 329 | }); 330 | var gradient = MSGradient.alloc().initBlankGradient(); 331 | gradient.gradientType = 0; 332 | gradient.from = CGPointMake(linearGradient.from.x, linearGradient.from.y); 333 | gradient.to = CGPointMake(linearGradient.to.x, linearGradient.to.y); 334 | gradient.stops = stops; 335 | fill.gradient = gradient; 336 | group.style().addStyleFill(fill); 337 | } 338 | } 339 | 340 | if (s.rotation) { 341 | group.rotation = s.rotation; 342 | } 343 | 344 | if (s.blur) { 345 | setBlur(group, s.blur); 346 | } 347 | 348 | if (s.shadow) { 349 | setShadow(group, s.shadow); 350 | } 351 | 352 | if (s.opacity) { 353 | group.style().contextSettings().setOpacity(parseFloat(s.opacity)); 354 | } 355 | 356 | if (s.blendMode) { 357 | group.style().contextSettings().blendMode = _.blendModeToNumber(s.blendMode); 358 | } 359 | 360 | if (s.display) { 361 | group.isVisible = false; 362 | } 363 | 364 | if (s.lock) { 365 | group.isLocked = true; 366 | } 367 | 368 | if (s.mask) { 369 | group.prepareAsMask(); 370 | } 371 | 372 | group.setName(json.name); 373 | parent.object.addLayer(group); 374 | current.object = group; 375 | }; 376 | 377 | Importer.prototype.importPath = function(json, parent, current) { 378 | if (!_.isNull(parent) && !parent.object) { 379 | return; 380 | } 381 | 382 | if (!json.path) { 383 | return; 384 | } 385 | 386 | var s = parseStyle(json.styles); 387 | var layer = MSShapePathLayer.alloc().init(); 388 | layer.objectID = json.objectId; 389 | 390 | var isClose = false; 391 | var svgAttr = json.path; 392 | var regex = new RegExp(' [MLC]?([e0-9,.-]+) Z"$'); 393 | if (regex.test(svgAttr)) { 394 | isClose = true; 395 | } 396 | var svg = ''; 397 | var path = NSBezierPath.bezierPathFromSVGString(svg); 398 | layer.bezierPath = path; 399 | 400 | if (isClose) { 401 | layer.closeLastPath(true); 402 | } 403 | 404 | if (s.rotation) { 405 | layer.rotation = s.rotation; 406 | } 407 | 408 | layer.setName(json.name); 409 | parent.object.addLayer(layer); 410 | }; 411 | 412 | Importer.prototype.importText = function(json, parent, current) { 413 | if (_.isNull(parent) || !_.isNull(parent) && !parent.object) { 414 | return; 415 | } 416 | 417 | var s = parseStyle(json.styles); 418 | var text = MSTextLayer.alloc().init(); 419 | text.objectID = json.objectId; 420 | 421 | if (s.content) { 422 | text.stringValue = s.content; 423 | } 424 | 425 | text.font = NSFont.fontWithName_size(s.fontFamily, s.fontSize); 426 | if (s.color) { 427 | text.textColor = _.stringToColor(s.color); 428 | } 429 | 430 | if (s.lineHeight) { 431 | text.lineHeight = s.lineHeight; 432 | } 433 | 434 | if (s.textAlign) { 435 | if (s.textAlign == 'left') { 436 | text.textAlignment = 0; 437 | } else if (s.textAlign == 'right') { 438 | text.textAlignment = 1; 439 | } else if (s.textAlign == 'center') { 440 | text.textAlignment = 2; 441 | } 442 | } 443 | 444 | if (s.letterSpacing) { 445 | text.characterSpacing = s.letterSpacing; 446 | } 447 | 448 | if (s.textBehaviour) { 449 | text.textBehaviour = s.textBehaviour == 'auto' ? 0 : 1; 450 | } 451 | 452 | if (s.rotation) { 453 | text.rotation = s.rotation; 454 | } 455 | 456 | if (s.blur) { 457 | setBlur(text, s.blur); 458 | } 459 | 460 | if (s.shadow) { 461 | setShadow(text, s.shadow); 462 | } 463 | 464 | if (s.display) { 465 | text.isVisible = false; 466 | } 467 | 468 | if (s.opacity) { 469 | text.style().contextSettings().opacity = parseFloat(s.opacity); 470 | } 471 | 472 | if (s.blendMode) { 473 | text.style().contextSettings().blendMode = _.blendModeToNumber(s.blendMode); 474 | } 475 | 476 | if (s.textDecoration) { 477 | if (s.textDecoration == 'underline' || s.textDecoration == 'double-underline') { 478 | text.addAttribute_value('NSUnderline', _.underlineToNumber(s.textDecoration)); 479 | } else { 480 | text.addAttribute_value('NSStrikethrough', 1); 481 | } 482 | } 483 | 484 | if (s.lock) { 485 | text.isLocked = true; 486 | } 487 | 488 | text.frame = MSRect.rectWithRect(CGRectMake(s.left, s.top, s.width, s.height)); 489 | text.setName(json.name); 490 | parent.object.addLayer(text); 491 | }; 492 | 493 | Importer.prototype.importImage = function(json, parent, current) { 494 | if (!_.isNull(parent) && !parent.object) { 495 | return; 496 | } 497 | 498 | var s = parseStyle(json.styles); 499 | var imagePath = _.joinPath(current.path, s.backgroundImage[0].image); 500 | var image = NSImage.alloc().initWithContentsOfFile(imagePath); 501 | var imageData = MSImageData.alloc().initWithImage_convertColorSpace(image, false); 502 | var rect = NSMakeRect(s.left, s.top, s.width, s.height); 503 | var bitmap = MSBitmapLayer.alloc().initWithFrame_image(rect, imageData); 504 | bitmap.objectID = json.objectId; 505 | bitmap.setName(json.name); 506 | 507 | if (s.rotation) { 508 | bitmap.rotation = s.rotation; 509 | } 510 | 511 | if (s.blur) { 512 | setBlur(bitmap, s.blur); 513 | } 514 | 515 | if (s.shadow) { 516 | setShadow(bitmap, s.shadow); 517 | } 518 | 519 | if (s.display) { 520 | bitmap.isVisible = false; 521 | } 522 | 523 | if (s.opacity) { 524 | bitmap.style().contextSettings().opacity = parseFloat(s.opacity); 525 | } 526 | 527 | if (s.blendMode) { 528 | bitmap.style().contextSettings().blendMode = _.blendModeToNumber(s.blendMode); 529 | } 530 | 531 | if (s.lock) { 532 | bitmap.isLocked = true; 533 | } 534 | 535 | parent.object.addLayer(bitmap); 536 | }; 537 | 538 | /** 539 | * @param {Array} jsonPaths - File.jsonFilePaths 540 | * @param {String} path - working dir 541 | */ 542 | function jsonTree(jsonPaths, path) { 543 | var tree = {}; 544 | for (var i = 0; i < jsonPaths.length; i++) { 545 | var dirs = jsonPaths[i].pathComponents(); 546 | var p = tree; 547 | for (var j = 0; j < dirs.length; j++) { 548 | var n = dirs[j]; 549 | if (n.pathExtension() == 'json') { 550 | n = 'jsonFileName'; 551 | p[n] = dirs[j]; 552 | 553 | var filePath = _.joinPath(path, jsonPaths[i]); 554 | var jsonString = File.readFileContents(filePath); 555 | var json = JSON.parse(jsonString); 556 | p['json'] = json; 557 | } else { 558 | p[n] = p[n] || {}; 559 | } 560 | p = p[n]; 561 | } 562 | } 563 | return tree; 564 | } 565 | 566 | function parentPos(path, tree) { 567 | var p = tree; 568 | var components = path.pathComponents(); 569 | for (var i = 0; i < (components.length - 2); i++) { 570 | var n = components[i]; 571 | p = p[n]; 572 | } 573 | if (p.jsonFileName) { 574 | return p; 575 | } else { 576 | return null; 577 | } 578 | } 579 | 580 | function currentPath(path) { 581 | var components = path.pathComponents(); 582 | components.pop(); 583 | return components.join('/'); 584 | } 585 | 586 | function currentPos(path, tree) { 587 | var p = tree; 588 | var components = path.pathComponents(); 589 | for (var i = 0; i < components.length - 1; i++) { 590 | var n = components[i]; 591 | p = p[n]; 592 | } 593 | 594 | return p; 595 | } 596 | 597 | function parseStyle(styles) { 598 | var re = {}; 599 | for (var i = 0; i < styles.length; i++) { 600 | // positions 601 | if (positionRegex.test(styles[i])) { 602 | var ms = positionRegex.exec(styles[i]); 603 | var k = ms[1], v = ms[2]; 604 | re[k] = parseFloat(v); 605 | 606 | // borders 607 | } else if (borderRegex.test(styles[i])) { 608 | var ms = borderRegex.exec(styles[i]); 609 | var thickness = ms[1], color = ms[2]; 610 | 611 | re.borders = re.borders || []; 612 | var borderStyles = { 613 | thickness: parseFloat(thickness), 614 | color: color, 615 | }; 616 | if (ms[3]) { 617 | borderStyles.none = true; 618 | } 619 | re.borders.push(borderStyles); 620 | 621 | // border radius 622 | } else if (borderRadiusRegex.test(styles[i])) { 623 | var ms = borderRadiusRegex.exec(styles[i]); 624 | re.borderRadius = ms[1]; 625 | 626 | // text content 627 | } else if (contentRegex.test(styles[i])) { 628 | var ms = contentRegex.exec(styles[i]); 629 | re.content = ms[1]; 630 | 631 | // font family 632 | } else if (fontFamilyRegex.test(styles[i])) { 633 | var ms = fontFamilyRegex.exec(styles[i]); 634 | re.fontFamily = ms[1]; 635 | 636 | // font size 637 | } else if (fontSizeRegex.test(styles[i])) { 638 | var ms = fontSizeRegex.exec(styles[i]); 639 | re.fontSize = parseFloat(ms[1]); 640 | 641 | // line height 642 | } else if (lineHeightRegex.test(styles[i])) { 643 | var ms = lineHeightRegex.exec(styles[i]); 644 | re.lineHeight = parseFloat(ms[1]); 645 | 646 | // text align 647 | } else if (textAlignRegex.test(styles[i])) { 648 | var ms = textAlignRegex.exec(styles[i]); 649 | re.textAlign = ms[1]; 650 | 651 | // letter spacing 652 | } else if (letterSpacingRegex.test(styles[i])) { 653 | var ms = letterSpacingRegex.exec(styles[i]); 654 | re.letterSpacing = parseFloat(ms[1]); 655 | 656 | // text behaviour 657 | } else if (textBehaviourRegex.test(styles[i])) { 658 | var ms = textBehaviourRegex.exec(styles[i]); 659 | re.textBehaviour = ms[1]; 660 | 661 | // transform: rotate() 662 | } else if (transformRotateRegex.test(styles[i])) { 663 | var ms = transformRotateRegex.exec(styles[i]); 664 | re.rotation = parseFloat(ms[1]); 665 | 666 | // boolean-operation 667 | } else if (booleanOperationRegex.test(styles[i])) { 668 | var ms = booleanOperationRegex.exec(styles[i]); 669 | re.booleanOperation = ms[1]; 670 | 671 | // blur: gaussian/background 672 | } else if (blurType1Regex.test(styles[i])) { 673 | var ms = blurType1Regex.exec(styles[i]); 674 | re.blur = { type: ms[1], radius: parseFloat(ms[2]) }; 675 | 676 | // blur: motion/zoom 677 | } else if (blurType2Regex.test(styles[i])) { 678 | var ms = blurType2Regex.exec(styles[i]); 679 | re.blur = { type: ms[1], radius: parseFloat(ms[2]), angle: parseFloat(ms[3]) }; 680 | 681 | // shadow 682 | } else if (shadowRegex.test(styles[i])) { 683 | re.shadow = parseShadow(styles[i]); 684 | 685 | // color 686 | } else if (colorRegex.test(styles[i])) { 687 | var ms = colorRegex.exec(styles[i]); 688 | re.color = ms[1]; 689 | 690 | // opacity 691 | } else if (opacityRegex.test(styles[i])) { 692 | var ms = opacityRegex.exec(styles[i]); 693 | re.opacity = ms[1]; 694 | 695 | // blendMode 696 | } else if (blendModeRegex.test(styles[i])) { 697 | var ms = blendModeRegex.exec(styles[i]); 698 | re.blendMode = ms[1]; 699 | 700 | // background color 701 | } else if (backgroundRegex.test(styles[i])) { 702 | re.background = parseBackground(styles[i]); 703 | 704 | // background image 705 | } else if (backgroundImageRegex.test(styles[i])) { 706 | var parsedBackgroundImage = parseBackgroundImage(styles[i]); 707 | if (parsedBackgroundImage.image.length > 0) { 708 | re.backgroundImage = parsedBackgroundImage.image; 709 | } 710 | if (parsedBackgroundImage.linearGradient.length > 0) { 711 | re.linearGradient = parsedBackgroundImage.linearGradient; 712 | } 713 | 714 | } else if (displayRegex.test(styles[i])) { 715 | re.display = 'none'; 716 | 717 | } else if (lockRegex.test(styles[i])) { 718 | re.lock = true; 719 | 720 | } else if (maskRegex.test(styles[i])) { 721 | re.mask = true; 722 | 723 | } else if (textDecarationRegex.test(styles[i])) { 724 | var ms = textDecarationRegex.exec(styles[i]); 725 | re.textDecoration = ms[1]; 726 | 727 | } else { 728 | // print(styles[i]); 729 | } 730 | } 731 | return re; 732 | } 733 | 734 | function compareJsonFilePath(tree, a, b) { 735 | var as = a.pathComponents(); 736 | var bs = b.pathComponents(); 737 | var aLen = as.length; 738 | var bLen = bs.length; 739 | 740 | if (aLen == bLen && as.slice(0, -2).join('/') == bs.slice(0, -2).join('/')) { 741 | return currentPos(b, tree).json.index - currentPos(a, tree).json.index; 742 | } else { 743 | return aLen - bLen; 744 | } 745 | } 746 | 747 | function setBlur(layer, attr) { 748 | var blur = MSStyleBlur.alloc().init(); 749 | blur.type = _.blurTypeToNumber(attr.type); 750 | blur.radius = attr.radius; 751 | if (attr.angle) { 752 | blur.motionAngle = attr.angle; 753 | } 754 | blur.isEnabled = true; 755 | layer.style().blur = blur; 756 | } 757 | 758 | var backgroundParamsRegex = new RegExp("(" + Re.color + ")( blend-mode\\([a-z]+\\))?( none)?"); 759 | /** 760 | * @param {String} style - "background: ..." 761 | */ 762 | function parseBackground(style) { 763 | var re = new Array(); 764 | var s = style.replace(new RegExp("^background: "), ''); 765 | s = s.replace(new RegExp(";$"), ''); 766 | var ss = s.split(', '); 767 | 768 | for (var i = 0; i < ss.length; i++) { 769 | var ms = backgroundParamsRegex.exec(ss[i]); 770 | var params = { color: ms[1] }; 771 | if (ms[2]) { 772 | params.blendMode = backgroundBlendModeRegex.exec(ms[2])[1]; 773 | } 774 | if (ms[3]) { 775 | params.none = true; 776 | } 777 | 778 | re.push(params); 779 | } 780 | return re; 781 | } 782 | 783 | var backgroundImageParamsRegex = new RegExp("url\\(([0-9a-f]+\\.png)\\)( blend-mode\\([a-z]+\\))?( opacity\\([\\d.]+\\))?( none)?"); 784 | var linearGradientParamsRegex = new RegExp("linear-gradient\\((.+)\\)( blend-mode\\([a-z]+\\))?( none)?"); 785 | var radialGradientParamsRegex = new RegExp("radial-gradient\\((.+)\\)( blend-mode\\([a-z]+\\))?( none)?"); 786 | /** 787 | * @param {String} style - "background-image: ..." 788 | */ 789 | function parseBackgroundImage(style) { 790 | var re = { 791 | image: new Array(), 792 | linearGradient: new Array(), 793 | radialGradient: new Array() 794 | }; 795 | var s = style.replace(new RegExp("^background-image: "), ''); 796 | s = s.replace(new RegExp(";$"), ''); 797 | var ss = s.match(new RegExp(backgroundImageRegexParamPart, 'g')); 798 | 799 | for (var i = 0; i < ss.length; i++) { 800 | if (backgroundImageParamsRegex.test(ss[i])) { 801 | var ms = backgroundImageParamsRegex.exec(ss[i]); 802 | var params = { image: ms[1] }; 803 | if (ms[2]) { 804 | params.blendMode = backgroundBlendModeRegex.exec(ms[2])[1]; 805 | } 806 | if (ms[3]) { 807 | params.opacity = parseFloat(backgroundOpacityRegex.exec(ms[3])[1]); 808 | } 809 | if (ms[4]) { 810 | params.none = true; 811 | } 812 | re.image.push(params); 813 | } else if (linearGradientParamsRegex.test(ss[i])) { 814 | var ms = linearGradientParamsRegex.exec(ss[i]); 815 | var rules = ms[1].match(new RegExp("((" + Re.color + ") ([0-9.]+))", 'g')); 816 | var positions = ms[1].replace(/,.+/, '').split(' '); 817 | rules = rules.map(function(rule){ 818 | var re = new RegExp("(" + Re.color + ") ([0-9.]+)"); 819 | var ms = re.exec(rule); 820 | return { color: ms[1], length: parseFloat(ms[2]) }; 821 | }); 822 | re.linearGradient.push({ 823 | from: { x: positions[0], y: positions[1] }, 824 | to: { x: positions[2], y: positions[3] }, 825 | stops: rules 826 | }); 827 | } 828 | } 829 | return re; 830 | } 831 | 832 | var shadowParamsRegex = new RegExp("(inset )?((?:[\\d.]+px ){4})(#[0-9A-F]{6}|rgba\\([0-9,.]+\\))( none)?"); 833 | /** 834 | * @param {String} style - "box-shadow: ..." 835 | */ 836 | function parseShadow(style) { 837 | var re = { inner: new Array(), outer: new Array() }; 838 | var s = style.replace(new RegExp("^box-shadow: "), ''); 839 | s = s.replace(new RegExp(";$"), ''); 840 | var ss = s.split(', '); 841 | var lenRegex = new RegExp("([\\d.]+px)", 'g'); 842 | var parseLens = function(strs){ 843 | var re = {}; 844 | for (var i = 0; i < strs.length; i++) { 845 | var num = parseFloat(strs[i].replace(new RegExp("px"), '')); 846 | if (i === 0) { 847 | re.offsetX = num; 848 | } else if (i === 1) { 849 | re.offsetY = num; 850 | } else if (i === 2) { 851 | re.blurRadius = num; 852 | } else if (i === 3) { 853 | re.spreadRadius = num; 854 | } 855 | } 856 | return re; 857 | }; 858 | 859 | for (var i = 0; i < ss.length; i++) { 860 | var ms = shadowParamsRegex.exec(ss[i]); 861 | var params = parseLens(ms[2].match(lenRegex)); 862 | if (ms[3]) { 863 | params.color = ms[3]; 864 | } 865 | params.enable = true; 866 | if (ms[4]) { 867 | params.enable = false; 868 | } 869 | if (ms[1]) { 870 | re.inner.push(params); 871 | } else { 872 | re.outer.push(params); 873 | } 874 | } 875 | 876 | return re; 877 | } 878 | 879 | function setShadow(layer, style) { 880 | var inner = style.inner; 881 | var outer = style.outer; 882 | var createShadow = function(s){ 883 | var shadow = MSStyleShadow.alloc().init(); 884 | shadow.offsetX = s.offsetX; 885 | shadow.offsetY = s.offsetY; 886 | shadow.blurRadius = s.blurRadius; 887 | shadow.spread = s.spreadRadius; 888 | if (s.enable) { 889 | shadow.isEnabled = true; 890 | } else { 891 | shadow.isEnabled = false; 892 | } 893 | shadow.color = _.stringToColor(s.color); 894 | return shadow; 895 | }; 896 | 897 | if (inner.length > 0) { 898 | var shadows = new Array(); 899 | for (var i = 0; i < inner.length; i++) { 900 | shadows.push(createShadow(inner[i])); 901 | } 902 | layer.style().innerShadows = shadows; 903 | } 904 | 905 | if (outer.length > 0) { 906 | var shadows = new Array(); 907 | for (var i = 0; i < outer.length; i++) { 908 | shadows.push(createShadow(outer[i])); 909 | } 910 | layer.style().shadows = shadows; 911 | } 912 | } 913 | 914 | module.exports = Importer; 915 | --------------------------------------------------------------------------------