1 /** 2 * This object is responsable for creating and updating the properties for figures 3 * 4 * @constructor 5 * @this {Builder} 6 * 7 * Builder allows for an {Array} of {BuilderProperty}'s to be displayed in a property panel, 8 * edited values update the owner 9 * @author Zack Newsham <zack_newsham@yahoo.co.uk> 10 **/ 11 function Builder(){ 12 13 } 14 15 16 /**Creates a {Builder} out of JSON parsed object 17 *@param {JSONObject} o - the JSON parsed object 18 *@return {Builder} a newly constructed Builder 19 *@author Alex Gheorghiu <alex@scriptoid.com> 20 **/ 21 Builder.load = function(o){ 22 var newBuilder = new Builder(); 23 newBuilder.properties = BuilderProperty.loadArray(o.properties); 24 newBuilder.figureId = o.figureId; 25 return newBuilder; 26 } 27 28 /** 29 *Creates the property panel for a figure 30 *@param {DOMObject} DOMObject - the div of the properties panel 31 *@param {Figure} figure - the figure for which the properties will be displayed 32 **/ 33 Builder.contructPropertiesPanel = function(DOMObject, figure){ 34 for(var i=0; i<figure.properties.length; i++){ 35 figure.properties[i].injectInputArea(DOMObject,figure.id); 36 } 37 } 38 39 /** 40 *Creates the properties for main CanvasProps 41 *@param {DOMObject} DOMObject - the div of the properties panel 42 *@param {CanvasProps} canvasProps - the CanvasProps for which the properties will be displayed 43 **/ 44 Builder.constructCanvasPropertiesPanel = function(DOMObject, canvasProps){ 45 var div = document.createElement("div"); 46 //div.innerHTML = '<div></div>';why is this here? 47 48 //width 49 var divWidth = document.createElement("div"); 50 divWidth.innerHTML = '<div class = "label">Width</div>'; 51 52 var inputWidth = document.createElement("input"); 53 inputWidth.type = "text"; 54 inputWidth.className = "text"; //required for onkeydown 55 //inputWidth.style.cssText = "float: right"; 56 inputWidth.value = canvasProps.getWidth(); 57 divWidth.appendChild(inputWidth); 58 59 div.appendChild(divWidth); 60 61 //height 62 var divHeight = document.createElement("div"); 63 divHeight.innerHTML = '<div class = "label">Height</div>'; 64 65 var inputHeight = document.createElement("input"); 66 inputHeight.type = "text"; 67 inputHeight.className = "text"; //required for onkeydown 68 //inputHeight.style.cssText = "float: right"; 69 inputHeight.value = canvasProps.getHeight(); 70 divHeight.appendChild(inputHeight); 71 72 div.appendChild(divHeight); 73 74 //update button 75 var divButton = document.createElement("div"); 76 divButton.innerHTML = '<div class = "label"></div>'; 77 78 var btnUpdate = document.createElement("input"); 79 btnUpdate.setAttribute("type", "button"); 80 btnUpdate.setAttribute("value", "Update"); 81 82 btnUpdate.onclick = function(){ 83 //update canvas props 84 85 Log.group("builder.js->constructCanvasPropertiesPanel()->Canvas update"); 86 //Log.info('Nr of actions in Undo system: ' + History.ACTIONS.length); 87 //Did the width change? 88 if(doUndo && canvasProps.getWidth() != inputWidth.value){ 89 var undo = new CanvasResizeCommand(canvasProps, History.OBJECT_STATIC, "Width", canvasProps.getWidth(), inputWidth.value); 90 canvasProps.width = inputWidth.value; 91 History.addUndo(undo); 92 } 93 94 //Did the height change? 95 if(doUndo && canvasProps.getHeight() != inputHeight.value){ 96 var undo = new CanvasResizeCommand(canvasProps, History.OBJECT_STATIC, "Height", canvasProps.getHeight(), inputHeight.value); 97 canvasProps.height = inputHeight.value; 98 History.addUndo(undo); 99 } 100 //Log.info('Nr of actions in Undo system: ' + History.ACTIONS.length); 101 Log.groupEnd(); 102 103 canvasProps.setWidth(inputWidth.value); 104 canvasProps.setHeight(inputHeight.value); 105 //alert(canvasProps); 106 107 draw(); 108 }; 109 110 divButton.appendChild(btnUpdate); 111 div.appendChild(divButton); 112 113 114 115 DOMObject.appendChild(div); 116 } 117 118 119 120 /** The stucture that will hold any changable property of a shape 121 * 122 * @constructor 123 * @this {Builder} 124 * @param {String} name - the name of the property 125 * @param {String} property - the property (in dot notation) we are using in the form of 'primitives.0.style.strokeStyle' 126 * @param {Object} type - could be either, 'Color', 'Boolean', 'Text' or {Array} 127 * In case it's an {Array} it is of type [{Text,Value}] and is used to generate a DD menu 128 */ 129 function BuilderProperty(name, property, type){ 130 this.name = name; 131 this.property = property; 132 this.type = type; 133 // Log.info('BuilderProperty(): ' + 'Propery type: ' + this.type + ' name: ' + this.name + ' property: ' + this.property); 134 } 135 136 /**Color property type*/ 137 BuilderProperty.TYPE_COLOR = 'Color'; 138 139 /**Text property type*/ 140 BuilderProperty.TYPE_TEXT = 'Text'; 141 142 /**SingleText property type*/ 143 BuilderProperty.TYPE_SINGLE_TEXT = 'SingleText'; 144 145 /**Text size property type*/ 146 BuilderProperty.TYPE_TEXT_FONT_SIZE = 'TextFontSize'; 147 148 /**Font family property type*/ 149 BuilderProperty.TYPE_TEXT_FONT_FAMILY = 'TextFontFamily'; 150 151 /**Text aligment property type*/ 152 BuilderProperty.TYPE_TEXT_FONT_ALIGNMENT = 'TextFontAlignment'; 153 154 /**Boolean property type*/ 155 BuilderProperty.TYPE_BOOLEAN = 'Boolean'; 156 157 /**Line width property type*/ 158 BuilderProperty.TYPE_LINE_WIDTH = 'LineWidth'; 159 160 /**Image Fill type*/ 161 BuilderProperty.TYPE_IMAGE_FILL = 'ImageFill'; 162 163 /**File Upload type*/ 164 BuilderProperty.TYPE_IMAGE_UPLOAD = "ImageUpload"; 165 166 /**Connector's end property type*/ 167 BuilderProperty.TYPE_CONNECTOR_END= 'ConnectorEnd'; 168 169 BuilderProperty.IMAGE_FILL = [{Text: 'No Scaling', Value: CanvasImage.FIXED_NONE},{Text: 'Fit to Area', Value: CanvasImage.FIXED_BOTH},{Text: 'Fit to Width',Value: CanvasImage.FIXED_WIDTH},{Text: 'Fit to Height',Value: CanvasImage.FIXED_HEIGHT},{Text: ' Auto Fit',Value: CanvasImage.FIXED_AUTO}] 170 /**Line widths*/ 171 BuilderProperty.LINE_WIDTHS = [ 172 {Text: '1px', Value: '1'},{Text: '2px',Value: '2'},{Text: '3px',Value: '3'}, 173 {Text: '4px',Value: '4'},{Text: '5px',Value: '5'},{Text: '6px',Value: '6'}, 174 {Text: '7px',Value: '7'},{Text: '8px',Value: '8'},{Text: '9px',Value: '9'}, 175 {Text: '10px',Value: '10'}]; 176 177 /**Font sizes*/ 178 BuilderProperty.FONT_SIZES = []; 179 for(var i=0; i<73; i++){ 180 BuilderProperty.FONT_SIZES.push({Text:i+'px', Value:i}); 181 } 182 183 /**Connector ends*/ 184 BuilderProperty.CONNECTOR_ENDS = [{Text:'Normal', Value:'Normal'},{Text:'Arrow', Value:'Arrow'}, 185 {Text:'Empty Triangle', Value:'Empty'},{Text:'Filled Triangle', Value:'Filled'}]; 186 187 /**Display separator*/ 188 BuilderProperty.SEPARATOR = 'SEPARATOR'; 189 190 /**Creates a {BuilderProperty} out of JSON parsed object 191 *@param {JSONObject} o - the JSON parsed object 192 *@return {BuilderProperty} a newly constructed Point 193 *@author Alex Gheorghiu <alex@scriptoid.com> 194 **/ 195 BuilderProperty.load = function(o){ 196 var prop = new BuilderProperty(); 197 prop.name = o.name; 198 prop.property = o.property; 199 prop.type = o.type; 200 return prop; 201 } 202 203 204 /**Creates an array of BuilderProperties from an array of {JSONObject}s 205 *@param {Array} v - the array of JSONObjects 206 *@return an {Array} of {BuilderProperty}-ies 207 *@author Alex Gheorghiu <alex@scriptoid.com> 208 **/ 209 BuilderProperty.loadArray = function(v){ 210 var newProps = []; 211 for(var i=0; i< v.length; i++){ 212 newProps.push(BuilderProperty.load(v[i])); 213 } 214 return newProps; 215 } 216 217 BuilderProperty.prototype = { 218 219 toString:function(){ 220 return 'Propery type: ' + this.type + ' name: ' + this.name + ' property: ' + this.property; 221 }, 222 223 equals : function(anotherBuilderProperty){ 224 return this.type == anotherBuilderProperty.type 225 && this.name == anotherBuilderProperty.name 226 && this.property == anotherBuilderProperty.property; 227 }, 228 229 /** 230 *Generates a HTML fragment to allow to edit its property. 231 *For example if current property is a color then this method will 232 *inject a color picker in the specified DOMObject 233 * 234 *@param {HTMLElement} DOMObject - the div of the properties panel 235 *@param {Number} figureId - the id of the figure we are using 236 */ 237 injectInputArea:function(DOMObject,figureId){ 238 if(this.name == BuilderProperty.SEPARATOR){ 239 DOMObject.appendChild(document.createElement("hr")); 240 return; 241 } 242 else if(this.type == BuilderProperty.TYPE_COLOR){ 243 this.generateColorCode(DOMObject,figureId); 244 } 245 else if(this.type == BuilderProperty.TYPE_TEXT){ 246 this.generateTextCode(DOMObject,figureId); 247 } 248 else if(this.type == BuilderProperty.TYPE_SINGLE_TEXT){ 249 this.generateSingleTextCode(DOMObject,figureId); 250 } 251 else if(this.type == BuilderProperty.TYPE_TEXT_FONT_SIZE){ 252 this.generateArrayCode(DOMObject,figureId, BuilderProperty.FONT_SIZES); 253 // this.generateFontSizesCode(DOMObject,figureId); 254 } 255 else if(this.type == BuilderProperty.TYPE_TEXT_FONT_FAMILY){ 256 this.generateArrayCode(DOMObject,figureId, Text.FONTS); 257 } 258 else if(this.type == BuilderProperty.TYPE_TEXT_FONT_ALIGNMENT){ 259 this.generateArrayCode(DOMObject,figureId, Text.ALIGNMENTS); 260 } 261 else if(this.type == BuilderProperty.TYPE_CONNECTOR_END){ 262 this.generateArrayCode(DOMObject,figureId, BuilderProperty.CONNECTOR_ENDS); 263 } 264 else if(this.type == BuilderProperty.TYPE_LINE_WIDTH){ 265 this.generateArrayCode(DOMObject,figureId, BuilderProperty.LINE_WIDTHS); 266 } 267 else if(this.type == BuilderProperty.TYPE_IMAGE_FILL){ 268 this.generateArrayCode(DOMObject,figureId, BuilderProperty.IMAGE_FILL); 269 } 270 else if(this.type == BuilderProperty.TYPE_IMAGE_UPLOAD){ 271 this.generateFileCode(DOMObject, figureId) 272 } 273 }, 274 275 276 /** 277 *Creates an Ajax file upload element 278 *@param {HTMLElement} DOMObject - the div of the properties panel 279 *@param {Number} figureId - the id of the figure we are using 280 */ 281 generateFileCode:function(DOMObject, figureId){ 282 var d = new Date(); 283 var uniqueId = d.getTime(); 284 var value = this.getValue(figureId); 285 var div = document.createElement("div"); 286 div.innerHTML = '<div class=label style="width: 100%"><div style="float: left;">' + this.name + '</div><div style="float: right; clear: right;" align="right"></div></div>'; 287 288 var upload = document.createElement("input"); 289 upload.type = "submit"; 290 upload.value = "Upload"; 291 upload.style.cssText="float: right; clear: both;"; 292 upload.onclick = function(figureId,property){ 293 return function(){ 294 updateFigure(figureId, property, "") 295 } 296 }(figureId, this.property); 297 298 var file = document.createElement("input" ); 299 file.type = "file"; 300 file.id = "fileToUpload"; 301 file.name = "fileToUpload"; 302 file.style.cssText="width: 150px; float: right;"; 303 div.children[0].children[1].appendChild(file); 304 div.children[0].appendChild(upload); 305 DOMObject.appendChild(div); 306 }, 307 308 /** 309 *Creates a boolean editor; usually a chechbox 310 * 311 *@param {HTMLElement} DOMObject - the div of the properties panel 312 *@param {Number} figureId - the id of the figure we are using 313 **/ 314 generateBooleanCode:function(DOMObject,figureId){ 315 var d = new Date(); 316 var uniqueId = d.getTime(); 317 var value = this.getValue(figureId); 318 var div = document.createElement("div"); 319 div.innerHTML = '<div class=label>' + this.name + '</div>'; 320 321 var check = document.createElement("input"); 322 check.type = "checkbox" 323 check.className = "text"; //required for onkeydown 324 check.style.cssText ="float: right"; 325 check.checked = value; 326 div.children[0].appendChild(check); 327 check.onclick = function(figureId,property){ 328 return function(){ 329 updateFigure(figureId, property, this.checked) 330 } 331 }(figureId, this.property); 332 333 DOMObject.appendChild(div); 334 }, 335 336 337 /**Generate the code to edit the text. 338 *The text got updated when you leave the input area 339 * 340 *@param {HTMLElement} DOMObject - the div of the properties panel 341 *@param {Number} figureId - the id of the figure we are using 342 **/ 343 generateTextCode:function(DOMObject,figureId){ 344 var uniqueId = new Date().getTime(); 345 var value = this.getValue(figureId); 346 347 var div = document.createElement("div"); 348 div.className = "textLine"; 349 div.innerHTML = '<div class = "label">' + this.name + '</div>'; 350 351 var text = document.createElement("textarea"); 352 text.className = "text"; //required for onkeydown 353 //text.style.cssText ="float: right"; 354 //text.style.cssText ="float: left"; 355 text.value = value; 356 text.style.width = "100%"; 357 //text.style.float = "left"; 358 div.appendChild(document.createElement("br")); 359 div.appendChild(text); 360 361 text.onchange = function(figureId,property){ 362 return function(){ 363 updateFigure(figureId, property, this.value) 364 } 365 }(figureId, this.property); 366 367 368 text.onmouseout = text.onchange; 369 text.onkeyup = text.onchange; 370 DOMObject.appendChild(div); 371 }, 372 373 374 /**Generate the code to edit the text. 375 *The text got updated when you leave the input area 376 * 377 *@param {HTMLElement} DOMObject - the div of the properties panel 378 *@param {Number} figureId - the id of the figure we are using 379 **/ 380 generateSingleTextCode:function(DOMObject,figureId){ 381 var uniqueId = new Date().getTime(); 382 var value = this.getValue(figureId); 383 384 var div = document.createElement("div"); 385 div.className = "line"; 386 div.innerHTML = '<div class = "label">' + this.name + '</div>'; 387 388 var text = document.createElement("input"); 389 text.type = "text"; 390 text.className = "text"; //required for onkeydown 391 text.style.cssText = "float: right"; 392 //text.style.cssText ="float: left"; 393 text.value = value; 394 //text.style.width = "100%"; 395 //text.style.float = "left"; 396 div.appendChild(text); 397 398 text.onchange = function(figureId,property){ 399 return function(){ 400 updateFigure(figureId, property, this.value) 401 } 402 }(figureId, this.property); 403 404 405 text.onmouseout = text.onchange; 406 text.onkeyup = text.onchange; 407 DOMObject.appendChild(div); 408 }, 409 410 411 /**Used to generate a drop down menu 412 * 413 *@param {HTMLElement} DOMObject - the div of the properties panel 414 *@param {Number} figureId - the id of the figure we are using 415 *@param {Array} v - a vector or hashes ex: [{Text:'Normal', Value:'Normal'},{Text:'Arrow', Value:'Arrow'}] 416 */ 417 generateArrayCode:function(DOMObject,figureId, v){ 418 // Log.info("Font size length: " + v.length); 419 var uniqueId = new Date().getTime(); 420 421 var value = this.getValue(figureId); 422 423 var div = document.createElement("div"); 424 div.className = "line"; 425 div.innerHTML = '<div class="label">'+this.name+'</div>'; 426 427 var select = document.createElement("select"); 428 select.style.cssText ="float: right;"; 429 div.appendChild(select); 430 431 for(var i=0; i< v.length; i++){ 432 var option = document.createElement("option"); 433 option.value = v[i].Value; 434 // Log.info("\t Text : " + v[i].Text + " Value : " + v[i].Value); 435 option.text = v[i].Text; //see: http://www.w3schools.com/jsref/coll_select_options.asp 436 select.options.add(option); //push does not exist in the options array 437 if(option.value == value){ 438 option.selected = true; 439 } 440 } 441 442 var selProperty = this.property; //save it in a separate variable as if refered by (this) it will refert to the 'select' DOM Object 443 select.onchange = function(){ 444 //alert('Font size triggered. Figure id : ' + figureId + ' property: ' + selProperty + ' new value' + this.options[this.selectedIndex].value); 445 updateFigure(figureId, selProperty, this.options[this.selectedIndex].value); 446 }; 447 448 DOMObject.appendChild(div); 449 }, 450 451 452 /** 453 *Used to generate a color picker 454 * 455 *@param{HTMLElement} DOMObject - the div of the properties panel 456 *@param{Number} figureId - the id of the figure we are using 457 */ 458 generateColorCode: function(DOMObject,figureId){ 459 var value = this.getValue(figureId); 460 461 var uniqueId = new Date().getTime(); 462 var div = document.createElement("div"); 463 div.className = "line"; 464 var retVal = '<div class="label">' + this.name + '</div>\n'; 465 retVal += '<div id="colorSelector' + uniqueId + '" style="/*border: 1px solid #000000;*/ width: 16px; height: 16px; display: block; float: right; padding-right: 3px;">\n'; 466 retVal += '<input type="text" value="' + value + '" id="colorpickerHolder' + uniqueId + '">\n'; 467 //retVal+='<div style="background-color: '+value+'; width: 20px; height: 20px;"></div>\n'; 468 retVal += '</div>\n'; 469 //very unhappy with this innerHTML thingy 470 div.innerHTML = retVal; 471 472 DOMObject.appendChild(div); 473 474 //let plugin do the job 475 $('#colorpickerHolder'+uniqueId).colorPicker(); 476 477 //on change update the figure 478 var propExposedToAnonymous = this.property; 479 $('#colorpickerHolder'+uniqueId).change(function() { 480 Log.info('generateColorCode(): figureId: ' + figureId + 'type: ' + this.type + ' name: ' + this.name + ' property: ' + this.property); 481 updateFigure(figureId, propExposedToAnonymous, $('#colorpickerHolder'+uniqueId).val()); 482 }); 483 }, 484 485 486 487 /**We use this to return a value based on the figure and the property string, 488 *similar to Javas Class.forname...sort of anyway 489 *We need this because passing direct references to simple data types (including strings) only passes the value, not a reference to that value 490 * 491 *@param{Number} figureId - the id of the shape {Figure} or {Connector} we are using, could also be the canvas (figureId = 'a') 492 */ 493 getValue:function(figureId){ 494 var obj = stack.figureGetById(figureId); 495 if(obj == null){ //ok so it's not a Figure...so it should be a Connector 496 obj = CONNECTOR_MANAGER.connectorGetById(figureId); 497 } 498 if(obj == null){ 499 if(figureId == "canvas"){ 500 obj = canvas; 501 } 502 } 503 var propertyAccessors = this.property.split("."); 504 // Log.info("BuilderProperty::getValue() : propertyAccessors : " + propertyAccessors ); 505 for(var i = 0; i<propertyAccessors.length-1; i++){ 506 // Log.info("\tBuilderProperty::getValue() : i = " + i + ' name= ' + propertyAccessors[i]); 507 obj = obj[propertyAccessors[i]]; 508 } 509 510 //null is allowed, undefined is not 511 if(obj[propertyAccessors[propertyAccessors.length -1]] === undefined){ 512 /**If there is a getxxx() accesory method in the object we will call that function*/ 513 var propName = "get"+propertyAccessors[propertyAccessors.length -1]; 514 if(propName in obj){ //@see https://developer.mozilla.org/en/JavaScript/Reference/Operators/Special_Operators/in_Operator 515 return obj["get"+propertyAccessors[propertyAccessors.length -1]](); 516 } 517 else{ 518 return null; // :p 519 } 520 } 521 else{ 522 //Access the object property's 523 return obj[propertyAccessors[propertyAccessors.length -1]]; 524 } 525 } 526 }