129 | A ChiliPeppr Workspace that lets you do stuff.
130 |
131 |
132 |
133 |
134 |
135 |
136 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # com-chilipeppr-workspace-sample
2 | A ChiliPeppr Workspace sample.
3 |
4 | 
5 |
6 | ## ChiliPeppr Workspace / Sample
7 |
8 | All ChiliPeppr workspaces/widgets/elements are defined using cpdefine() which is a method
9 | that mimics require.js. Each defined object must have a unique ID so it does
10 | not conflict with other ChiliPeppr objects.
11 |
12 | | Item | Value |
13 | | ------------- | ------------- |
14 | | ID | com-chilipeppr-workspace-sample |
15 | | Name | Workspace / Sample |
16 | | Description | A ChiliPeppr Workspace sample. |
17 | | chilipeppr.load() URL | http://raw.githubusercontent.com/chilipeppr/workspace-sample/master/auto-generated-workspace.html |
18 | | Edit URL | http://ide.c9.io/chilipeppr/workspace-sample |
19 | | Github URL | http://github.com/chilipeppr/workspace-sample |
20 | | Test URL | https://preview.c9users.io/chilipeppr/workspace-sample/workspace.html |
21 |
22 | ## Example Code for chilipeppr.load() Statement
23 |
24 | You can use the code below as a starting point for instantiating this workspace
25 | from ChiliPeppr's Edit Boot Script dialog box. The key is that you need to load
26 | your workspace inlined into the standard #pnlWorkspace div so the DOM can parse your HTML, CSS, and
27 | Javascript. Then you use cprequire() to find your workspace's Javascript and get
28 | back the instance of it to init() it.
29 |
30 | ```javascript
31 | // This code should be pasted into the ChiliPeppr Edit Boot Javascript dialog box
32 | // located in the upper right corner of any chilipeppr.com page.
33 | // The ChiliPeppr environment has a standard div called #pnlWorkspace that
34 | // this workspace should be loaded into.
35 | chilipeppr.load(
36 | "#pnlWorkspace",
37 | "http://raw.githubusercontent.com/chilipeppr/workspace-sample/master/auto-generated-workspace.html",
38 | function() {
39 | // Callback after workspace loaded into #pnlWorkspace
40 | // Now use require.js to get reference to instantiated workspace
41 | cprequire(
42 | ["inline:com-chilipeppr-workspace-sample"], // the id you gave your workspace
43 | function(myWorkspaceSample) {
44 | // Callback that is passed reference to the newly loaded workspace
45 | console.log("Workspace / Sample just got loaded.", myWorkspaceSample);
46 | myWorkspaceSample.init();
47 | }
48 | );
49 | }
50 | );
51 |
52 | ```
53 |
54 | ## Publish
55 |
56 | This workspace publishes the following signals. These signals are owned by this workspace and are published to
57 | all objects inside the ChiliPeppr environment that listen to them via the
58 | chilipeppr.subscribe(signal, callback) method.
59 | To better understand how ChiliPeppr's subscribe() method works see amplify.js's documentation at http://amplifyjs.com/api/pubsub/
60 |
61 |
62 |
63 |
64 |
Signal
65 |
Description
66 |
67 |
68 |
69 |
(No signals defined in this workspace)
70 |
71 |
72 |
73 | ## Subscribe
74 |
75 | This workspace subscribes to the following signals. These signals are owned by this workspace.
76 | Other objects inside the ChiliPeppr environment can publish to these signals via the chilipeppr.publish(signal, data) method.
77 | To better understand how ChiliPeppr's publish() method works see amplify.js's documentation at http://amplifyjs.com/api/pubsub/
78 |
79 |
80 |
81 |
82 |
Signal
83 |
Description
84 |
85 |
86 |
87 |
(No signals defined in this workspace)
88 |
89 |
90 |
91 | ## Foreign Publish
92 |
93 | This workspace publishes to the following signals that are owned by other objects.
94 | To better understand how ChiliPeppr's subscribe() method works see amplify.js's documentation at http://amplifyjs.com/api/pubsub/
95 |
96 |
97 |
98 |
99 |
Signal
100 |
Description
101 |
102 |
103 |
104 |
(No signals defined in this workspace)
105 |
106 |
107 |
108 | ## Foreign Subscribe
109 |
110 | This workspace publishes to the following signals that are owned by other objects.
111 | To better understand how ChiliPeppr's publish() method works see amplify.js's documentation at http://amplifyjs.com/api/pubsub/
112 |
113 |
114 |
115 |
116 |
Signal
117 |
Description
118 |
119 |
120 |
121 |
(No signals defined in this workspace)
122 |
123 |
124 |
125 | ## Methods / Properties
126 |
127 | The table below shows, in order, the methods and properties inside the workspace object.
128 |
129 |
130 |
131 |
132 |
Method / Property
133 |
Type
134 |
Description
135 |
136 |
137 |
138 |
id
string
"com-chilipeppr-workspace-sample"
The ID of the widget. You must define this and make it unique.
Contains reference to the Console widget object. Hang onto the reference
139 | so we can resize it when the window resizes because we want it to manually
140 | resize to fill the height of the browser so it looks clean.
widgetSpjs
object
Contains reference to the Serial Port JSON Server object.
init
function
function ()
The workspace's init method. It loads the all the widgets contained in the workspace
141 | and inits them.
getBillboard
function
function ()
Returns the billboard HTML, CSS, and Javascript for this Workspace. The billboard
142 | is used by the home page, the workspace picker, and the fork pulldown to show a
143 | consistent name/image/description tag for the workspace throughout the ChiliPeppr ecosystem.
addBillboardToWorkspaceMenu
function
function ()
Inject the billboard into the Workspace upper right corner pulldown which
144 | follows the standard template for workspace pulldown menus.
setupResize
function
function ()
Listen to window resize event.
onResize
function
function ()
When browser window resizes, forcibly resize the Console window
loadTemplateWidget
function
function (callback)
Load the Template widget via chilipeppr.load() so folks have a sample
145 | widget they can fork as a starting point for their own.
loadSpjsWidget
function
function (callback)
Load the Serial Port JSON Server widget via chilipeppr.load()
loadConsoleWidget
function
function (callback)
Load the Console widget via chilipeppr.load()
loadWorkspaceMenu
function
function (callback)
Load the workspace menu and show the pubsubviewer and fork links using
146 | our pubsubviewer widget that makes those links for us.
147 |
148 |
149 |
150 |
151 | ## About ChiliPeppr
152 |
153 | [ChiliPeppr](http://chilipeppr.com) is a hardware fiddle, meaning it is a
154 | website that lets you easily
155 | create a workspace to fiddle with your hardware from software. ChiliPeppr provides
156 | a [Serial Port JSON Server](https://github.com/johnlauer/serial-port-json-server)
157 | that you run locally on your computer, or remotely on another computer, to connect to
158 | the serial port of your hardware like an Arduino or other microcontroller.
159 |
160 | You then create a workspace at ChiliPeppr.com that connects to your hardware
161 | by starting from scratch or forking somebody else's
162 | workspace that is close to what you are after. Then you write widgets in
163 | Javascript that interact with your hardware by forking the base template
164 | widget or forking another widget that
165 | is similar to what you are trying to build.
166 |
167 | ChiliPeppr is massively capable such that the workspaces for
168 | [TinyG](http://chilipeppr.com/tinyg) and [Grbl](http://chilipeppr.com/grbl) CNC
169 | controllers have become full-fledged CNC machine management software used by
170 | tens of thousands.
171 |
172 | ChiliPeppr has inspired many people in the hardware/software world to use the
173 | browser and Javascript as the foundation for interacting with hardware. The
174 | Arduino team in Italy caught wind of ChiliPeppr and now
175 | ChiliPeppr's Serial Port JSON Server is the basis for the
176 | [Arduino's new web IDE](https://create.arduino.cc/). If the Arduino team is excited about building on top
177 | of ChiliPeppr, what
178 | will you build on top of it?
179 |
180 |
--------------------------------------------------------------------------------
/workspace.js:
--------------------------------------------------------------------------------
1 | /* global cpdefine chilipeppr cprequire */
2 | cprequire_test(["inline:com-chilipeppr-workspace-sample"], function(ws) {
3 |
4 | console.log("initting workspace");
5 |
6 | /**
7 | * The Root workspace (when you see the ChiliPeppr Header) auto Loads the Flash
8 | * Widget so we can show the 3 second flash messages. However, in test mode we
9 | * like to see them as well, so just load it from the cprequire_test() method
10 | * so we have similar functionality when testing this workspace.
11 | */
12 | var loadFlashMsg = function() {
13 | chilipeppr.load("#com-chilipeppr-widget-flash-instance",
14 | "http://raw.githubusercontent.com/chilipeppr/element-flash/master/auto-generated-widget.html",
15 | // "http://fiddle.jshell.net/chilipeppr/90698kax/show/light/",
16 | function() {
17 | console.log("mycallback got called after loading flash msg module");
18 | cprequire(["inline:com-chilipeppr-elem-flashmsg"], function(fm) {
19 | //console.log("inside require of " + fm.id);
20 | fm.init();
21 | });
22 | }
23 | );
24 | };
25 | loadFlashMsg();
26 |
27 | // Init workspace
28 | ws.init();
29 |
30 | // Do some niceties for testing like margins on widget and title for browser
31 | $('title').html("Sample Workspace");
32 | $('body').css('padding', '10px');
33 |
34 | } /*end_test*/ );
35 |
36 | // This is the main definition of your widget. Give it a unique name.
37 | cpdefine("inline:com-chilipeppr-workspace-sample", ["chilipeppr_ready"], function() {
38 | return {
39 | /**
40 | * The ID of the widget. You must define this and make it unique.
41 | */
42 | id: "com-chilipeppr-workspace-sample", // Make the id the same as the cpdefine id
43 | name: "Workspace / Sample", // The descriptive name of your widget.
44 | desc: `A ChiliPeppr Workspace sample.`,
45 | url: "(auto fill by runme.js)", // The final URL of the working widget as a single HTML file with CSS and Javascript inlined. You can let runme.js auto fill this if you are using Cloud9.
46 | fiddleurl: "(auto fill by runme.js)", // The edit URL. This can be auto-filled by runme.js in Cloud9 if you'd like, or just define it on your own to help people know where they can edit/fork your widget
47 | githuburl: "(auto fill by runme.js)", // The backing github repo
48 | testurl: "(auto fill by runme.js)", // The standalone working widget so can view it working by itself
49 | /**
50 | * Contains reference to the Console widget object. Hang onto the reference
51 | * so we can resize it when the window resizes because we want it to manually
52 | * resize to fill the height of the browser so it looks clean.
53 | */
54 | widgetConsole: null,
55 | /**
56 | * Contains reference to the Serial Port JSON Server object.
57 | */
58 | widgetSpjs: null,
59 | /**
60 | * The workspace's init method. It loads the all the widgets contained in the workspace
61 | * and inits them.
62 | */
63 | init: function() {
64 |
65 | // Most workspaces will instantiate the Serial Port JSON Server widget
66 | this.loadSpjsWidget();
67 | // Most workspaces will instantiate the Serial Port Console widget
68 | this.loadConsoleWidget(function() {
69 | setTimeout(function() { $(window).trigger('resize'); }, 100);
70 | });
71 |
72 | this.loadTemplateWidget();
73 |
74 | // Create our workspace upper right corner triangle menu
75 | this.loadWorkspaceMenu();
76 | // Add our billboard to the menu (has name, url, picture of workspace)
77 | this.addBillboardToWorkspaceMenu();
78 |
79 | // Setup an event to react to window resize. This helps since
80 | // some of our widgets have a manual resize to cleanly fill
81 | // the height of the browser window. You could turn this off and
82 | // just set widget min-height in CSS instead
83 | this.setupResize();
84 | setTimeout(function() { $(window).trigger('resize'); }, 100);
85 |
86 | },
87 | /**
88 | * Returns the billboard HTML, CSS, and Javascript for this Workspace. The billboard
89 | * is used by the home page, the workspace picker, and the fork pulldown to show a
90 | * consistent name/image/description tag for the workspace throughout the ChiliPeppr ecosystem.
91 | */
92 | getBillboard: function() {
93 | var el = $('#' + this.id + '-billboard').clone();
94 | el.removeClass("hidden");
95 | el.find('.billboard-desc').text(this.desc);
96 | return el;
97 | },
98 | /**
99 | * Inject the billboard into the Workspace upper right corner pulldown which
100 | * follows the standard template for workspace pulldown menus.
101 | */
102 | addBillboardToWorkspaceMenu: function() {
103 | // get copy of billboard
104 | var billboardEl = this.getBillboard();
105 | $('#' + this.id + ' .com-chilipeppr-ws-billboard').append(billboardEl);
106 | },
107 | /**
108 | * Listen to window resize event.
109 | */
110 | setupResize: function() {
111 | $(window).on('resize', this.onResize.bind(this));
112 | },
113 | /**
114 | * When browser window resizes, forcibly resize the Console window
115 | */
116 | onResize: function() {
117 | if (this.widgetConsole) this.widgetConsole.resize();
118 | },
119 | /**
120 | * Load the Template widget via chilipeppr.load() so folks have a sample
121 | * widget they can fork as a starting point for their own.
122 | */
123 | loadTemplateWidget: function(callback) {
124 |
125 | chilipeppr.load(
126 | "#com-chilipeppr-widget-template-instance",
127 | "http://raw.githubusercontent.com/chilipeppr/widget-template/master/auto-generated-widget.html",
128 | function() {
129 | // Callback after widget loaded into #myDivWidgetTemplate
130 | // Now use require.js to get reference to instantiated widget
131 | cprequire(
132 | ["inline:com-chilipeppr-widget-template"], // the id you gave your widget
133 | function(myObjWidgetTemplate) {
134 | // Callback that is passed reference to the newly loaded widget
135 | console.log("Widget / Template just got loaded.", myObjWidgetTemplate);
136 | myObjWidgetTemplate.init();
137 | }
138 | );
139 | }
140 | );
141 | },
142 | /**
143 | * Load the Serial Port JSON Server widget via chilipeppr.load()
144 | */
145 | loadSpjsWidget: function(callback) {
146 |
147 | var that = this;
148 |
149 | chilipeppr.load(
150 | "#com-chilipeppr-widget-serialport-instance",
151 | "http://raw.githubusercontent.com/chilipeppr/widget-spjs/master/auto-generated-widget.html",
152 | // "http://fiddle.jshell.net/chilipeppr/vetj5fvx/show/light/",
153 | function() {
154 | console.log("mycallback got called after loading spjs module");
155 | cprequire(["inline:com-chilipeppr-widget-serialport"], function(spjs) {
156 | //console.log("inside require of " + fm.id);
157 | spjs.setSingleSelectMode();
158 | spjs.init({
159 | isSingleSelectMode: true,
160 | defaultBuffer: "default",
161 | defaultBaud: 115200,
162 | bufferEncouragementMsg: 'For your device please choose the "default" buffer in the pulldown and a 115200 baud rate before connecting.'
163 | });
164 | //spjs.showBody();
165 | //spjs.consoleToggle();
166 |
167 | that.widgetSpjs - spjs;
168 |
169 | if (callback) callback(spjs);
170 |
171 | });
172 | }
173 | );
174 | },
175 | /**
176 | * Load the Console widget via chilipeppr.load()
177 | */
178 | loadConsoleWidget: function(callback) {
179 | var that = this;
180 | chilipeppr.load(
181 | "#com-chilipeppr-widget-spconsole-instance",
182 | "http://raw.githubusercontent.com/chilipeppr/widget-console/master/auto-generated-widget.html",
183 | // "http://fiddle.jshell.net/chilipeppr/rczajbx0/show/light/",
184 | function() {
185 | // Callback after widget loaded into #com-chilipeppr-widget-spconsole-instance
186 | cprequire(
187 | ["inline:com-chilipeppr-widget-spconsole"], // the id you gave your widget
188 | function(mywidget) {
189 | // Callback that is passed reference to your newly loaded widget
190 | console.log("My Console widget just got loaded.", mywidget);
191 | that.widgetConsole = mywidget;
192 |
193 | // init the serial port console
194 | // 1st param tells the console to use "single port mode" which
195 | // means it will only show data for the green selected serial port
196 | // rather than for multiple serial ports
197 | // 2nd param is a regexp filter where the console will filter out
198 | // annoying messages you don't generally want to see back from your
199 | // device, but that the user can toggle on/off with the funnel icon
200 | that.widgetConsole.init(true, /myfilter/);
201 | if (callback) callback(mywidget);
202 | }
203 | );
204 | }
205 | );
206 | },
207 | /**
208 | * Load the workspace menu and show the pubsubviewer and fork links using
209 | * our pubsubviewer widget that makes those links for us.
210 | */
211 | loadWorkspaceMenu: function(callback) {
212 | // Workspace Menu with Workspace Billboard
213 | var that = this;
214 | chilipeppr.load(
215 | "http://raw.githubusercontent.com/chilipeppr/widget-pubsubviewer/master/auto-generated-widget.html",
216 | // "http://fiddle.jshell.net/chilipeppr/zMbL9/show/light/",
217 | function() {
218 | require(['inline:com-chilipeppr-elem-pubsubviewer'], function(pubsubviewer) {
219 |
220 | var el = $('#' + that.id + ' .com-chilipeppr-ws-menu .dropdown-menu-ws');
221 | console.log("got callback for attachto menu for workspace. attaching to el:", el);
222 |
223 | pubsubviewer.attachTo(
224 | el,
225 | that,
226 | "Workspace"
227 | );
228 |
229 | if (callback) callback();
230 | });
231 | }
232 | );
233 | },
234 | }
235 | });
--------------------------------------------------------------------------------
/auto-generated-workspace.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Workspace / Sample
8 |
9 |
10 |
11 |
12 |
14 |
15 |
16 |
90 |
91 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
";
51 |
52 | //html = html + htmlDocs;
53 | var finalHtml = htmlDocs.replace(//, notes);
54 |
55 | res.end(finalHtml);
56 |
57 | }
58 | else if (uri == "/pushtogithub") {
59 |
60 | console.log("/pushtogithub called");
61 |
62 | var stdout = pushToGithubSync()
63 |
64 | var json = {
65 | success: true,
66 | desc: "Pushed to Github",
67 | log: stdout
68 | }
69 |
70 | res.writeHead(200, {
71 | 'Content-Type': 'application/json'
72 | });
73 | res.end(JSON.stringify(json));
74 | }
75 | else if (uri == "/pullfromgithub") {
76 |
77 | console.log("/pullfromgithub called");
78 |
79 | var stdout = pullFromGithubSync();
80 |
81 | var json = {
82 | success: true,
83 | desc: "Pulled from Github",
84 | log: stdout
85 | }
86 |
87 | res.writeHead(200, {
88 | 'Content-Type': 'application/json'
89 | });
90 | res.end(JSON.stringify(json));
91 |
92 | }
93 | else if (uri == "/mergeFromCpTemplateRepo") {
94 |
95 | console.log("/mergeFromCpTemplateRepo called");
96 |
97 | var stdout = mergeFromCpTemplateRepo();
98 |
99 | var json = {
100 | success: true,
101 | desc: "Merged the latest ChiliPeppr Template to this repo. Please check for merge conflicts. You can run \"git status\" for a summary of conflicts, if any.",
102 | log: stdout
103 | }
104 |
105 | res.writeHead(200, {
106 | 'Content-Type': 'application/json'
107 | });
108 | res.end(JSON.stringify(json));
109 |
110 | } else {
111 |
112 | var filename = path.join(process.cwd(), unescape(uri));
113 | var stats;
114 |
115 | try {
116 | stats = fs.lstatSync(filename); // throws if path doesn't exist
117 | }
118 | catch (e) {
119 | res.writeHead(404, {
120 | 'Content-Type': 'text/plain'
121 | });
122 | res.write('404 Not Found\n');
123 | res.end();
124 | return;
125 | }
126 |
127 | if (stats.isFile()) {
128 | // path exists, is a file
129 | var mimeType = mimeTypes[path.extname(filename).split(".").reverse()[0]];
130 | res.writeHead(200, {
131 | 'Content-Type': mimeType
132 | });
133 |
134 | var fileStream = fs.createReadStream(filename);
135 | fileStream.pipe(res);
136 | }
137 | else if (stats.isDirectory()) {
138 | // path exists, is a directory
139 | res.writeHead(200, {
140 | 'Content-Type': 'text/plain'
141 | });
142 | res.write('Index of ' + uri + '\n');
143 | res.write('TODO, show index?\n');
144 | res.end();
145 | }
146 | else {
147 | // Symbolic link, other?
148 | // TODO: follow symlinks? security?
149 | res.writeHead(500, {
150 | 'Content-Type': 'text/plain'
151 | });
152 | res.write('500 Internal server error\n');
153 | res.end();
154 | }
155 |
156 | }
157 |
158 | }).listen(process.env.PORT);
159 |
160 | String.prototype.regexIndexOf = function(regex, startpos) {
161 | var indexOf = this.substring(startpos || 0).search(regex);
162 | return (indexOf >= 0) ? (indexOf + (startpos || 0)) : indexOf;
163 | }
164 |
165 | var fileAutoGeneratePath = "auto-generated-workspace.html"
166 | var fileJsPath = "workspace.js"
167 | var fileCssPath = "workspace.css"
168 | var fileHtmlPath = "workspace.html"
169 |
170 | var widgetUrl = 'http://' +
171 | process.env.C9_PROJECT + '-' + process.env.C9_USER +
172 | '.c9users.io/workspace.html';
173 | var testUrl = 'https://preview.c9users.io/' +
174 | process.env.C9_USER + '/' +
175 | process.env.C9_PROJECT + '/' + fileHtmlPath;
176 | var testUrlNoSsl = 'http://' + process.env.C9_PROJECT +
177 | '-' + process.env.C9_USER + '.c9users.io/' + fileHtmlPath;
178 | var editUrl = 'http://ide.c9.io/' +
179 | process.env.C9_USER + '/' +
180 | process.env.C9_PROJECT;
181 | var github;
182 |
183 | var widgetSrc, widget, id, deps, cpdefine, requirejs, cprequire_test;
184 | var widgetDocs = {};
185 |
186 | var init = function() {
187 | github = getGithubUrl();
188 | }
189 |
190 | var isEvaled = false;
191 | var evalWidgetJs = function() {
192 |
193 | if (isEvaled) return;
194 |
195 | // This method reads in your widget.js and evals it to
196 | // figure out all the info from it to generate docs and sample
197 | // code to make your life easy
198 | widgetSrc = fs.readFileSync(fileJsPath)+'';
199 |
200 | // fill in some auto fill stuff
201 | /*
202 | var widgetUrl = 'http://' +
203 | process.env.C9_PROJECT + '-' + process.env.C9_USER +
204 | '.c9users.io/widget.html';
205 | var editUrl = 'http://ide.c9.io/' +
206 | process.env.C9_USER + '/' +
207 | process.env.C9_PROJECT;
208 | var github = getGithubUrl();
209 | */
210 |
211 | var reUrl = /(url\s*:\s*['"]?)\(auto fill by runme\.js\)/;
212 | //console.log("reUrl:", reUrl);
213 | widgetSrc = widgetSrc.replace(reUrl, "$1" + github.rawurl);
214 | widgetSrc = widgetSrc.replace(/(fiddleurl\s*:\s*['"]?)\(auto fill by runme\.js\)/, "$1" + editUrl);
215 | widgetSrc = widgetSrc.replace(/(githuburl\s*:\s*['"]?)\(auto fill by runme\.js\)/, "$1" + github.url);
216 | widgetSrc = widgetSrc.replace(/(testurl\s*:\s*['"]?)\(auto fill by runme\.js\)/, "$1" + widgetUrl);
217 |
218 | // rewrite the javascript
219 | //fs.writeFileSync(fileJsPath, widgetSrc);
220 |
221 | console.log("before we eval here is the src:", widgetSrc);
222 | eval(widgetSrc);
223 | //console.log("evaled the widget.js");
224 | //isEvaled = true;
225 |
226 | // generate docs
227 | for (var key in widget) {
228 |
229 | var obj = widget[key];
230 | widgetDocs[key] = {
231 | type: typeof obj,
232 | property: false,
233 | method: false,
234 | descSrc: "",
235 | descHtml: "",
236 | descMd: "", // markdown
237 | };
238 | var objDoc = widgetDocs[key];
239 |
240 | if (typeof obj === 'function') {
241 |
242 | // grab first line of source code
243 | var srcFirstLine = obj.toString().substring(0, obj.toString().indexOf("\n"));
244 | // drop {
245 | srcFirstLine = srcFirstLine.replace(/\{/, "");
246 | //srcFirstLine = srcFirstLine.replace(/function\s*\(\s*\)\s*\{/, "");
247 | objDoc.descHtml = srcFirstLine; // + "
";
248 | objDoc.descMd = srcFirstLine; // + "\n\n";
249 |
250 | // we have the source code for the function, so go find it, but then
251 | // look at the comments above it
252 | //var indx = widgetSrc.indexOf(obj.toString());
253 | var indx = widgetSrc.regexIndexOf(new RegExp(key + "\\s*?:\\s*?function"));
254 | if (indx > 0) {
255 |
256 | //s += "found index " + indx;
257 |
258 | // extract docs from above this method
259 | var docItem = extractDocs(indx);
260 | if (docItem.html.length > 0) {
261 | if (objDoc.descHtml.length > 0) objDoc.descHtml += '
';
262 | objDoc.descHtml += docItem.html;
263 | }
264 | if (docItem.md.length > 0) {
265 | if (objDoc.descMd.length > 0) objDoc.descMd += '\n\n';
266 | objDoc.descMd += docItem.md;
267 | }
268 | objDoc.descSrc += docItem.src;
269 | }
270 |
271 | } else if (typeof obj === 'string') {
272 | objDoc.descSrc = JSON.stringify(obj);
273 |
274 | // if there's a default value then put it in docs
275 | if (obj.length > 0) {
276 | //objDoc.descHtml += "Default value: " + JSON.stringify(obj);
277 | objDoc.descHtml += JSON.stringify(obj);
278 | objDoc.descMd += JSON.stringify(obj);
279 | }
280 |
281 | // see if any docs in src code
282 | var indx = widgetSrc.regexIndexOf(new RegExp(key + "\\s*?:"));
283 | if (indx > 0) {
284 |
285 | // extract docs from above this method
286 | var docItem = extractDocs(indx);
287 | if (docItem.html.length > 0) {
288 | if (objDoc.descHtml.length > 0) objDoc.descHtml += '
';
289 | objDoc.descHtml += docItem.html;
290 | }
291 | if (docItem.md.length > 0) {
292 | if (objDoc.descMd.length > 0) objDoc.descMd += '\n\n';
293 | objDoc.descMd += docItem.md;
294 | }
295 | objDoc.descSrc += docItem.src;
296 | }
297 |
298 |
299 | } else {
300 | objDoc.descSrc = JSON.stringify(obj, null, " ");
301 |
302 | if (key.match(/publish|subscribe|foreignPublish|foreignSubscribe/)) {
303 | objDoc.descHtml += "Please see docs above.";
304 | }
305 |
306 | // look for description above or at end of line of source code
307 |
308 | var indx = widgetSrc.regexIndexOf(new RegExp(key + "\\s*?:"));
309 | if (indx > 0) {
310 |
311 | // extract docs from above this method
312 | var docItem = extractDocs(indx);
313 | if (docItem.html.length > 0) {
314 | if (objDoc.descHtml.length > 0) objDoc.descHtml += '
';
315 | objDoc.descHtml += docItem.html;
316 | }
317 | if (docItem.md.length > 0) {
318 | if (objDoc.descMd.length > 0) objDoc.descMd += '\n\n';
319 | objDoc.descMd += docItem.md;
320 | }
321 | objDoc.descSrc += docItem.src;
322 | }
323 |
324 | }
325 |
326 | }
327 | }
328 |
329 | // We are passed in an indx which is where we start in the overall
330 | // widgetSrc. We look backwards, i.e. line/lines above for comments
331 | var extractDocs = function(indx) {
332 |
333 | var o = {
334 | html: "", // html docs
335 | src: "", // src docs
336 | md: "" // markdown docs
337 | }
338 |
339 | // if there is a */ up to this indx we've got a comment
340 | // reverse string to search backwards
341 | var partial = widgetSrc.substring(0, indx);
342 | var widgetSrcRev = reverseStr(partial);
343 | //console.log("candidate for " + key + ":", widgetSrcRev.substring(0, 100));
344 |
345 | // if the next item in rev str is /* then we have a comment
346 | if (widgetSrcRev.match(/^[\s\r\n]+\/\*/)) {
347 |
348 | // search to **/ which is /**
349 | var indx2 = widgetSrcRev.indexOf("**/");
350 | var comment = widgetSrcRev.substring(0, indx2);
351 | comment = reverseStr(comment);
352 | //console.log("comment for " + key + ":", comment);
353 | o.src = comment;
354 |
355 | // cleanup
356 | comment = comment.replace(/[\r\n\s\*\/]+$/, ""); // cleanup end
357 | var lines = comment.split(/\r?\n/);
358 | var newlines = [];
359 | for (var ctr in lines) {
360 | var line = lines[ctr];
361 | line = line.replace(/^[\s\*]+/g, "");
362 | newlines.push(line);
363 | }
364 | comment = newlines.join("\n");
365 | comment = comment.replace(/^[\s\r\n]/, ""); // cleanup beginning
366 |
367 | // convert two newlines to
368 | comment = comment.replace(/\n\n/g, "
");
369 |
370 | // put more space in front of @param
371 | comment = comment.replace(/\@param\s+?(\S+)\s+?(\S+)\s*?\-?\s*?/g, "
$2 ($1) ");
372 |
373 | //console.log("clean comment for " + key + " " + comment);
374 | o.html += comment;
375 | // make it work for markdown
376 | o.md += comment.replace("
", "\n\n").replace(/|<\/b>/g, "");
377 |
378 | }
379 | return o;
380 | }
381 |
382 | // create our own version of cpdefine so we can use the evalWidgetJs above
383 | cpdefine = function(myid, mydeps, callback) {
384 | widget = callback();
385 | id = myid;
386 | deps = mydeps;
387 | //console.log("cool, our own cpdefine got called. id:", id, "deps:", deps);
388 | }
389 | // define other top-level methods just to avoid errors
390 | requirejs = function() {}
391 | requirejs.config = function() {};
392 | cprequire_test = function() {};
393 |
394 | var generateWidgetReadme = function() {
395 |
396 | // First we have to eval so stuff is in memory
397 | evalWidgetJs();
398 |
399 | // Spit out Markdown docs
400 | var md = `# $widget-id
401 | $widget-desc
402 |
403 | $widget-img
404 |
405 | ## ChiliPeppr $widget-name
406 |
407 | All ChiliPeppr workspaces/widgets/elements are defined using cpdefine() which is a method
408 | that mimics require.js. Each defined object must have a unique ID so it does
409 | not conflict with other ChiliPeppr objects.
410 |
411 | | Item | Value |
412 | | ------------- | ------------- |
413 | | ID | $widget-id |
414 | | Name | $widget-name |
415 | | Description | $widget-desc |
416 | | chilipeppr.load() URL | $widget-cpurl |
417 | | Edit URL | $widget-editurl |
418 | | Github URL | $widget-giturl |
419 | | Test URL | $widget-testurl |
420 |
421 | ## Example Code for chilipeppr.load() Statement
422 |
423 | You can use the code below as a starting point for instantiating this workspace
424 | from ChiliPeppr's Edit Boot Script dialog box. The key is that you need to load
425 | your workspace inlined into the standard #pnlWorkspace div so the DOM can parse your HTML, CSS, and
426 | Javascript. Then you use cprequire() to find your workspace's Javascript and get
427 | back the instance of it to init() it.
428 |
429 | \`\`\`javascript
430 | $widget-cploadjs
431 | \`\`\`
432 |
433 | ## Publish
434 |
435 | This workspace publishes the following signals. These signals are owned by this workspace and are published to
436 | all objects inside the ChiliPeppr environment that listen to them via the
437 | chilipeppr.subscribe(signal, callback) method.
438 | To better understand how ChiliPeppr's subscribe() method works see amplify.js's documentation at http://amplifyjs.com/api/pubsub/
439 |
440 |
441 |
442 |
443 |
Signal
444 |
Description
445 |
446 |
447 |
448 | $row-publish-start
449 |
(No signals defined in this widget/element)
450 | $row-publish-end
451 |
452 |
453 |
454 | ## Subscribe
455 |
456 | This workspace subscribes to the following signals. These signals are owned by this workspace.
457 | Other objects inside the ChiliPeppr environment can publish to these signals via the chilipeppr.publish(signal, data) method.
458 | To better understand how ChiliPeppr's publish() method works see amplify.js's documentation at http://amplifyjs.com/api/pubsub/
459 |
460 |
461 |
462 |
463 |
Signal
464 |
Description
465 |
466 |
467 |
468 | $row-subscribe-start
469 |
(No signals defined in this widget/element)
470 | $row-subscribe-end
471 |
472 |
473 |
474 | ## Foreign Publish
475 |
476 | This workspace publishes to the following signals that are owned by other objects.
477 | To better understand how ChiliPeppr's subscribe() method works see amplify.js's documentation at http://amplifyjs.com/api/pubsub/
478 |
479 |
492 |
493 | ## Foreign Subscribe
494 |
495 | This workspace publishes to the following signals that are owned by other objects.
496 | To better understand how ChiliPeppr's publish() method works see amplify.js's documentation at http://amplifyjs.com/api/pubsub/
497 |
498 |
511 |
512 | ## Methods / Properties
513 |
514 | The table below shows, in order, the methods and properties inside the workspace object.
515 |
516 |
517 |
518 |
519 |
Method / Property
520 |
Type
521 |
Description
522 |
523 |
524 |
525 | $row-methods-start
526 |
(No methods or properties defined in this widget/element)
527 | $row-methods-end
528 |
529 |
530 |
531 |
532 | ## About ChiliPeppr
533 |
534 | [ChiliPeppr](http://chilipeppr.com) is a hardware fiddle, meaning it is a
535 | website that lets you easily
536 | create a workspace to fiddle with your hardware from software. ChiliPeppr provides
537 | a [Serial Port JSON Server](https://github.com/johnlauer/serial-port-json-server)
538 | that you run locally on your computer, or remotely on another computer, to connect to
539 | the serial port of your hardware like an Arduino or other microcontroller.
540 |
541 | You then create a workspace at ChiliPeppr.com that connects to your hardware
542 | by starting from scratch or forking somebody else's
543 | workspace that is close to what you are after. Then you write widgets in
544 | Javascript that interact with your hardware by forking the base template
545 | widget or forking another widget that
546 | is similar to what you are trying to build.
547 |
548 | ChiliPeppr is massively capable such that the workspaces for
549 | [TinyG](http://chilipeppr.com/tinyg) and [Grbl](http://chilipeppr.com/grbl) CNC
550 | controllers have become full-fledged CNC machine management software used by
551 | tens of thousands.
552 |
553 | ChiliPeppr has inspired many people in the hardware/software world to use the
554 | browser and Javascript as the foundation for interacting with hardware. The
555 | Arduino team in Italy caught wind of ChiliPeppr and now
556 | ChiliPeppr's Serial Port JSON Server is the basis for the
557 | [Arduino's new web IDE](https://create.arduino.cc/). If the Arduino team is excited about building on top
558 | of ChiliPeppr, what
559 | will you build on top of it?
560 |
561 | `
562 |
563 | /*
564 | var widgetUrl = 'http://' +
565 | process.env.C9_PROJECT + '-' + process.env.C9_USER +
566 | '.c9users.io/widget.html';
567 | var testUrl = 'https://preview.c9users.io/' +
568 | process.env.C9_USER + '/' +
569 | process.env.C9_PROJECT + '/widget.html';
570 | var editUrl = 'http://ide.c9.io/' +
571 | process.env.C9_USER + '/' +
572 | process.env.C9_PROJECT;
573 | var github = getGithubUrl();
574 | */
575 |
576 | md = md.replace(/\$widget-id/g, widget.id);
577 | md = md.replace(/\$widget-name/g, widget.name);
578 | md = md.replace(/\$widget-desc/g, widget.desc);
579 | md = md.replace(/\$widget-cpurl/g, github.rawurl);
580 | md = md.replace(/\$widget-editurl/g, editUrl);
581 | md = md.replace(/\$widget-giturl/g, github.url);
582 | md = md.replace(/\$widget-testurl/g, testUrl);
583 |
584 | var cpload = generateCpLoadStmt();
585 | md = md.replace(/\$widget-cploadjs/g, cpload);
586 |
587 | // see if there is a screenshot, if so use it
588 | var img = "";
589 | if (fs.existsSync("screenshot.png")) {
590 | img = "![alt text]" +
591 | "(screenshot.png \"Screenshot\")";
592 | }
593 | md = md.replace(/\$widget-img/g, img);
594 |
595 | /*
596 | // now generate methods/properties
597 | //$widget-methprops
598 | var s = "";
599 | for (var key in widget) {
600 | var obj = widget[key];
601 | s += '| ' + key +
602 | ' | ' + typeof obj +
603 | ' | ';
604 | s += widgetDocs[key].descHtml.replace(/[\r\n]/g, "");
605 | s += ' |\n';
606 | }
607 | //console.log("adding markdown:", s);
608 | md = md.replace(/\$widget-methprops/g, s);
609 |
610 |
611 | // now do pubsub signals
612 | var s;
613 | s = appendKeyValForMarkdown(widget.publish);
614 | md = md.replace(/\$widget-publish/, s);
615 | s = appendKeyValForMarkdown(widget.subscribe);
616 | md = md.replace(/\$widget-subscribe/, s);
617 | s = appendKeyValForMarkdown(widget.foreignPublish);
618 | md = md.replace(/\$widget-foreignpublish/, s);
619 | s = appendKeyValForMarkdown(widget.foreignSubscribe);
620 | md = md.replace(/\$widget-foreignsubscribe/, s);
621 | */
622 |
623 | // do the properties and methods
624 | var s = "";
625 | for (var key in widget) {
626 | var txt = widgetDocs[key].descHtml + '';
627 | // get rid of spaces and returns after closing pre tags cuz it messes up github markdown
628 | txt = txt.replace(/<\/pre>[\s\r\n]*/ig, "");
629 | // convert double newlines to
tags
630 | txt = txt.replace(/\n\s*\n\s*/g, "
");
631 |
632 | var obj = widget[key];
633 | s += '
' + key +
634 | '
' + typeof obj +
635 | '
';
636 | s += txt;
637 | s += '
';
638 | }
639 | md = md.replace(/\$row-methods-start[\s\S]+?\$row-methods-end/g, s);
640 |
641 | // now do pubsub signals
642 | var s;
643 | s = appendKeyVal(widget.publish);
644 | md = md.replace(/\$row-publish-start[\s\S]+?\$row-publish-end/, s);
645 | s = appendKeyVal(widget.subscribe);
646 | md = md.replace(/\$row-subscribe-start[\s\S]+?\$row-subscribe-end/g, s);
647 | s = appendKeyVal(widget.foreignPublish);
648 | md = md.replace(/\$row-foreign-publish-start[\s\S]+?\$row-foreign-publish-end/, s);
649 | s = appendKeyVal(widget.foreignSubscribe);
650 | md = md.replace(/\$row-foreign-subscribe-start[\s\S]+?\$row-foreign-subscribe-end/g, s);
651 |
652 | // now write out the auto-gen file
653 | fs.writeFileSync("README.md", md);
654 | console.log("Rewrote README.md");
655 |
656 | }
657 |
658 | var appendKeyValForMarkdown = function(data, id) {
659 | var str = "";
660 | if (data != null && typeof data === 'object' && Object.keys(data).length > 0) {
661 |
662 | //var keys = Object.keys(data);
663 | for (var key in data) {
664 | str += '| /' + widget.id + "" + key + ' | ' + data[key].replace(/\n/, " ") + ' |';
665 | }
666 | } else {
667 | str = '| (No signals defined in this widget/element) |';
668 | }
669 | return str;
670 | }
671 |
672 | var generateWidgetDocs = function() {
673 |
674 | // First we have to eval so stuff is in memory
675 | evalWidgetJs();
676 |
677 | // Spit out docs
678 | var html = "";
679 |
680 | html += `
681 |
682 |
683 | $pubsub-name
684 |
685 |
686 |
687 |
688 |
689 |
690 |
692 |
693 |
771 |
772 |
773 |
774 |
775 |
776 |
777 |
778 |
779 |
780 |
You can use the code below as a starting point for instantiating
850 | this workspace from ChiliPeppr's Edit Boot Script dialog. The key is that
851 | you need to load your workspace inlined into the #pnlWorkspace div so the DOM can parse
852 | your HTML, CSS, and Javascript. Then you use cprequire() to find
853 | your workspace's Javascript and get back the instantiated instance of it.
854 |
855 |
$cp-load-stmt
857 |
858 |
859 |
860 |
861 |
Interface Implementation
862 |
This widget/element implements an interface specification. Since
863 | Javascript does not have the notion of interfaces like the way languages
864 | such as Java have native support for interfaces, ChiliPeppr has defined
865 | its own loose version of an interface. If this widget/element has
866 | implemented an interface, it means it has followed a general standard
867 | set of pubsub signals that other widgets/elements should follow as well
868 | to make them swappable.
869 |
870 |
871 |
872 |
873 |
Interface Implementation
874 |
Description
875 |
876 |
877 |
878 |
879 |
880 |
881 |
882 |
883 |
Publish
884 |
This widget/element publishes the following signals. These signals are owned by this widget/element and are published to all objects inside the ChiliPeppr environment that listen to them via the chilipeppr.subscribe(signal, callback) method.
885 |
886 |
887 |
888 |
Signal
889 |
Description
890 |
891 |
892 |
893 |
894 | $row-publish-start
895 |
(No signals defined in this widget/element)
896 | $row-publish-end
897 |
898 |
899 |
900 |
901 |
Subscribe
902 |
This widget/element subscribes to the following signals. These signals are owned by this widget/element. Other objects inside the ChiliPeppr environment can publish to these signals via the chilipeppr.publish(signal, data) method.
The list below shows, in order, the methods and properties that exist
957 | inside this widget/element.
958 |
959 |
960 |
961 |
Method / Property
962 |
Type
963 |
Description
964 |
965 |
966 |
967 |
968 | $row-methods-start
969 |
(No methods or properties defined in this widget/element)
970 | $row-methods-end
971 |
972 |
973 |
974 |
975 |
976 |
977 |
Structure of a Workspace
978 |
The standard structure of a ChiliPeppr workspace includes making
979 | your workspace out of workspace.js, workspace.css, and workspace.html. The final
980 | workspace has everything inlined into one HTML file. It is important
981 | to have everything inlined so the chilipeppr.load() method succeeds
982 | because it only loads a single URL.
983 |
984 |
985 |
When this NodeJs page is executed it will combine
986 | your workspace.js, workspace.css, and workspace.html files into a monolithic
987 | HTML file called auto-generated-workspace.html. You should use this file
988 | as your final workspace inlined file.
989 |
990 |
This NodeJs script
991 | will also push your updated content to your forked repo on Github
992 | whenever it is run so that Github is as up-to-date
993 | as possible. This script simply runs the git-push.sh script as if
994 | you ran it on your own from the command line.
995 |
996 |
997 |
998 |
999 | `;
1000 |
1001 | /*
1002 | var widgetUrl = 'http://' +
1003 | process.env.C9_PROJECT + '-' + process.env.C9_USER +
1004 | '.c9users.io/widget.html';
1005 | var testUrl = 'https://preview.c9users.io/' +
1006 | process.env.C9_USER + '/' +
1007 | process.env.C9_PROJECT + '/widget.html';
1008 | var testUrlNoSsl = 'http://' + process.env.C9_PROJECT +
1009 | '-' + process.env.C9_USER + '.c9users.io/widget.html';
1010 | var editUrl = 'http://ide.c9.io/' +
1011 | process.env.C9_USER + '/' +
1012 | process.env.C9_PROJECT;
1013 | var github = getGithubUrl();
1014 | */
1015 |
1016 | html = html.replace(/\$pubsub-id/g, widget.id);
1017 | html = html.replace(/\$pubsub-name/g, widget.name);
1018 | html = html.replace(/\$pubsub-desc/g, widget.desc);
1019 | html = html.replace(/\$pubsub-url/g, github.rawurl);
1020 | html = html.replace(/\$pubsub-fiddleurl/g, editUrl);
1021 | html = html.replace(/\$pubsub-github/g, github.url);
1022 | html = html.replace(/\$pubsub-testurlnossl/g, testUrlNoSsl);
1023 | html = html.replace(/\$pubsub-testurl/g, testUrl);
1024 |
1025 | var cpload = generateCpLoadStmt();
1026 | html = html.replace(/\$cp-load-stmt/g, cpload);
1027 |
1028 | // do the properties and methods
1029 | var s = "";
1030 | for (var key in widget) {
1031 | var obj = widget[key];
1032 | s += '
' + key +
1033 | '
' + typeof obj +
1034 | '
';
1035 | s += widgetDocs[key].descHtml;
1036 | s += '
';
1037 | }
1038 | html = html.replace(/\$row-methods-start[\s\S]+?\$row-methods-end/g, s);
1039 |
1040 | // now do pubsub signals
1041 | var s;
1042 | s = appendKeyVal(widget.publish);
1043 | html = html.replace(/\$row-publish-start[\s\S]+?\$row-publish-end/, s);
1044 | s = appendKeyVal(widget.subscribe);
1045 | html = html.replace(/\$row-subscribe-start[\s\S]+?\$row-subscribe-end/g, s);
1046 | s = appendKeyVal(widget.foreignPublish);
1047 | html = html.replace(/\$row-foreign-publish-start[\s\S]+?\$row-foreign-publish-end/, s);
1048 | s = appendKeyVal(widget.foreignSubscribe);
1049 | html = html.replace(/\$row-foreign-subscribe-start[\s\S]+?\$row-foreign-subscribe-end/g, s);
1050 |
1051 | // debug source for widget
1052 | /*
1053 | html = html.replace(
1054 | /\$fullwidget/,
1055 | widget.toString().replace(/\n/g, " ").replace(/ /g, " ")
1056 | );
1057 | */
1058 |
1059 | return html;
1060 | }
1061 |
1062 | var reverseStr = function(s) {
1063 | var o = '';
1064 | for (var i = s.length - 1; i >= 0; i--)
1065 | o += s[i];
1066 | return o;
1067 | }
1068 |
1069 | var appendKeyVal = function(data, id) {
1070 | var str = "";
1071 | if (data != null && typeof data === 'object' && Object.keys(data).length > 0) {
1072 |
1073 | //var keys = Object.keys(data);
1074 | for (var key in data) {
1075 |
1076 | // clean up the description text
1077 | var txt = data[key] + '';
1078 | // get rid of spaces and returns after closing pre tags cuz it messes up github markdown
1079 | txt = txt.replace(/<\/pre>[\s\r\n]*/ig, "");
1080 | // convert double newlines to