├── .gitignore
├── LICENSE
├── README.md
├── lib
└── persistence.jstalk
├── webview.jstalk
└── www
├── css
└── pure-min.css
└── index.html
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # Compiled binary addons (http://nodejs.org/api/addons.html)
20 | build/Release
21 |
22 | # Dependency directory
23 | # Deployed apps should consider commenting this line out:
24 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
25 | node_modules
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Urs Hunkler
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # sketch-webview
2 |
3 | ## Sketch WebView explorations
4 | After Ale’s posting about different approaches to create Sketch dialogues I have investigated in WebViews.
5 |
6 | ### Success
7 | I was able to open a local HTML page and to execute JavaScript on the page: the HTML page is “www/index.html”. The Sketch script handling the WebView is “webview.jstalk”.
8 |
9 | As a side product I wrote a persistence handler based on Sam’s use of the threadDictionary. When I tried to save a JavaScript object I got errors - so I created a persist handler with a get and set method which converts JavaScript elements to/from JSON strings (See file “lib/persistence.jstalk”). This way you can save any element persitently.
10 |
11 | #### Some Issues
12 | The communication with the WebView is not possible when the panel with the WebView is created. The communication is possible from the second script run - the reference to the WebView is persistently saved.
13 |
14 | #### The WebFrameLoadDelegate
15 | For the direct communication a WebFrameLoadDelegate is needed. I wrote a JavaScript function “WebViewLoadDelegate” which implements the two methods `webView:didClearWindowObject:forFrame:` -> JS function `WebViewLoadDelegate.prototype.webView_didClearWindowObject_forFrame` and `webView:didFinishLoadForFrame:` -> JS function `WebViewLoadDelegate.prototype.webView_didFinishLoadForFrame`. I set the delegate on the WebView with `setFrameLoadDelegate()`, but the methods are never called. In the “Mocha” documentation I read that JavaScript functions are converted to classes (https://github.com/logancollins/Mocha#javascript-to-objective-c). So the delegate may be ok - I don’t know - and the methods may never be called because the script terminates before the WebView is established and shown.
16 |
17 | #### Question: How to implement a WebFrameLoadDelegate in CocoaScript?
18 | #### Question: How can a script continue to run without blocking other threads / Sketch?
19 |
20 | #### The JavaScript -> CocoaScript communication
21 | To execute JavaScript in the page with `webView.windowScriptObject().evaluateWebScript(js)`
22 | works perfect starting with the second script run. With the WebView the communication from the JavaScript in the page with ObjC/CocoaScript methods should be possible.
23 |
24 | I created a JavaScript object, implemented a property and a method and the `isSelectorExcludedFromWebScript` and
25 | `isKeyExcludedFromWebScript` methods to open all properties and methods to WebScripting. The JavaScript object is included with `scriptObject.setValue_forKey(runThisFunc, 'callThis’)`, The object ‘callThis’ is visible from the page for JavaScript. But neither the property nor the method are shown nor are they accessible - trying to get the property value or to call the method produce errors.
26 |
27 | #### Question: How can a class with properties and methods be created in CocoaScript in a way that the properties and methods are accessible from the page JavaScript?
28 |
29 | ### So far
30 | I have learned a lot about ObjC and CocoaScript and how a WebView should work. The possibility to execute JavaScript in the WebView is already great and I see a lot of potential here for people working with webdesign/webdevelopment.
31 |
32 | To solve the `WebFrameLoadDelegate` and the JavaScript -> CocoaScript communication I need support. It would be great when somebody may find the time to investigate and help solve the issues.
33 |
34 | Looking forward to be able to work with HTML pages from Sketch.
35 |
--------------------------------------------------------------------------------
/lib/persistence.jstalk:
--------------------------------------------------------------------------------
1 | /**
2 | * A persitent thread dictionary manager that handles CocoaScript boxed objects
3 | * and JavaScript data. JS data is saved as a JSON string because JS objects
4 | * can't be saved in the dictoionary directly.
5 | *
6 | * The persist object offers a setter and getter function which deal with the
7 | * data conversion where neccessary.
8 | *
9 | * To save data: persist.set(keyname, value);
10 | * To retrieve data: persist.get(keyname);
11 | *
12 | * @type {Object}
13 | */
14 | var persist = {
15 | dict: NSThread.mainThread().threadDictionary(),
16 |
17 | /**
18 | * Check if the returend value is a CocoaScript string, if so
19 | * it is a JSON object and will be parsed.
20 | *
21 | * @param {string} key The name of the element to get.
22 | * @return {mixed} The saved element or null
23 | */
24 | get: function (key) {
25 | var val = this.dict[key]);
26 |
27 | if (val !== null && val.className() == '__NSCFString') {
28 | val = JSON.parse(val);
29 | }
30 |
31 | return val;
32 | },
33 |
34 | /**
35 | * Check if the given value is not a CocoaScript MOBoxedObject -
36 | * it is a JavaScript type and must be JSON stringified.
37 | *
38 | * @param {string} key The name of the element to save
39 | * @param {mixed} val The element to save
40 | */
41 | set: function (key, val) {
42 | if (Object.prototype.toString.call(val) !== "[object MOBoxedObject]") {
43 | val = JSON.stringify(val);
44 | }
45 |
46 | this.dict[key] = val;
47 | }
48 | };
49 |
--------------------------------------------------------------------------------
/webview.jstalk:
--------------------------------------------------------------------------------
1 | // WebView tests (shift alt ctrl y)
2 |
3 | /**
4 | * The first script call creates the panel with the saved HTML page.
5 | * The following script calls execute the JavaScript calls in the webView.
6 | */
7 |
8 | // Add a hint to whomever is instantiating us,
9 | // that we'd like to stick around for a while.
10 | coscript.shouldKeepAround = true;
11 |
12 | // Include additionl frameworks.
13 | framework('WebKit');
14 | framework('AppKit');
15 |
16 | // Import the persistence handler.
17 | #import 'lib/persistence.jstalk'
18 |
19 | // Define a WebViewLoad delegate function - should be converted to an ObjC class.
20 | // See https://github.com/logancollins/Mocha
21 | var WebViewLoadDelegate = function () {};
22 |
23 | // Add the initiating delegate (callback) function.
24 | WebViewLoadDelegate.prototype.webView_didClearWindowObject_forFrame = function (sender, scriptObject, frame) {
25 | var jswrapper = 'try {[[js]]} catch (e) {e.toString();}',
26 | jscode = 'document.body.innerHTML;',
27 | js = jswrapper.replace('[[js]]', jscode);
28 |
29 | var result = scriptObject.evaluateWebScript(js);
30 | log(result);
31 | };
32 |
33 | // Add the delegate (callback) function which is called when the page has loaded.
34 | WebViewLoadDelegate.prototype.webView_didFinishLoadForFrame = function (sender, frame) {
35 | var jswrapper = 'try {[[js]]} catch (e) {e.toString();}',
36 | jscode = 'document.body.innerHTML;',
37 | js = jswrapper.replace('[[js]]', jscode);
38 |
39 | var scriptObject = sender.windowScriptObject();
40 |
41 | var result = scriptObject.evaluateWebScript(js);
42 | log(result);
43 | };
44 |
45 | // Prepare the function that will be used as an object added to the
46 | // scriptObject. The object should be visible with it's methods and properties
47 | // from the page JavaScript and should be usable to call Sketch script
48 | // functions from the page JavaScript.
49 | var runThis = function runThis () {
50 | var that = function () {};
51 |
52 | // Property visible to JavaScript.
53 | that.fixed = "Property named fixed";
54 | // Method visible to JavaScript.
55 | that.log = function () {
56 | log('Called from JavaScript via the Sketch script RunThis object.');
57 | return true;
58 | };
59 | // Method returns whether a selector should be hidden
60 | // from the scripting environment. If "false" is returned none is hidden.
61 | that.isSelectorExcludedFromWebScript = function (selector) {
62 | log('selector');
63 | log(selector);
64 | return false;
65 | };
66 | // Method returns whether a key should be hidden
67 | // from the scripting environment. If "false" is returned none is hidden.
68 | that.isKeyExcludedFromWebScript = function (key) {
69 | log('key');
70 | log(key);
71 | return false;
72 | };
73 |
74 | return that;
75 | };
76 |
77 | // Set the url to the saved webpage
78 | // Split the scriptpath into an array, remove last item which is
79 | // the script name, create a string from the array again and wrap the
80 | // path with 'file://' and the html file name.
81 | var URL = '';
82 | var scriptpath = sketch.scriptPath.split('/');
83 |
84 | scriptpath.pop();
85 | scriptpath = scriptpath.join('/') + '/';
86 | URL = encodeURI('file://' + scriptpath + 'www/index.html');
87 |
88 | /**
89 | * Prepare the panel, show it and save it into the persistent store.
90 | */
91 | var setupWebViewPanel = function () {
92 | // Prepare the panel:
93 | // Set the panel size and
94 | // initialize the webview and load the URL
95 | var frame = NSMakeRect(0, 0, 600, 480);
96 | var webView = WebView.alloc().initWithFrame(frame);
97 | var webViewFrame = webView.mainFrame();
98 |
99 | // The FrameLoadDelegate offers the webView_didFinishLoadForFrame
100 | // method which is called when the web page is loaded.
101 | // !!! The methods never fire because:
102 | // - it is implemented wrong?
103 | // - the delegate's method never is called because the script ends before the
104 | // page is loaded?
105 | var loadDelegate = new WebViewLoadDelegate();
106 | webView.setFrameLoadDelegate(loadDelegate);
107 |
108 | webViewFrame.loadRequest(NSURLRequest.requestWithURL(NSURL.URLWithString(URL)));
109 |
110 | // Set up the panel window
111 | var mask = NSTitledWindowMask + NSClosableWindowMask + NSMiniaturizableWindowMask + NSResizableWindowMask + NSUtilityWindowMask;
112 | var panel = NSPanel.alloc().initWithContentRect_styleMask_backing_defer(frame, mask, NSBackingStoreBuffered, true);
113 |
114 | // Add the webView to the prepared panel
115 | panel.contentView().addSubview(webView);
116 |
117 | // Show the panel
118 | panel.makeKeyAndOrderFront(panel);
119 |
120 | // persist the panel and the webView.
121 | persist.set('panel', panel);
122 | persist.set('webView', webView);
123 | };
124 |
125 | var doScript = function () {
126 | var jswrapper = 'try {[[js]]} catch (e) {e.toString();}',
127 | jscode = '',
128 | js = jswrapper.replace('[[js]]', jscode);
129 |
130 | // var result = webView.stringByEvaluatingJavaScriptFromString(js);
131 |
132 | // Get the windowScriptObject as the scripting connector
133 | var scriptObject = webView.windowScriptObject();
134 |
135 | // Add the RunThis object with the key 'callThis'. The callThis
136 | // object should be accessible by the page JavaScript.
137 | // !!! The object is seen by the page JavaScript but the methods/porperties
138 | // are not present.
139 | var runThisFunc = runThis();
140 | scriptObject.setValue_forKey(runThisFunc, 'callThis');
141 |
142 | // Add a text line.
143 | jscode = 'de.unodo.writeTest("From the Sketch script ' + new Date() + '");';
144 | js = jswrapper.replace('[[js]]', jscode);
145 | var result = scriptObject.evaluateWebScript(js);
146 | log(result);
147 |
148 | // Call the callback function to check if the 'callThis' class is visible
149 | // in the page for JavaScript.
150 | jscode = 'de.unodo.callBack();';
151 | js = jswrapper.replace('[[js]]', jscode);
152 | var result = scriptObject.evaluateWebScript(js);
153 | log(result);
154 |
155 | // Get the form data.
156 | jscode = 'de.unodo.getFormData();';
157 | js = jswrapper.replace('[[js]]', jscode);
158 | var result = scriptObject.evaluateWebScript(js);
159 | log('Formfield "Name" value: ' + result);
160 | };
161 |
162 | var panel = persist.get('panel');
163 | var webView = persist.get('webView');
164 |
165 | // If the panel does not exisit (null is returned from persist.get),
166 | // set the panel up and show it.
167 | // Else make the panel the front window and run the JavaScript functions.
168 | if (panel === null) {
169 | log('setupWebViewPanel');
170 | setupWebViewPanel();
171 | } else {
172 | // Show the panel
173 | panel.makeKeyAndOrderFront(panel);
174 |
175 | // var loadDelegate = new WebViewLoadDelegate;
176 | // webView.setFrameLoadDelegate(loadDelegate);
177 |
178 | // Run the scripts
179 | doScript();
180 | }
181 |
182 | log('done');
183 |
--------------------------------------------------------------------------------
/www/css/pure-min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | Pure v0.3.1-pre
3 | Copyright 2013 Yahoo! Inc. All rights reserved.
4 | Licensed under the BSD License.
5 | https://github.com/yui/pure/blob/master/LICENSE.md
6 | */
7 | /*!
8 | normalize.css v1.1.3 | MIT License | git.io/normalize
9 | Copyright (c) Nicolas Gallagher and Jonathan Neal
10 | */
11 | /*! normalize.css v1.1.3 | MIT License | git.io/normalize */article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none;height:0}[hidden]{display:none}html{font-size:100%;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}html,button,input,select,textarea{font-family:sans-serif}body{margin:0}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{font-size:2em;margin:.67em 0}h2{font-size:1.5em;margin:.83em 0}h3{font-size:1.17em;margin:1em 0}h4{font-size:1em;margin:1.33em 0}h5{font-size:.83em;margin:1.67em 0}h6{font-size:.67em;margin:2.33em 0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}blockquote{margin:1em 40px}dfn{font-style:italic}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}mark{background:#ff0;color:#000}p,pre{margin:1em 0}code,kbd,pre,samp{font-family:monospace,serif;_font-family:'courier new',monospace;font-size:1em}pre{white-space:pre;white-space:pre-wrap;word-wrap:break-word}q{quotes:none}q:before,q:after{content:'';content:none}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}dl,menu,ol,ul{margin:1em 0}dd{margin:0 0 0 40px}menu,ol,ul{padding:0 0 0 40px}nav ul,nav ol{list-style:none;list-style-image:none}img{border:0;-ms-interpolation-mode:bicubic}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0;white-space:normal;*margin-left:-7px}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;*overflow:visible}button[disabled],html input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0;*height:13px;*width:13px}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}[hidden]{display:none!important}.pure-g{letter-spacing:-.31em;*letter-spacing:normal;*word-spacing:-.43em;text-rendering:optimizespeed;font-family:FreeSans,Arimo,"Droid Sans",Helvetica,Arial,sans-serif;display:-webkit-flex;-webkit-flex-flow:row wrap;display:-ms-flexbox;-ms-flex-flow:row wrap}.opera-only :-o-prefocus,.pure-g{word-spacing:-.43em}.pure-u{display:inline-block;*display:inline;zoom:1;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-g [class *="pure-u"]{font-family:sans-serif}.pure-u-1,.pure-u-1-1,.pure-u-1-2,.pure-u-1-3,.pure-u-2-3,.pure-u-1-4,.pure-u-3-4,.pure-u-1-5,.pure-u-2-5,.pure-u-3-5,.pure-u-4-5,.pure-u-5-5,.pure-u-1-6,.pure-u-5-6,.pure-u-1-8,.pure-u-3-8,.pure-u-5-8,.pure-u-7-8,.pure-u-1-12,.pure-u-5-12,.pure-u-7-12,.pure-u-11-12,.pure-u-1-24,.pure-u-2-24,.pure-u-3-24,.pure-u-4-24,.pure-u-5-24,.pure-u-6-24,.pure-u-7-24,.pure-u-8-24,.pure-u-9-24,.pure-u-10-24,.pure-u-11-24,.pure-u-12-24,.pure-u-13-24,.pure-u-14-24,.pure-u-15-24,.pure-u-16-24,.pure-u-17-24,.pure-u-18-24,.pure-u-19-24,.pure-u-20-24,.pure-u-21-24,.pure-u-22-24,.pure-u-23-24,.pure-u-24-24{display:inline-block;*display:inline;zoom:1;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-u-1-24{width:4.1667%;*width:4.1357%}.pure-u-1-12,.pure-u-2-24{width:8.3333%;*width:8.3023%}.pure-u-1-8,.pure-u-3-24{width:12.5%;*width:12.469%}.pure-u-1-6,.pure-u-4-24{width:16.6667%;*width:16.6357%}.pure-u-1-5{width:20%;*width:19.969%}.pure-u-5-24{width:20.8333%;*width:20.8023%}.pure-u-1-4,.pure-u-6-24{width:25%;*width:24.969%}.pure-u-7-24{width:29.1667%;*width:29.1357%}.pure-u-1-3,.pure-u-8-24{width:33.3333%;*width:33.3023%}.pure-u-3-8,.pure-u-9-24{width:37.5%;*width:37.469%}.pure-u-2-5{width:40%;*width:39.969%}.pure-u-5-12,.pure-u-10-24{width:41.6667%;*width:41.6357%}.pure-u-11-24{width:45.8333%;*width:45.8023%}.pure-u-1-2,.pure-u-12-24{width:50%;*width:49.969%}.pure-u-13-24{width:54.1667%;*width:54.1357%}.pure-u-7-12,.pure-u-14-24{width:58.3333%;*width:58.3023%}.pure-u-3-5{width:60%;*width:59.969%}.pure-u-5-8,.pure-u-15-24{width:62.5%;*width:62.469%}.pure-u-2-3,.pure-u-16-24{width:66.6667%;*width:66.6357%}.pure-u-17-24{width:70.8333%;*width:70.8023%}.pure-u-3-4,.pure-u-18-24{width:75%;*width:74.969%}.pure-u-19-24{width:79.1667%;*width:79.1357%}.pure-u-4-5{width:80%;*width:79.969%}.pure-u-5-6,.pure-u-20-24{width:83.3333%;*width:83.3023%}.pure-u-7-8,.pure-u-21-24{width:87.5%;*width:87.469%}.pure-u-11-12,.pure-u-22-24{width:91.6667%;*width:91.6357%}.pure-u-23-24{width:95.8333%;*width:95.8023%}.pure-u-1,.pure-u-1-1,.pure-u-5-5,.pure-u-24-24{width:100%}.pure-g-r{letter-spacing:-.31em;*letter-spacing:normal;*word-spacing:-.43em;font-family:FreeSans,Arimo,"Droid Sans",Helvetica,Arial,sans-serif;display:-webkit-flex;-webkit-flex-flow:row wrap;display:-ms-flexbox;-ms-flex-flow:row wrap}.opera-only :-o-prefocus,.pure-g-r{word-spacing:-.43em}.pure-g-r [class *="pure-u"]{font-family:sans-serif}.pure-g-r img{max-width:100%;height:auto}@media (min-width:980px){.pure-visible-phone{display:none}.pure-visible-tablet{display:none}.pure-hidden-desktop{display:none}}@media (max-width:480px){.pure-g-r>.pure-u,.pure-g-r>[class *="pure-u-"]{width:100%}}@media (max-width:767px){.pure-g-r>.pure-u,.pure-g-r>[class *="pure-u-"]{width:100%}.pure-hidden-phone{display:none}.pure-visible-desktop{display:none}}@media (min-width:768px) and (max-width:979px){.pure-hidden-tablet{display:none}.pure-visible-desktop{display:none}}.pure-button{display:inline-block;*display:inline;zoom:1;line-height:normal;white-space:nowrap;vertical-align:baseline;text-align:center;cursor:pointer;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.pure-button::-moz-focus-inner{padding:0;border:0}.pure-button{font-family:inherit;font-size:100%;*font-size:90%;*overflow:visible;padding:.5em 1em;color:#444;color:rgba(0,0,0,.8);*color:#444;border:1px solid #999;border:0 rgba(0,0,0,0);background-color:#E6E6E6;text-decoration:none;border-radius:2px;-webkit-transition:.1s linear -webkit-box-shadow;-moz-transition:.1s linear -moz-box-shadow;-ms-transition:.1s linear box-shadow;-o-transition:.1s linear box-shadow;transition:.1s linear box-shadow}.pure-button-hover,.pure-button:hover,.pure-button:focus{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#1a000000', GradientType=0);background-image:-webkit-gradient(linear,0 0,0 100%,from(transparent),color-stop(40%,rgba(0,0,0,.05)),to(rgba(0,0,0,.1)));background-image:-webkit-linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1));background-image:-moz-linear-gradient(top,rgba(0,0,0,.05) 0,rgba(0,0,0,.1));background-image:-o-linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1));background-image:linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1))}.pure-button:focus{outline:0}.pure-button-active,.pure-button:active{box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 0 6px rgba(0,0,0,.2) inset}.pure-button[disabled],.pure-button-disabled,.pure-button-disabled:hover,.pure-button-disabled:focus,.pure-button-disabled:active{border:0;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);filter:alpha(opacity=40);-khtml-opacity:.4;-moz-opacity:.4;opacity:.4;cursor:not-allowed;box-shadow:none}.pure-button-hidden{display:none}.pure-button::-moz-focus-inner{padding:0;border:0}.pure-button-primary,.pure-button-selected,a.pure-button-primary,a.pure-button-selected{background-color:#0078e7;color:#fff}.pure-form input:not([type]),.pure-form input[type=text],.pure-form input[type=password],.pure-form input[type=email],.pure-form input[type=url],.pure-form input[type=date],.pure-form input[type=month],.pure-form input[type=time],.pure-form input[type=datetime],.pure-form input[type=datetime-local],.pure-form input[type=week],.pure-form input[type=number],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=color],.pure-form select,.pure-form textarea{padding:.5em .6em;display:inline-block;border:1px solid #ccc;font-size:.8em;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;-webkit-transition:.3s linear border;-moz-transition:.3s linear border;-ms-transition:.3s linear border;-o-transition:.3s linear border;transition:.3s linear border;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.pure-form input:not([type]):focus,.pure-form input[type=text]:focus,.pure-form input[type=password]:focus,.pure-form input[type=email]:focus,.pure-form input[type=url]:focus,.pure-form input[type=date]:focus,.pure-form input[type=month]:focus,.pure-form input[type=time]:focus,.pure-form input[type=datetime]:focus,.pure-form input[type=datetime-local]:focus,.pure-form input[type=week]:focus,.pure-form input[type=number]:focus,.pure-form input[type=search]:focus,.pure-form input[type=tel]:focus,.pure-form input[type=color]:focus,.pure-form select:focus,.pure-form textarea:focus{outline:0;outline:thin dotted \9;border-color:#129FEA}.pure-form input[type=file]:focus,.pure-form input[type=radio]:focus,.pure-form input[type=checkbox]:focus{outline:thin dotted #333;outline:1px auto #129FEA}.pure-form .pure-checkbox,.pure-form .pure-radio{margin:.5em 0;display:block}.pure-form input:not([type])[disabled],.pure-form input[type=text][disabled],.pure-form input[type=password][disabled],.pure-form input[type=email][disabled],.pure-form input[type=url][disabled],.pure-form input[type=date][disabled],.pure-form input[type=month][disabled],.pure-form input[type=time][disabled],.pure-form input[type=datetime][disabled],.pure-form input[type=datetime-local][disabled],.pure-form input[type=week][disabled],.pure-form input[type=number][disabled],.pure-form input[type=search][disabled],.pure-form input[type=tel][disabled],.pure-form input[type=color][disabled],.pure-form select[disabled],.pure-form textarea[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input[readonly],.pure-form select[readonly],.pure-form textarea[readonly]{background:#eee;color:#777;border-color:#ccc}.pure-form input:focus:invalid,.pure-form textarea:focus:invalid,.pure-form select:focus:invalid{color:#b94a48;border:1px solid #ee5f5b}.pure-form input:focus:invalid:focus,.pure-form textarea:focus:invalid:focus,.pure-form select:focus:invalid:focus{border-color:#e9322d}.pure-form input[type=file]:focus:invalid:focus,.pure-form input[type=radio]:focus:invalid:focus,.pure-form input[type=checkbox]:focus:invalid:focus{outline-color:#e9322d}.pure-form select{border:1px solid #ccc;background-color:#fff}.pure-form select[multiple]{height:auto}.pure-form label{margin:.5em 0 .2em;font-size:90%}.pure-form fieldset{margin:0;padding:.35em 0 .75em;border:0}.pure-form legend{display:block;width:100%;padding:.3em 0;margin-bottom:.3em;font-size:125%;color:#333;border-bottom:1px solid #e5e5e5}.pure-form-stacked input:not([type]),.pure-form-stacked input[type=text],.pure-form-stacked input[type=password],.pure-form-stacked input[type=email],.pure-form-stacked input[type=url],.pure-form-stacked input[type=date],.pure-form-stacked input[type=month],.pure-form-stacked input[type=time],.pure-form-stacked input[type=datetime],.pure-form-stacked input[type=datetime-local],.pure-form-stacked input[type=week],.pure-form-stacked input[type=number],.pure-form-stacked input[type=search],.pure-form-stacked input[type=tel],.pure-form-stacked input[type=color],.pure-form-stacked select,.pure-form-stacked label,.pure-form-stacked textarea{display:block;margin:.25em 0}.pure-form-aligned input,.pure-form-aligned textarea,.pure-form-aligned select,.pure-form-aligned .pure-help-inline,.pure-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.pure-form-aligned textarea{vertical-align:top}.pure-form-aligned .pure-control-group{margin-bottom:.5em}.pure-form-aligned .pure-control-group label{text-align:right;display:inline-block;vertical-align:middle;width:10em;margin:0 1em 0 0}.pure-form-aligned .pure-controls{margin:1.5em 0 0 10em}.pure-form input.pure-input-rounded,.pure-form .pure-input-rounded{border-radius:2em;padding:.5em 1em}.pure-form .pure-group fieldset{margin-bottom:10px}.pure-form .pure-group input{display:block;padding:10px;margin:0;border-radius:0;position:relative;top:-1px}.pure-form .pure-group input:focus{z-index:2}.pure-form .pure-group input:first-child{top:1px;border-radius:4px 4px 0 0}.pure-form .pure-group input:last-child{top:-2px;border-radius:0 0 4px 4px}.pure-form .pure-group button{margin:.35em 0}.pure-form .pure-input-1{width:100%}.pure-form .pure-input-2-3{width:66%}.pure-form .pure-input-1-2{width:50%}.pure-form .pure-input-1-3{width:33%}.pure-form .pure-input-1-4{width:25%}.pure-form .pure-help-inline,.pure-form-message-inline{display:inline-block;padding-left:.3em;color:#666;vertical-align:middle;font-size:90%}.pure-form-message{display:block;color:#666;font-size:90%}@media only screen and (max-width :480px){.pure-form button[type=submit]{margin:.7em 0 0}.pure-form input:not([type]),.pure-form input[type=text],.pure-form input[type=password],.pure-form input[type=email],.pure-form input[type=url],.pure-form input[type=date],.pure-form input[type=month],.pure-form input[type=time],.pure-form input[type=datetime],.pure-form input[type=datetime-local],.pure-form input[type=week],.pure-form input[type=number],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=color],.pure-form label{margin-bottom:.3em;display:block}.pure-group input:not([type]),.pure-group input[type=text],.pure-group input[type=password],.pure-group input[type=email],.pure-group input[type=url],.pure-group input[type=date],.pure-group input[type=month],.pure-group input[type=time],.pure-group input[type=datetime],.pure-group input[type=datetime-local],.pure-group input[type=week],.pure-group input[type=number],.pure-group input[type=search],.pure-group input[type=tel],.pure-group input[type=color]{margin-bottom:0}.pure-form-aligned .pure-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.pure-form-aligned .pure-controls{margin:1.5em 0 0}.pure-form .pure-help-inline,.pure-form-message-inline,.pure-form-message{display:block;font-size:80%;padding:.2em 0 .8em}}.pure-menu ul{position:absolute;visibility:hidden}.pure-menu.pure-menu-open{visibility:visible;z-index:2;width:100%}.pure-menu ul{left:-10000px;list-style:none;margin:0;padding:0;top:-10000px;z-index:1}.pure-menu>ul{position:relative}.pure-menu-open>ul{left:0;top:0;visibility:visible}.pure-menu-open>ul:focus{outline:0}.pure-menu li{position:relative}.pure-menu a,.pure-menu .pure-menu-heading{display:block;color:inherit;line-height:1.5em;padding:5px 20px;text-decoration:none;white-space:nowrap}.pure-menu.pure-menu-horizontal>.pure-menu-heading{display:inline-block;*display:inline;zoom:1;margin:0;vertical-align:middle}.pure-menu.pure-menu-horizontal>ul{display:inline-block;*display:inline;zoom:1;vertical-align:middle}.pure-menu li a{padding:5px 20px}.pure-menu-can-have-children>.pure-menu-label:after{content:'\25B8';float:right;font-family:'Lucida Grande','Lucida Sans Unicode','DejaVu Sans',sans-serif;margin-right:-20px;margin-top:-1px}.pure-menu-can-have-children>.pure-menu-label{padding-right:30px}.pure-menu-separator{background-color:#dfdfdf;display:block;height:1px;font-size:0;margin:7px 2px;overflow:hidden}.pure-menu-hidden{display:none}.pure-menu-fixed{position:fixed;top:0;left:0;width:100%}.pure-menu-horizontal li{display:inline-block;*display:inline;zoom:1;vertical-align:middle}.pure-menu-horizontal li li{display:block}.pure-menu-horizontal>.pure-menu-children>.pure-menu-can-have-children>.pure-menu-label:after{content:"\25BE"}.pure-menu-horizontal>.pure-menu-children>.pure-menu-can-have-children>.pure-menu-label{padding-right:30px}.pure-menu-horizontal li.pure-menu-separator{height:50%;width:1px;margin:0 7px}.pure-menu-horizontal li li.pure-menu-separator{height:1px;width:auto;margin:7px 2px}.pure-menu.pure-menu-open,.pure-menu.pure-menu-horizontal li .pure-menu-children{background:#fff;border:1px solid #b7b7b7}.pure-menu.pure-menu-horizontal,.pure-menu.pure-menu-horizontal .pure-menu-heading{border:0}.pure-menu a{border:1px solid transparent;border-left:0;border-right:0}.pure-menu a,.pure-menu .pure-menu-can-have-children>li:after{color:#777}.pure-menu .pure-menu-can-have-children>li:hover:after{color:#fff}.pure-menu .pure-menu-open{background:#dedede}.pure-menu li a:hover,.pure-menu li a:focus{background:#eee}.pure-menu li.pure-menu-disabled a:hover,.pure-menu li.pure-menu-disabled a:focus{background:#fff;color:#bfbfbf}.pure-menu .pure-menu-disabled>a{background-image:none;border-color:transparent;cursor:default}.pure-menu .pure-menu-disabled>a,.pure-menu .pure-menu-can-have-children.pure-menu-disabled>a:after{color:#bfbfbf}.pure-menu .pure-menu-heading{color:#565d64;text-transform:uppercase;font-size:90%;margin-top:.5em;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:#dfdfdf}.pure-menu .pure-menu-selected a{color:#000}.pure-menu.pure-menu-open.pure-menu-fixed{border:0;border-bottom:1px solid #b7b7b7}.pure-paginator{letter-spacing:-.31em;*letter-spacing:normal;*word-spacing:-.43em;text-rendering:optimizespeed;list-style:none;margin:0;padding:0}.opera-only :-o-prefocus,.pure-paginator{word-spacing:-.43em}.pure-paginator li{display:inline-block;*display:inline;zoom:1;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-paginator .pure-button{border-radius:0;padding:.8em 1.4em;vertical-align:top;height:1.1em}.pure-paginator .pure-button:focus,.pure-paginator .pure-button:active{outline-style:none}.pure-paginator .prev,.pure-paginator .next{color:#C0C1C3;text-shadow:0 -1px 0 rgba(0,0,0,.45)}.pure-paginator .prev{border-radius:2px 0 0 2px}.pure-paginator .next{border-radius:0 2px 2px 0}@media (max-width:480px){.pure-menu-horizontal{width:100%}.pure-menu-children li{display:block;border-bottom:1px solid #000}}.pure-table{border-collapse:collapse;border-spacing:0;empty-cells:show;border:1px solid #cbcbcb}.pure-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.pure-table td,.pure-table th{border-left:1px solid #cbcbcb;border-width:0 0 0 1px;font-size:inherit;margin:0;overflow:visible;padding:6px 12px}.pure-table td:first-child,.pure-table th:first-child{border-left-width:0}.pure-table thead{background:#e0e0e0;color:#000;text-align:left;vertical-align:bottom}.pure-table td{background-color:transparent}.pure-table-odd td{background-color:#f2f2f2}.pure-table-striped tr:nth-child(2n-1) td{background-color:#f2f2f2}.pure-table-bordered td{border-bottom:1px solid #cbcbcb}.pure-table-bordered tbody>tr:last-child td,.pure-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.pure-table-horizontal td,.pure-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #cbcbcb}.pure-table-horizontal tbody>tr:last-child td{border-bottom-width:0}
--------------------------------------------------------------------------------
/www/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
63 | In sed nunc in ligula vestibulum euismod at quis neque. Fusce lacus neque, vehicula sit amet aliquam sit amet, dictum quis nibh. Donec ullamcorper, felis aliquam sagittis dignissim, urna ante vehicula eros, et porta justo.
64 |