Browse Source

An in-place browser-based configurator utility

- A web form with fields for configuration values
- Reads and parses standard config files
- Modifies configurations dynamically in-place
Scott Lahteine 9 years ago
parent
commit
ef18bfdd3f

+ 27
- 0
Marlin/configurator/css/configurator.css View File

@@ -0,0 +1,27 @@
1
+/* configurator.css */
2
+/* Styles for Marlin Configurator */
3
+
4
+body { margin: 0; padding: 0; background: #458; color: #FFC; font-family: sans-serif; }
5
+#main { float: left; width: 100%; margin-right: -100%; }
6
+#main { padding: 0 4%; width: 92%; }
7
+#main { font-family: monospace; }
8
+.info { color: #AAF; }
9
+.info span { color: #FFF; }
10
+.info span span { color: #000; font-weight: bold; }
11
+p { width: 80%; color: #FF0; }
12
+#help strong { color: #0DD; }
13
+img { display: none; }
14
+label, input, select, textarea { display: block; float: left; margin: 1px 0; }
15
+label.newline, textarea { clear: both; }
16
+label { width: 130px; height: 1em; padding: 10px 480px 10px 1em; margin-right: -470px; text-align: right; }
17
+input[type="text"], select, .jstepper { margin: 0.75em 0 0; }
18
+input[type="checkbox"], input[type="radio"] { margin: 1em 0 0; }
19
+#config_form { display: block; background: #DDD; padding: 20px; color: #000; }
20
+/*#config_text, #config_adv_text { font-family: "Andale mono", monospace; clear: both; }*/
21
+#config_text, #config_adv_text { height: 25em; overflow: auto; background-color: #FFF; color: #888; padding: 10px; }
22
+input[type="checkbox"], input[type="radio"].enabler { margin-left: 1em; }
23
+input:disabled { color: #BBB; }
24
+.clear { clear: both; }
25
+h1, h2, h3, h4, h5, h6 { clear: both; }
26
+h2 { margin: 0; padding: 1em 0 0; }
27
+.jstepper { display: block; }

+ 52
- 0
Marlin/configurator/index.html View File

@@ -0,0 +1,52 @@
1
+<html>
2
+  <head>
3
+    <title>Marlin Configurator</title>
4
+    <meta charset="UTF-8">
5
+    <script type="text/javascript" src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
6
+    <script src="js/configurator.js"></script>
7
+    <script src="js/jcanvas.js"></script>
8
+    <script src="js/jstepper.js"></script>
9
+    <link rel="stylesheet" href="css/configurator.css" type="text/css" media="all" />
10
+  </head>
11
+  <body>
12
+    <div id="main">
13
+      <h1>Marlin Configurator 0.1a</h1>
14
+      <p>Enter values in the form, get a Marlin configuration.<br/>Will include a drop-down of known configurations.</p>
15
+      <ul id="help">
16
+        <li><strong>HELP</strong> - This is the help region</li>
17
+      </ul>
18
+
19
+      <form id="config_form">
20
+
21
+        <!-- label>Serial Port:</label><input name="SERIAL_PORT" type="text" size="4" maxlength="2" value="99" / -->
22
+
23
+        <label>Serial Port:</label><select name="SERIAL_PORT"></select><div id="serial_stepper"></div>
24
+
25
+        <label>Baud Rate:</label><select name="BAUDRATE"></select>
26
+
27
+        <label>AT90USB BT IF:</label>
28
+          <input name="BTENABLED" type="checkbox" value="1" checked />
29
+
30
+        <label class="newline">Motherboard:</label><select name="MOTHERBOARD"></select>
31
+
32
+        <label class="newline">Custom Name:</label><input name="CUSTOM_MENDEL_NAME" class="switchable" type="text" size="14" maxlength="12" value="" />
33
+
34
+        <label class="newline">Machine UUID:</label><input name="MACHINE_UUID" class="switchable" type="text" size="38" maxlength="36" value="" />
35
+
36
+        <label class="newline">Extruders:</label><select name="EXTRUDERS"></select>
37
+
38
+        <label class="newline">Power Supply:</label><select name="POWER_SUPPLY"></select>
39
+
40
+        <label>PS Default Off:</label>
41
+          <input name="PS_DEFAULT_OFF" type="checkbox" value="1" checked />
42
+
43
+        <h2>Marlin/Configuration.h</h2>
44
+        <pre id="config_text" class="prettyprint linenums"></pre>
45
+        <h2>Marlin/Configuration_adv.h</h2>
46
+        <pre id="config_adv_text" class="prettyprint linenums"></pre>
47
+
48
+        <br class="clear" />
49
+      </form>
50
+    </div>
51
+  </body>
52
+</html>

+ 293
- 0
Marlin/configurator/js/configurator.js View File

@@ -0,0 +1,293 @@
1
+/**
2
+ * configurator.js
3
+ *
4
+ * Marlin Configuration Utility
5
+ *    - Web form for entering configuration options
6
+ *    - A reprap calculator to calculate movement values
7
+ *    - Uses HTML5 to generate downloadables in Javascript
8
+ *    - Reads and parses standard configuration files from local folders
9
+ *
10
+ * Supporting functions
11
+ *    - Parser to read Marlin Configuration.h and Configuration_adv.h files
12
+ *    - Utilities to replace values in configuration files
13
+ */
14
+$(function(){
15
+
16
+var marlin_config = '..';
17
+
18
+// Extend String
19
+String.prototype.lpad = function(len, chr) {
20
+  if (chr === undefined) { chr = '&nbsp;'; }
21
+  var s = this+'', need = len - s.length;
22
+  if (need > 0) { s = new Array(need+1).join(chr) + s; }
23
+  return s;
24
+};
25
+
26
+String.prototype.prePad = function(len, chr) {
27
+  return len ? this.lpad(len, chr) : this;
28
+};
29
+
30
+String.prototype.zeroPad = function(len) {
31
+  return len ? this.prePad(len, '0') : this;
32
+};
33
+
34
+/**
35
+ * selectField.addOptions takes an array or keyed object
36
+ */
37
+$.fn.extend({
38
+  addOptions: function(arrObj) {
39
+    return this.each(function() {
40
+      var sel = $(this);
41
+      var isArr = Object.prototype.toString.call(arrObj) == "[object Array]";
42
+      $.each(arrObj, function(k, v) {
43
+        sel.append( $('<option>',{value:isArr?v:k}).text(v) );
44
+      });
45
+    });
46
+  }
47
+});
48
+
49
+// The app is a singleton
50
+var configuratorApp = (function(){
51
+
52
+  // private variables and functions go here
53
+  var self,
54
+      pi2 = Math.PI * 2,
55
+      $config = $('#config_text'),
56
+      $config_adv = $('#config_adv_text'),
57
+      boards_list = {},
58
+      therms_list = {};
59
+
60
+  // Return this anonymous object as configuratorApp
61
+  return {
62
+    my_public_var: 4,
63
+
64
+    init: function() {
65
+      self = this; // a 'this' for use when 'this' is something else
66
+
67
+      // Read boards.h
68
+      boards_list = {};
69
+      $.get(marlin_config + "/boards.h", function(txt) {
70
+        // Get all the boards and save them into an object
71
+        var r, findDef = new RegExp('[ \\t]*#define[ \\t]+(BOARD_[^ \\t]+)[ \\t]+(\\d+)[ \\t]*(//[ \\t]*)?(.+)?', 'mg');
72
+        while((r = findDef.exec(txt)) !== null) {
73
+          boards_list[r[1]] = r[2].prePad(r[2] < 100 ? (r[2] < 10 ? 5 : 4) : 0, ' ') + " — " + r[4].replace(/\).*/, ')');
74
+        }
75
+      });
76
+
77
+      // Read Configuration.h
78
+      $.get(marlin_config+"/Configuration.h", function(txt) {
79
+        $config.text(txt);
80
+      });
81
+
82
+      // Read Configuration.h
83
+      $.get(marlin_config+"/Configuration_adv.h", function(txt) {
84
+        $config_adv.text(txt);
85
+        self.setupConfigForm();
86
+      });
87
+
88
+    },
89
+
90
+    setupConfigForm: function() {
91
+      // Modify form fields and make the form responsive.
92
+      // As values change on the form, we could update the
93
+      // contents of text areas containing the configs, for
94
+      // example.
95
+
96
+      // while(!$config_adv.text() == null) {}
97
+      // while(!$config.text() == null) {}
98
+
99
+      // Go through all form items with names
100
+      $('#config_form').find('[name]').each(function() {
101
+        // Set its id to its name
102
+        var name = $(this).attr('name');
103
+        $(this).attr({id: name});
104
+        // Attach its label sibling
105
+        var $label = $(this).prev();
106
+        if ($label[0].tagName == 'LABEL') {
107
+          $label.attr('for',name);
108
+        }
109
+      });
110
+
111
+      // Get all 'switchable' class items and add a checkbox
112
+      $('#config_form .switchable').each(function(){
113
+        $(this).after(
114
+          $('<input>',{type:'checkbox',value:'1',class:'enabler'}).prop('checked',true)
115
+            .attr('id',this.id + '-switch')
116
+            .change(self.handleSwitch)
117
+        );
118
+      });
119
+
120
+      /**
121
+       * For now I'm manually creating these references
122
+       * but I should be able to parse Configuration.h
123
+       * and iterate the #defines.
124
+       *
125
+       * For any #ifdef blocks I can create field groups
126
+       * which can be dimmed together when the option
127
+       * is disabled.
128
+       *
129
+       * Then we only need to specify exceptions to
130
+       * standard behavior, (which is to add a text field)
131
+       */
132
+      $('#SERIAL_PORT').addOptions([0,1,2,3,4,5,6,7]);
133
+      this.initField('SERIAL_PORT');
134
+
135
+      $('#BAUDRATE').addOptions([2400,9600,19200,38400,57600,115200,250000]);
136
+      this.initField('BAUDRATE');
137
+
138
+      this.initField('BTENABLED');
139
+
140
+      $('#MOTHERBOARD').addOptions(boards_list);
141
+      this.initField('MOTHERBOARD');
142
+
143
+      this.initField('CUSTOM_MENDEL_NAME');
144
+
145
+      this.initField('MACHINE_UUID');
146
+
147
+      $('#EXTRUDERS').addOptions([1,2,3,4]);
148
+      this.initField('EXTRUDERS');
149
+
150
+      $('#POWER_SUPPLY').addOptions({'1':'ATX','2':'Xbox 360'});
151
+      this.initField('POWER_SUPPLY');
152
+
153
+      this.initField('PS_DEFAULT_OFF');
154
+
155
+/*
156
+      $('#serial_stepper').jstepper({
157
+        min: 0,
158
+        max: 3,
159
+        val: $('#SERIAL_PORT').val(),
160
+        arrowWidth: '18px',
161
+        arrowHeight: '15px',
162
+        color: '#FFF',
163
+        acolor: '#F70',
164
+        hcolor: '#FF0',
165
+        id: 'select-me',
166
+        stepperClass: 'inner',
167
+        textStyle: {width:'1.5em',fontSize:'120%',textAlign:'center'},
168
+        // onChange: function(v) { },
169
+      });
170
+*/
171
+
172
+      // prettyPrint();
173
+    },
174
+
175
+    initField: function(name) {
176
+      var $elm = $('#'+name), isText = $elm.attr('type') == 'text';
177
+      this.setFieldFromDefine(name);
178
+      isText ? $elm.bind('input', this.handleChange) : $elm.change(this.handleChange)
179
+    },
180
+
181
+    handleChange: function(e) {
182
+      self.updateDefineForField(e.target);
183
+    },
184
+
185
+    handleSwitch: function(e) {
186
+      var $elm = $(e.target), $prev = $elm.prev();
187
+      var on = $elm.prop('checked') || false;
188
+      $prev.attr('disabled', !on);
189
+      self.setDefineEnabled($prev[0], on);
190
+    },
191
+
192
+    setDefineEnabled: function(elm, val) {
193
+      var $elm = $(elm);
194
+
195
+      // console.log("Enable: " + elm.id + " = " + val);
196
+
197
+      var txt = $config.text();
198
+
199
+      var findDef = new RegExp('^[ \\t]*(//[ \\t]*)?(#define[ \\t]+' + elm.id + '([ \\t].*)?)$', 'm');
200
+      txt = txt.replace(findDef, val ? '$2': '//$2');
201
+
202
+      // Now set the text in the field
203
+      $config.text(txt);
204
+    },
205
+
206
+    updateDefineForField: function(elm) {
207
+      var $elm = $(elm),
208
+          isCheck = $elm.attr('type') == 'checkbox',
209
+          val = isCheck ? $elm.prop('checked') : $elm.val();
210
+
211
+      // console.log("Set: " + elm.id + " = " + val);
212
+
213
+      var txt = $config.text();
214
+
215
+      if (isCheck) {
216
+        var findDef = new RegExp('^([ \\t]*)(//[ \\t]*)?(#define[ \\t]+' + elm.id + '([ \\t].*)?)$', 'm');
217
+        txt = txt.replace(findDef, val ? '$1$3': '$1//$3');
218
+      }
219
+      else {
220
+        // Match the define name, 1 = "#define name ", 3=value, 4=comment
221
+        var findDef = new RegExp('^([ \\t]*(//)?[ \\t]*#define[ \\t]+' + elm.id + '[ \\t]+)(.*)([ \\t]*(//)?.*)$', 'm');
222
+        if ($elm.hasClass('quote')) val = '"' + val + '"'
223
+        txt = txt.replace(findDef, '$1!!REGEXP!!$4').replace('!!REGEXP!!', val);
224
+      }
225
+      // Now set the text in the field
226
+      $config.text(txt);
227
+
228
+      // $config.scrollTo(500);
229
+    },
230
+
231
+    setFieldFromDefine: function(name, adv) {
232
+      var $elm = $('#'+name), elm = $elm[0];
233
+      var isCheck = $elm.attr('type') == 'checkbox';
234
+      var val = this.defineValue(name, adv);
235
+
236
+      isCheck ? $elm.prop('checked', val) : $elm.val("" + val);
237
+
238
+      // If the item has a checkbox then set enabled state too
239
+      var $cb = $('#'+name+'-switch');
240
+      if ($cb.length) {
241
+        var on = self.defineIsEnabled(name,adv);
242
+        $elm.attr('disabled', !on);
243
+        $cb.prop('checked', on);
244
+      }
245
+    },
246
+
247
+    defineValue: function(name, adv) {
248
+      var $elm = $('#'+name), elm = $elm[0];
249
+      var $c = adv ? $config_adv : $config;
250
+      if ($elm.attr('type') == 'checkbox') {
251
+        // maybe spaces, maybe comment, #define, spaces, name, maybe a comment
252
+        var findDef = new RegExp('^[ \\t]*(//[ \\t]*)?(#define[ \\t]+' + elm.id + '([ \\t]*(//)?.*)?)$', 'm');
253
+        var result = findDef.exec($c.text());
254
+        return (result ? result[1] == undefined || result[1].trim() != '//' : false);
255
+      }
256
+      else {
257
+        // maybe spaces, maybe comment, #define, spaces, name, one or more spaces, value, maybe a comment
258
+        var findDef = new RegExp('^([ \\t]*(//[ \\t]*)?#define[ \\t]+' + elm.id + '[ \\t]+)(.*)([ \\t]*(//)?.*)$', 'm');
259
+        var result = findDef.exec($c.text());
260
+        if (result !== null) {
261
+          var val = result[3];
262
+          if (val.search(/^".*"$/) >= 0) $elm.addClass('quote');
263
+          if ($elm.hasClass('quote')) val = val.replace(/^"(.*)"$/, '$1');
264
+          return val;
265
+        }
266
+        return 'fail';
267
+      }
268
+    },
269
+
270
+    defineIsEnabled: function(name, adv) {
271
+      var $elm = $('#'+name);
272
+      var $c = adv ? $config_adv : $config;
273
+      var findDef = new RegExp('^[ \\t]*(//[ \\t]*)?(#define[ \\t]+' + name + '([ \\t]*(//)?.*)?)$', 'm');
274
+      var result = findDef.exec($c.text());
275
+      return (result ? result[1].trim() != '//' : false);
276
+    },
277
+
278
+    logOnce: function(o) {
279
+      if (typeof o.didLogThisObject === 'undefined') {
280
+        console.log(o);
281
+        o.didLogThisObject = true;
282
+      }
283
+    },
284
+
285
+    EOF: null
286
+  };
287
+
288
+})();
289
+
290
+// Typically the app would be in its own file, but this would be here
291
+configuratorApp.init();
292
+
293
+});

+ 524
- 0
Marlin/configurator/js/jcanvas.js View File

@@ -0,0 +1,524 @@
1
+/*!
2
+  jCanvas v2.2.1
3
+  Caleb Evans
4
+  2.2.1 revisions by Thinkyhead
5
+*/
6
+
7
+(function($, document, Math, Number, undefined) {
8
+
9
+// jC global object
10
+var jC = {};
11
+jC.originals = {
12
+	width: 20,
13
+	height: 20,
14
+	cornerRadius: 0,
15
+	fillStyle: 'transparent',
16
+	strokeStyle: 'transparent',
17
+	strokeWidth: 5,
18
+	strokeCap: 'butt',
19
+	strokeJoin: 'miter',
20
+	shadowX: 0,
21
+	shadowY: 0,
22
+	shadowBlur: 10,
23
+	shadowColor: 'transparent',
24
+	x: 0, y: 0,
25
+	x1: 0, y1: 0,
26
+	radius: 10,
27
+	start: 0,
28
+	end: 360,
29
+	ccw: false,
30
+	inDegrees: true,
31
+	fromCenter: true,
32
+	closed: false,
33
+	sides: 3,
34
+	angle: 0,
35
+	text: '',
36
+	font: 'normal 12pt sans-serif',
37
+	align: 'center',
38
+	baseline: 'middle',
39
+	source: '',
40
+	repeat: 'repeat'
41
+};
42
+// Duplicate original defaults
43
+jC.defaults = $.extend({}, jC.originals);
44
+
45
+// Set global properties
46
+function setGlobals(context, map) {
47
+	context.fillStyle = map.fillStyle;
48
+	context.strokeStyle = map.strokeStyle;
49
+	context.lineWidth = map.strokeWidth;
50
+	context.lineCap = map.strokeCap;
51
+	context.lineJoin = map.strokeJoin;
52
+	context.shadowOffsetX = map.shadowX;
53
+	context.shadowOffsetY = map.shadowY;
54
+	context.shadowBlur = map.shadowBlur;
55
+	context.shadowColor = map.shadowColor;
56
+}
57
+
58
+// Close path if chosen
59
+function closePath(context, map) {
60
+	if (map.closed === true) {
61
+		context.closePath();
62
+		context.fill();
63
+		context.stroke();
64
+	} else {
65
+		context.fill();
66
+		context.stroke();
67
+		context.closePath();
68
+	}
69
+}
70
+
71
+// Measure angles in degrees if chosen
72
+function checkUnits(map) {
73
+	if (map.inDegrees === true) {
74
+		return Math.PI / 180;
75
+	} else {
76
+		return 1;
77
+	}
78
+}
79
+
80
+// Set canvas defaults
81
+$.fn.canvas = function(args) {
82
+	// Reset defaults if no value is passed
83
+	if (typeof args === 'undefined') {
84
+		jC.defaults = jC.originals;
85
+	} else {
86
+		jC.defaults = $.extend({}, jC.defaults, args);
87
+	}
88
+	return this;
89
+};
90
+
91
+// Load canvas
92
+$.fn.loadCanvas = function(context) {
93
+	if (typeof context === 'undefined') {context = '2d';}
94
+	return this[0].getContext(context);
95
+};
96
+
97
+// Create gradient
98
+$.fn.gradient = function(args) {
99
+	var ctx = this.loadCanvas(),
100
+		// Specify custom defaults
101
+		gDefaults = {
102
+			x1: 0, y1: 0,
103
+			x2: 0, y2: 0,
104
+			r1: 10, r2: 100
105
+		},
106
+		params = $.extend({}, gDefaults, args),
107
+		gradient, stops = 0, percent, i;
108
+		
109
+	// Create radial gradient if chosen
110
+	if (typeof args.r1 === 'undefined' && typeof args.r2 === 'undefined') {
111
+		gradient = ctx.createLinearGradient(params.x1, params.y1, params.x2, params.y2);
112
+	} else {
113
+		gradient = ctx.createRadialGradient(params.x1, params.y1, params.r1, params.x2, params.y2, params.r2);
114
+	}
115
+	
116
+	// Count number of color stops
117
+	for (i=1; i<=Number.MAX_VALUE; i+=1) {
118
+		if (params['c' + i]) {
119
+			stops += 1;
120
+		} else {
121
+			break;
122
+		}
123
+	}
124
+	
125
+	// Calculate color stop percentages if absent
126
+	for (i=1; i<=stops; i+=1) {
127
+		percent = Math.round((100 / (stops-1)) * (i-1)) / 100;
128
+		if (typeof params['s' + i] === 'undefined') {
129
+			params['s' + i] = percent;
130
+		}
131
+		gradient.addColorStop(params['s' + i], params['c' + i]);
132
+	}
133
+	return gradient;
134
+};
135
+
136
+// Create pattern
137
+$.fn.pattern = function(args) {
138
+	var ctx = this.loadCanvas(),
139
+		params = $.extend({}, jC.defaults, args),
140
+		pattern,
141
+		img = document.createElement('img');
142
+	img.src = params.source;
143
+	
144
+	// Create pattern
145
+	function create() {
146
+		if (img.complete === true) {
147
+			// Create pattern
148
+			pattern = ctx.createPattern(img, params.repeat);
149
+		} else {
150
+			throw "The pattern has not loaded yet";
151
+		}
152
+	}
153
+	try {
154
+		create();
155
+	} catch(error) {
156
+		img.onload = create;
157
+	}
158
+	return pattern;
159
+};
160
+
161
+// Clear canvas
162
+$.fn.clearCanvas = function(args) {
163
+	var ctx = this.loadCanvas(),
164
+		params = $.extend({}, jC.defaults, args);
165
+
166
+	// Draw from center if chosen
167
+	if (params.fromCenter === true) {
168
+		params.x -= params.width / 2;
169
+		params.y -= params.height / 2;
170
+	}
171
+
172
+	// Clear entire canvas if chosen
173
+	ctx.beginPath();
174
+	if (typeof args === 'undefined') {
175
+		ctx.clearRect(0, 0, this.width(), this.height());
176
+	} else {
177
+		ctx.clearRect(params.x, params.y, params.width, params.height);
178
+	} 
179
+	ctx.closePath();
180
+	return this;
181
+};
182
+
183
+// Save canvas
184
+$.fn.saveCanvas = function() {
185
+	var ctx = this.loadCanvas();
186
+	ctx.save();
187
+	return this;
188
+};
189
+
190
+// Restore canvas
191
+$.fn.restoreCanvas = function() {
192
+	var ctx = this.loadCanvas();
193
+	ctx.restore();
194
+	return this;
195
+};
196
+
197
+// Scale canvas
198
+$.fn.scaleCanvas = function(args) {
199
+	var ctx = this.loadCanvas(),
200
+		params = $.extend({}, jC.defaults, args);
201
+	ctx.save();
202
+	ctx.translate(params.x, params.y);
203
+	ctx.scale(params.width, params.height);
204
+	ctx.translate(-params.x, -params.y)
205
+	return this;
206
+};
207
+
208
+// Translate canvas
209
+$.fn.translateCanvas = function(args) {
210
+	var ctx = this.loadCanvas(),
211
+		params = $.extend({}, jC.defaults, args);
212
+	ctx.save();
213
+	ctx.translate(params.x, params.y);		
214
+	return this;
215
+};
216
+
217
+// Rotate canvas
218
+$.fn.rotateCanvas = function(args) {
219
+	var ctx = this.loadCanvas(),
220
+		params = $.extend({}, jC.defaults, args),
221
+		toRad = checkUnits(params);
222
+	
223
+	ctx.save();
224
+	ctx.translate(params.x, params.y);
225
+	ctx.rotate(params.angle * toRad);
226
+	ctx.translate(-params.x, -params.y);
227
+	return this;
228
+};
229
+
230
+// Draw rectangle
231
+$.fn.drawRect = function(args) {
232
+	var ctx = this.loadCanvas(),
233
+		params = $.extend({}, jC.defaults, args),
234
+		toRad = checkUnits(params),
235
+		x1, y1, x2, y2, r;
236
+	setGlobals(ctx, params);
237
+	
238
+	// Draw from center if chosen
239
+	if (params.fromCenter === true) {
240
+		params.x -= params.width / 2;
241
+		params.y -= params.height / 2;
242
+	}
243
+		
244
+	// Draw rounded rectangle if chosen
245
+	if (params.cornerRadius > 0) {
246
+		x1 = params.x;
247
+		y1 = params.y;
248
+		x2 = params.x + params.width;
249
+		y2 = params.y + params.height;
250
+		r = params.cornerRadius;
251
+		if ((x2 - x1) - (2 * r) < 0) {
252
+			r = (x2 - x1) / 2;
253
+		}
254
+		if ((y2 - y1) - (2 * r) < 0) {
255
+			r = (y2 - y1) / 2;
256
+		}
257
+		ctx.beginPath();
258
+		ctx.moveTo(x1+r,y1);
259
+		ctx.lineTo(x2-r,y1);
260
+		ctx.arc(x2-r, y1+r, r, 270*toRad, 360*toRad, false);
261
+		ctx.lineTo(x2,y2-r);
262
+		ctx.arc(x2-r, y2-r, r, 0, 90*toRad, false);
263
+		ctx.lineTo(x1+r,y2);
264
+		ctx.arc(x1+r, y2-r, r, 90*toRad, 180*toRad, false);
265
+		ctx.lineTo(x1,y1+r);
266
+		ctx.arc(x1+r, y1+r, r, 180*toRad, 270*toRad, false);
267
+		ctx.fill();
268
+		ctx.stroke();
269
+		ctx.closePath();
270
+	} else {
271
+		ctx.fillRect(params.x, params.y, params.width, params.height);
272
+		ctx.strokeRect(params.x, params.y, params.width, params.height);
273
+	}
274
+	return this;
275
+};
276
+
277
+// Draw arc
278
+$.fn.drawArc = function(args) {
279
+	var ctx = this.loadCanvas(),
280
+		params = $.extend({}, jC.defaults, args),
281
+		toRad = checkUnits(params);
282
+		setGlobals(ctx, params);
283
+	
284
+	// Draw from center if chosen
285
+	if (params.fromCenter === false) {
286
+		params.x += params.radius;
287
+		params.y += params.radius;
288
+	}
289
+	
290
+	ctx.beginPath();
291
+	ctx.arc(params.x, params.y, params.radius, (params.start*toRad)-(Math.PI/2), (params.end*toRad)-(Math.PI/2), params.ccw);
292
+	// Close path if chosen
293
+	closePath(ctx, params);
294
+	return this;
295
+};
296
+
297
+// Draw ellipse
298
+$.fn.drawEllipse = function(args) {
299
+	var ctx = this.loadCanvas(),
300
+		params = $.extend({}, jC.defaults, args),
301
+		controlW = params.width * (4/3);
302
+		setGlobals(ctx, params);
303
+	
304
+	// Draw from center if chosen
305
+	if (params.fromCenter === false) {
306
+		params.x += params.width / 2;
307
+		params.y += params.height / 2;
308
+	}
309
+	
310
+	// Increment coordinates to prevent negative values
311
+	params.x += 1e-10;
312
+	params.y += 1e-10;
313
+	
314
+	// Create ellipse
315
+	ctx.beginPath();
316
+	ctx.moveTo(params.x, params.y-params.height/2);
317
+	ctx.bezierCurveTo(params.x-controlW/2,params.y-params.height/2,
318
+		params.x-controlW/2,params.y+params.height/2,
319
+		params.x,params.y+params.height/2);
320
+	ctx.bezierCurveTo(params.x+controlW/2,params.y+params.height/2,
321
+		params.x+controlW/2,params.y-params.height/2,
322
+		params.x,params.y-params.height/2);
323
+	ctx.closePath();
324
+	ctx.fill();
325
+	ctx.stroke();
326
+	return this;
327
+};
328
+
329
+// Draw line
330
+$.fn.drawLine = function(args) {
331
+	var ctx = this.loadCanvas(),
332
+		params = $.extend({}, jC.defaults, args),
333
+		max = Number.MAX_VALUE, l,
334
+		lx, ly;
335
+	setGlobals(ctx, params);
336
+	
337
+	// Draw each point
338
+	ctx.beginPath();
339
+	ctx.moveTo(params.x1, params.y1);
340
+	for (l=2; l<max; l+=1) {
341
+		lx = params['x' + l];
342
+		ly = params['y' + l];
343
+		// Stop loop when all points are drawn
344
+		if (typeof lx === 'undefined' || typeof ly === 'undefined') {
345
+			break;
346
+		}
347
+		ctx.lineTo(lx, ly);
348
+	}
349
+	// Close path if chosen
350
+	closePath(ctx, params);
351
+	return this;
352
+};
353
+
354
+// Draw quadratic curve
355
+$.fn.drawQuad = function(args) {
356
+	var ctx = this.loadCanvas(),
357
+		params = $.extend({}, jC.defaults, args),
358
+		max = Number.MAX_VALUE, l,
359
+		lx, ly, lcx, lcy;
360
+	setGlobals(ctx, params);
361
+	
362
+	// Draw each point
363
+	ctx.beginPath();
364
+	ctx.moveTo(params.x1, params.y1);
365
+	for (l=2; l<max; l+=1) {
366
+		lx = params['x' + l];
367
+    if (typeof lx === 'undefined') break;
368
+		ly = params['y' + l];
369
+    if (typeof ly === 'undefined') break;
370
+		lcx = params['cx' + (l-1)];
371
+    if (typeof lcx === 'undefined') break;
372
+		lcy = params['cy' + (l-1)];
373
+    if (typeof lcy === 'undefined') break;
374
+		ctx.quadraticCurveTo(lcx, lcy, lx, ly);
375
+	}
376
+	// Close path if chosen
377
+	closePath(ctx, params);
378
+	return this;
379
+};
380
+
381
+// Draw Bezier curve
382
+$.fn.drawBezier = function(args) {
383
+	var ctx = this.loadCanvas(),
384
+		params = $.extend({}, jC.defaults, args),
385
+		max = Number.MAX_VALUE,
386
+		l=2, lc=1, lx, ly, lcx1, lcy1, lcx2, lcy2, i;
387
+	setGlobals(ctx, params);
388
+
389
+	// Draw each point
390
+	ctx.beginPath();
391
+	ctx.moveTo(params.x1, params.y1);
392
+	for (i=2; i<max; i+=1) {
393
+		lx = params['x' + l];
394
+    if (typeof lx === 'undefined') break;
395
+		ly = params['y' + l];
396
+    if (typeof ly === 'undefined') break;
397
+		lcx1 = params['cx' + lc];
398
+    if (typeof lcx1 === 'undefined') break;
399
+		lcy1 = params['cy' + lc];
400
+    if (typeof lcy1 === 'undefined') break;
401
+		lcx2 = params['cx' + (lc+1)];
402
+    if (typeof lcx2 === 'undefined') break;
403
+		lcy2 = params['cy' + (lc+1)];
404
+    if (typeof lcy2 === 'undefined') break;
405
+		ctx.bezierCurveTo(lcx1, lcy1, lcx2, lcy2, lx, ly);
406
+		l += 1;
407
+		lc += 2;
408
+	}
409
+	// Close path if chosen
410
+	closePath(ctx, params);
411
+	return this;
412
+};
413
+
414
+// Draw text
415
+$.fn.drawText = function(args) {
416
+	var ctx = this.loadCanvas(),
417
+		params = $.extend({}, jC.defaults, args);
418
+	setGlobals(ctx, params);
419
+	
420
+	// Set text-specific properties
421
+	ctx.textBaseline = params.baseline;
422
+	ctx.textAlign = params.align;
423
+	ctx.font = params.font;
424
+	
425
+	ctx.strokeText(params.text, params.x, params.y);
426
+	ctx.fillText(params.text, params.x, params.y);
427
+	return this;
428
+};
429
+
430
+// Draw image
431
+$.fn.drawImage = function(args) {
432
+	var ctx = this.loadCanvas(),
433
+		params = $.extend({}, jC.defaults, args),
434
+		// Define image source
435
+		img = document.createElement('img');
436
+  	img.src = params.source;
437
+	  setGlobals(ctx, params);
438
+
439
+	// Draw image function
440
+	function draw() {
441
+		if (img.complete) {
442
+
443
+  		var scaleFac = img.width / img.height;
444
+
445
+			// If width/height are specified
446
+			if (typeof args.width !== 'undefined' && typeof args.height !== 'undefined') {
447
+				img.width = args.width;
448
+				img.height = args.height;
449
+			// If width is specified
450
+			} else if (typeof args.width !== 'undefined' && typeof args.height === 'undefined') {
451
+				img.width = args.width;
452
+				img.height = img.width / scaleFac;
453
+			// If height is specified
454
+			} else if (typeof args.width === 'undefined' && typeof args.height !== 'undefined') {
455
+				img.height = args.height;
456
+				img.width = img.height * scaleFac;
457
+			}
458
+		
459
+			// Draw from center if chosen
460
+			if (params.fromCenter === true) {
461
+				params.x -= img.width / 2;
462
+				params.y -= img.height / 2;
463
+			}
464
+
465
+			// Draw image
466
+			ctx.drawImage(img, params.x, params.y, img.width, img.height);
467
+		} else {
468
+			throw "The image has not loaded yet.";
469
+		}
470
+	}
471
+
472
+  function dodraw() {
473
+    // console.log("dodraw...");
474
+    try {
475
+  	  // console.log("dodraw...try...");
476
+      draw();
477
+    }
478
+    catch(error) {
479
+      // console.log("dodraw...catch: " + error);
480
+    }
481
+  }
482
+
483
+	// Draw image if already loaded
484
+  // console.log("--------------------");
485
+  // console.log("drawImage " + img.src);
486
+	try {
487
+    // console.log("try...");
488
+		draw();
489
+	} catch(error) {
490
+    // console.log("catch: " + error);
491
+		img.onload = dodraw;
492
+	}
493
+	return this;
494
+};
495
+
496
+// Draw polygon
497
+$.fn.drawPolygon = function(args) {
498
+	var ctx = this.loadCanvas(),
499
+		params = $.extend({}, jC.defaults, args),
500
+		theta, dtheta, x, y,
501
+		toRad = checkUnits(params), i;
502
+	setGlobals(ctx, params);
503
+	
504
+	if (params.sides >= 3) {		
505
+		// Calculate points and draw
506
+		theta = (Math.PI/2) + (Math.PI/params.sides) + (params.angle*toRad);
507
+		dtheta = (Math.PI*2) / params.sides;
508
+		for (i=0; i<params.sides; i+=1) {
509
+			x = params.x + (params.radius * Math.cos(theta)) + 1e-10;
510
+			y = params.y + (params.radius * Math.sin(theta)) + 1e-10;
511
+			if (params.fromCenter === false) {
512
+				x += params.radius;
513
+				y += params.radius;
514
+			}
515
+			ctx.lineTo(x, y);
516
+			theta += dtheta;
517
+		}
518
+		closePath(ctx, params);
519
+	}
520
+	return this;
521
+};
522
+
523
+return window.jCanvas = jC;
524
+}(jQuery, document, Math, Number));

+ 220
- 0
Marlin/configurator/js/jstepper.js View File

@@ -0,0 +1,220 @@
1
+/*!
2
+ * jQuery "stepper" Plugin
3
+ * version 0.0.1
4
+ * @requires jQuery v1.3.2 or later
5
+ * @requires jCanvas
6
+ *
7
+ * Authored 2011-06-11 Scott Lahteine (thinkyhead.com)
8
+ *
9
+ *  A very simple numerical stepper.
10
+ *  TODO: place arrows based on div size, make 50/50 width
11
+ *
12
+ *  Usage example:
13
+ *
14
+ *  $('#mydiv').jstepper({
15
+ *    min: 1,
16
+ *    max: 4,
17
+ *    val: 1,
18
+ *    arrowWidth: 15,
19
+ *    arrowHeight: '22px',
20
+ *    color: '#FFF',
21
+ *    acolor: '#F70',
22
+ *    hcolor: '#FF0',
23
+ *    id: 'select-me',
24
+ *    stepperClass: 'inner',
25
+ *    textStyle: {width:'1.5em',fontSize:'20px',textAlign:'center'},
26
+ *    onChange: function(v) { },
27
+ *  });
28
+ *
29
+ */
30
+;(function($) {
31
+  var un = 'undefined';
32
+
33
+  $.jstepperArrows = [
34
+    { name:'prev', poly:[[1.0,0],[0,0.5],[1.0,1.0]] },
35
+    { name:'next', poly:[[0,0],[1.0,0.5],[0,1.0]] }
36
+  ];
37
+
38
+ 	$.fn.jstepper = function(args) {
39
+
40
+		return this.each(function() {
41
+
42
+      var defaults = {
43
+        min: 1,
44
+        max: null,
45
+        val: null,
46
+        active: true,
47
+        placeholder: null,
48
+        arrowWidth: 0,
49
+        arrowHeight: 0,
50
+        color: '#FFF',
51
+        hcolor: '#FF0',
52
+        acolor: '#F80',
53
+        id: '',
54
+        stepperClass: '',
55
+        textStyle: '',
56
+        onChange: (function(v){ if (typeof console.log !== 'undefined') console.log("val="+v); })
57
+      };
58
+
59
+      args = $.extend(defaults, args || {});
60
+
61
+		  var min = args.min * 1,
62
+          max = (args.max !== null) ? args.max * 1 : min,
63
+          span = max - min + 1,
64
+          val = (args.val !== null) ? args.val * 1 : min,
65
+          active = !args.disabled,
66
+          placeholder = args.placeholder,
67
+          arrowWidth = 1 * args.arrowWidth.toString().replace(/px/,''),
68
+          arrowHeight = 1 * args.arrowHeight.toString().replace(/px/,''),
69
+          color = args.color,
70
+          hcolor = args.hcolor,
71
+          acolor = args.acolor,
72
+			    $prev = $('<a href="#prev" style="cursor:w-resize;"><canvas/></a>'),
73
+			    $marq = $('<div class="number"/>').css({float:'left',textAlign:'center'}),
74
+			    $next = $('<a href="#next" style="cursor:e-resize;"><canvas/></a>'),
75
+			    arrow = [ $prev.find('canvas')[0], $next.find('canvas')[0] ],
76
+			    $stepper = $('<span class="jstepper"/>').append($prev).append($marq).append($next).append('<div style="clear:both;"/>'),
77
+			    onChange = args.onChange;
78
+
79
+      if (args.id) $stepper[0].id = args.id;
80
+      if (args.stepperClass) $stepper.addClass(args.stepperClass);
81
+      if (args.textStyle) $marq.css(args.textStyle);
82
+
83
+      // replace a span, but embed elsewhere
84
+      if (this.tagName == 'SPAN') {
85
+        var previd = this.id;
86
+        $(this).replaceWith($stepper);
87
+        if (previd) $stepper.attr('id',previd);
88
+      }
89
+      else {
90
+        $(this).append($stepper);
91
+      }
92
+
93
+      // hook to call functions on this object
94
+      $stepper[0].ui = {
95
+
96
+        refresh: function() {
97
+          this.updateNumber();
98
+          this._drawArrow(0, 1);
99
+          this._drawArrow(1, 1);
100
+          return this;
101
+        },
102
+
103
+        _drawArrow: function(i,state) {
104
+          var $elm = $(arrow[i]),
105
+              desc = $.jstepperArrows[i],
106
+              fillStyle = (state == 2) ? hcolor : (state == 3) ? acolor : color,
107
+              draw = { fillStyle: fillStyle },
108
+              w = $elm.width(), h = $elm.height();
109
+
110
+          if (w <= 0) w = $elm.attr('width');
111
+          if (h <= 0) h = $elm.attr('height');
112
+
113
+          $.each(desc.poly,function(i,v){
114
+            ++i;
115
+            draw['x'+i] = v[0] * w;
116
+            draw['y'+i] = v[1] * h;
117
+          });
118
+          $elm.restoreCanvas().clearCanvas().drawLine(draw);
119
+        },
120
+
121
+        updateNumber: function() {
122
+          $marq.html((active || placeholder === null) ? val.toString() : placeholder);
123
+          return this;
124
+        },
125
+
126
+        _doclick: function(i) {
127
+          this.add(i ? 1 : -1);
128
+          this._drawArrow(i, 3);
129
+          var self = this;
130
+          setTimeout(function(){ self._drawArrow(i, 2); }, 50);
131
+        },
132
+
133
+        add: function(x) {
134
+          val = (((val - min) + x + span) % span) + min;
135
+          this.updateNumber();
136
+          this.didChange(val);
137
+          return this;
138
+        },
139
+
140
+        min: function(v) {
141
+          if (typeof v === un) return min;
142
+          this.setRange(v,max);
143
+          return this;
144
+        },
145
+
146
+        max: function(v) {
147
+          if (typeof v === un) return max;
148
+          this.setRange(min,v);
149
+          return this;
150
+        },
151
+
152
+        val: function(v) {
153
+          if (typeof v === un) return val;
154
+          val = (((v - min) + span) % span) + min;
155
+          this.updateNumber();
156
+          return this;
157
+        },
158
+
159
+        setRange: function(lo, hi, ini) {
160
+          if (lo > hi) hi = (lo += hi -= lo) - hi;
161
+          min = lo; max = hi; span = hi - lo + 1;
162
+          if (typeof ini !== un) val = ini;
163
+          if (val < min) val = min;
164
+          if (val > max) val = max;
165
+          this.updateNumber();
166
+          return this;
167
+        },
168
+
169
+        active: function(a) {
170
+          if (typeof a === un) return active;
171
+          (active = a) ? $marq.removeClass('inactive') : $marq.addClass('inactive');
172
+          this.updateNumber();
173
+          return this;
174
+        },
175
+
176
+        disable: function() { this.active(false); return this; },
177
+        enable: function() { this.active(true); return this; },
178
+
179
+        clearPlaceholder: function() {
180
+          this.setPlaceholder(null);
181
+          return this;
182
+        },
183
+        setPlaceholder: function(p) {
184
+          placeholder = p;
185
+          if (!active) this.updateNumber();
186
+          return this;
187
+        },
188
+
189
+        didChange: onChange
190
+
191
+      };
192
+
193
+      // set hover and click for each arrow
194
+      $.each($.jstepperArrows, function(i,desc) {
195
+        var $elm = $(arrow[i]),
196
+            w = arrowWidth ? arrowWidth : $elm.width() ? $elm.width() : 15,
197
+            h = arrowHeight ? arrowHeight : $elm.height() ? $elm.height() : 24;
198
+        $elm[0]._index = i;
199
+        $elm
200
+          .css({float:'left'})
201
+          .attr({width:w,height:h,'class':desc.name})
202
+          .hover(
203
+            function(e) { $stepper[0].ui._drawArrow(e.target._index, 2); },
204
+            function(e) { $stepper[0].ui._drawArrow(e.target._index, 1); }
205
+          )
206
+          .click(function(e){
207
+            $stepper[0].ui._doclick(e.target._index);
208
+            return false;
209
+          });
210
+      });
211
+
212
+      // init the visuals first time
213
+  		$stepper[0].ui.refresh();
214
+
215
+		}); // this.each
216
+
217
+  }; // $.fn.jstepper
218
+
219
+})( jQuery );
220
+

Loading…
Cancel
Save