function Builder() { } /**Image base path*/ Builder.IMAGE_BASE_PATH = './assets/images/'; /**Path to fill icon image*/ Builder.IMAGE_FILL_ICON_PATH = Builder.IMAGE_BASE_PATH + 'prop-icon-fill.png'; /**Path to stroke icon image*/ Builder.IMAGE_STROKE_ICON_PATH = Builder.IMAGE_BASE_PATH + 'prop-icon-stroke.png'; /**Path to line width icon image*/ Builder.IMAGE_LINEWIDTH_ICON_PATH = Builder.IMAGE_BASE_PATH + 'prop-icon-linewidth.png'; /**Line (dashed) style icon image*/ Builder.IMAGE_LINESTYLE_ICON_PATH = Builder.IMAGE_BASE_PATH + 'prop-icon-linestyle.png'; /**Path to start style icon image*/ Builder.IMAGE_STARTSTYLE_ICON_PATH = Builder.IMAGE_BASE_PATH + 'prop-icon-startstyle.png'; /**Path to end style icon image*/ Builder.IMAGE_ENDSTYLE_ICON_PATH = Builder.IMAGE_BASE_PATH + 'prop-icon-endstyle.png'; /**Path to width icon image*/ Builder.IMAGE_WIDTH_ICON_PATH = Builder.IMAGE_BASE_PATH + 'prop-icon-h-resize.png'; /**Path to height icon image*/ Builder.IMAGE_HEIGHT_ICON_PATH = Builder.IMAGE_BASE_PATH + 'prop-icon-v-resize.png'; /**Path to URL style icon image*/ Builder.IMAGE_URL_ICON_PATH = Builder.IMAGE_BASE_PATH + 'prop-icon-url.png'; /**Path to text style icon image*/ Builder.IMAGE_TEXT_ICON_PATH = Builder.IMAGE_BASE_PATH + 'prop-icon-text.png'; /**Creates a {Builder} out of JSON parsed object *@param {JSONObject} o - the JSON parsed object *@return {Builder} a newly constructed Builder *@author Alex Gheorghiu **/ //Builder.load = function(o){ // var newBuilder = new Builder(); // newBuilder.properties = BuilderProperty.loadArray(o.properties); // newBuilder.figureId = o.figureId; // return newBuilder; //} /** *Creates the property panel for a shape {Figure} or {Connector} *@param {DOMObject} DOMObject - the div of the properties panel *@param {Figure} shape - the figure for which the properties will be displayed **/ Builder.constructPropertiesPanel = function (DOMObject, shape) { for (var i = 0; i < shape.properties.length; i++) { // regExp to avoid properties of Text editor //if (/(primitives\.\d+|middleText)\.(str|size|font|align|underlined|style\.fillStyle)/g.test(shape.properties[i].property) === false) { //} shape.properties[i].injectInputArea(DOMObject, shape.id); } }; /** *Creates the property panel for a Text primitive of shape {Figure} and returns it *@param {DOMObject} textEditor - the
of the properties panel (declared inside editor page) *@param {DOMObject} textEditorTools - the
of the text editor's tools (declared insider editor page) *@param {Figure} shape - the figure - parent of Text primitive *@param {Number} textPrimitiveId - the id value of Text primitive child of figure for which the properties will be displayed * *@return {TextEditorPopup} - new instance of TextEditorPopup after init * @author Artyom Pokatilov **/ Builder.constructTextPropertiesPanel = function (textEditor, textEditorTools, shape, textPrimitiveId) { var textEditor = new TextEditorPopup(textEditor, textEditorTools, shape, textPrimitiveId); textEditor.init(); return textEditor; }; /** *Creates the properties for main CanvasProps *@param {DOMObject} DOMObject - the div of the properties panel *@param {CanvasProps} canvasProps - the CanvasProps for which the properties will be displayed **/ Builder.constructCanvasPropertiesPanel = function (DOMObject, canvasProps) { var div = document.createElement("div"); var icon; // colorPicker plugin requires div to be already appended to the DOM DOMObject.appendChild(div); //title var titleDiv = document.createElement("div"); titleDiv.className = 'GroupLabel'; titleDiv.textContent = '画板设置'; div.appendChild(titleDiv); //separator var separator = document.createElement("hr"); div.appendChild(separator); //fill color var colorDiv = document.createElement("div"); colorDiv.className = "line"; var currentColor = canvasProps.getFillColor(); var uniqueId = new Date().getTime(); // var labelDiv = document.createElement("div"); // labelDiv.className = "label"; // labelDiv.textContent = "背景颜色"; // icon = new Image(); // icon.className = 'prop-icon'; // icon.src = Builder.IMAGE_FILL_ICON_PATH; // labelDiv.appendChild(icon); // colorDiv.appendChild(labelDiv); // var colorSelectorDiv = document.createElement("div"); // colorSelectorDiv.id = 'colorSelector' + uniqueId; // colorSelectorDiv.className = 'color-selector'; // var colorInput = document.createElement("input"); // colorInput.type = "text"; // colorInput.id = 'colorpickerHolder' + uniqueId; // colorInput.value = currentColor; // colorSelectorDiv.appendChild(colorInput); // colorDiv.appendChild(colorSelectorDiv); // div.appendChild(colorDiv); // var colorPicker = document.getElementById('colorpickerHolder' + uniqueId); // //let plugin do the job // $(colorPicker).colorPicker(); // //on change update the canvasProps // colorPicker.onchange = function () { // var newColor = colorPicker.value; // //Did we change fill color? // if (canvasProps.getFillColor() !== newColor) { // var cmd = new CanvasChangeColorCommand(newColor); // cmd.execute(); // History.addUndo(cmd); // //trigger a repaint; // draw(); // } // }; //width var divWidth = document.createElement("div"); divWidth.className = 'label'; divWidth.textContent = '宽度'; icon = new Image(); icon.className = 'prop-icon'; icon.src = Builder.IMAGE_WIDTH_ICON_PATH; divWidth.appendChild(icon); var inputWidth = document.createElement("input"); inputWidth.type = "text"; inputWidth.className = "text"; //required for onkeydown inputWidth.value = canvasProps.getWidth(); divWidth.appendChild(inputWidth); div.appendChild(divWidth); //height var divHeight = document.createElement("div"); divHeight.className = 'label'; divHeight.textContent = '高度'; icon = new Image(); icon.className = 'prop-icon'; icon.src = Builder.IMAGE_HEIGHT_ICON_PATH; divHeight.appendChild(icon); var inputHeight = document.createElement("input"); inputHeight.type = "text"; inputHeight.className = "text"; //required for onkeydown inputHeight.value = canvasProps.getHeight(); divHeight.appendChild(inputHeight); div.appendChild(divHeight); //button block: Update and Fit options var divButton = document.createElement("div"); //update button var btnUpdate = document.createElement("input"); btnUpdate.setAttribute("type", "button"); btnUpdate.setAttribute("value", "应用"); btnUpdate.onclick = function () { //update canvas props // Did we entered number value for width? var widthVal = Number(inputWidth.value); if (isNaN(widthVal)) { widthVal = canvasProps.getWidth(); inputWidth.value = widthVal; } var heightVal = Number(inputHeight.value); if (isNaN(heightVal)) { heightVal = canvasProps.getHeight(); inputHeight.value = heightVal; } //Did we change width or height? if (canvasProps.getWidth() !== widthVal || canvasProps.getHeight() !== heightVal) { var undo = new CanvasChangeSizeCommand(widthVal, heightVal); undo.execute(); History.addUndo(undo); } //执行保存到数据库MapData--CCForm_FK_MapData draw(); }; divButton.appendChild(btnUpdate); //fit button // var btnFit = document.createElement("input"); // btnFit.setAttribute("type", "button"); // btnFit.setAttribute("value", "Fit"); // btnFit.onclick = function () { // /* Algorithm // * 1) Find border points of Figure/Container/Connector bounds // * 2) Get new width and height of canvas from 1) // * 3) If canvas size changed -> Use CanvasChangeSizeCommand to change canvas size // */ // var workAreaBounds = STACK.getWorkAreaBounds(); // var newCanvasWidth = workAreaBounds[2] - workAreaBounds[0]; // var newCanvasHeight = workAreaBounds[3] - workAreaBounds[1]; // // Did canvas size changed? // if (newCanvasWidth !== canvasProps.getWidth() || newCanvasHeight !== canvasProps.getHeight()) { // var cmdCanvasFit = new CanvasFitCommand( // newCanvasWidth + DIAGRAMO.CANVAS_FIT_PADDING * 2, // newCanvasHeight + DIAGRAMO.CANVAS_FIT_PADDING * 2, // workAreaBounds[0] - DIAGRAMO.CANVAS_FIT_PADDING, // new (0,0) point goes this X coordinate // workAreaBounds[1] - DIAGRAMO.CANVAS_FIT_PADDING // new (0,0) point goes this Y coordinate // ); // cmdCanvasFit.execute(); // History.addUndo(cmdCanvasFit); // // redraw canvas // draw(); // } // }; // divButton.appendChild(btnFit); div.appendChild(divButton); } /** The structure that will declare any visible and changable property of a shape. * * Note: A {BuilderProperty} DOES NOT STORE THE VALUE OF THE PROPERTY but only * describe what properties of a {Style} are exposed and how the {Builder} will * create interface (fragments) for user in properties panel. * * @constructor * @this {Builder} * @param {String} name - the name of the property * @param {String} property - the property (in dot notation) we are using in the form of 'primitives.0.style.strokeStyle' * @param {Object} type - could be either, 'Color', 'Boolean', 'Text' or {Array} * In case it's an {Array} it is of type [{Text,Value}] and is used to generate a DD menu */ function BuilderProperty(name, property, type, value) { this.name = name; this.property = property; this.type = type; this.PropertyValue = value ? value : null; } /**Color property type*/ BuilderProperty.TYPE_COLOR = 'Color'; /**Text property type*/ BuilderProperty.TYPE_TEXT = 'Text'; /**SingleText property type*/ BuilderProperty.TYPE_SINGLE_TEXT = 'SingleText'; /**Text size property type*/ BuilderProperty.TYPE_TEXT_FONT_SIZE = 'TextFontSize'; /**Font family property type*/ BuilderProperty.TYPE_TEXT_FONT_FAMILY = 'TextFontFamily'; /**Text aligment property type*/ BuilderProperty.TYPE_TEXT_FONT_ALIGNMENT = 'TextFontAlignment'; /**Text underlined property type*/ BuilderProperty.TYPE_TEXT_UNDERLINED = 'TextUnderlined'; /**Boolean property type*/ BuilderProperty.TYPE_BOOLEAN = 'Boolean'; /**Line width property type*/ BuilderProperty.TYPE_LINE_WIDTH = 'LineWidth'; /**Line width property style*/ BuilderProperty.TYPE_LINE_STYLE = 'LineStyle'; /**Image Fill type*/ BuilderProperty.TYPE_IMAGE_FILL = 'ImageFill'; /**File Upload type*/ BuilderProperty.TYPE_IMAGE_UPLOAD = "ImageUpload"; /**Connector's end property type*/ BuilderProperty.TYPE_CONNECTOR_END = 'ConnectorEnd'; /** CCForm Propertys **/ BuilderProperty.TYPE_SINGLE_TEXT_ReadOnly = "SingleTextReadOnly"; BuilderProperty.TYPE_GROUP_LABEL = 'GroupLabel'; BuilderProperty.CCFormEnum = "Combobox"; BuilderProperty.CCFormUpload = "UploadFile"; BuilderProperty.TYPE_TEXT_FONTWEIGHT = 'FontWeight'; BuilderProperty.TYPE_TEXT_FONTWEIGHTS = [ { Text: 'normal', Value: 'normal' }, { Text: 'bold', Value: 'bold' }, { Text: 'bolder', Value: 'bolder' }, { Text: 'lighter', Value: 'lighter' } ]; BuilderProperty.CCFormLink = 'linkButton'; /**URL attached to a figure*/ BuilderProperty.TYPE_URL = 'URL'; //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}] /**Line widths*/ BuilderProperty.LINE_WIDTHS = [ { Text: '0.5px', Value: '0.5' }, { Text: '1px', Value: '1' }, { Text: '2px', Value: '2' }, { Text: '3px', Value: '3' }, { Text: '4px', Value: '4' }, { Text: '5px', Value: '5' }, { Text: '6px', Value: '6' }, { Text: '7px', Value: '7' }, { Text: '8px', Value: '8' }, { Text: '9px', Value: '9' }, { Text: '10px', Value: '10'}]; /**Line styles*/ BuilderProperty.LINE_STYLES = [ { Text: 'Continous', Value: 'continuous' }, { Text: 'Dotted', Value: 'dotted' }, { Text: 'Dashed', Value: 'dashed' } ]; /**Font sizes*/ BuilderProperty.FONT_SIZES = []; for (var i = 0; i < 73; i++) { BuilderProperty.FONT_SIZES.push({ Text: i + 'px', Value: i }); } /**Connector ends*/ BuilderProperty.CONNECTOR_ENDS = [{ Text: 'Normal', Value: 'Normal' }, { Text: 'Arrow', Value: 'Arrow' }, { Text: 'Empty Triangle', Value: 'Empty' }, { Text: 'Filled Triangle', Value: 'Filled'}]; /**Display separator*/ BuilderProperty.SEPARATOR = 'SEPARATOR'; /**Css class for button checking control*/ BuilderProperty.BUTTON_CHECKER_CLASS = 'button-checker'; /**Name of attribute to define is property checked or not*/ BuilderProperty.BUTTON_CHECKED_ATTRIBUTE = 'button-checked'; /**Label for text underlined property*/ BuilderProperty.TEXT_UNDERLINED_LABEL = 'U'; /**Creates a {BuilderProperty} out of JSON parsed object *@param {JSONObject} o - the JSON parsed object *@return {BuilderProperty} a newly constructed Point *@author Alex Gheorghiu **/ BuilderProperty.load = function (o) { var prop = new BuilderProperty(); prop.name = o.name; prop.property = o.property; prop.type = o.type; prop.PropertyValue = o.PropertyValue; return prop; } /**Creates an array of BuilderProperties from an array of {JSONObject}s *@param {Array} v - the array of JSONObjects *@return an {Array} of {BuilderProperty}-ies *@author Alex Gheorghiu **/ BuilderProperty.loadArray = function (v) { var newProps = []; for (var i = 0; i < v.length; i++) { newProps.push(BuilderProperty.load(v[i])); } return newProps; } BuilderProperty.prototype = { constructor: BuilderProperty, toString: function () { return 'Propery type: ' + this.type + ' name: ' + this.name + ' property: ' + this.property; }, equals: function (anotherBuilderProperty) { return this.type == anotherBuilderProperty.type && this.name == anotherBuilderProperty.name && this.property == anotherBuilderProperty.property; }, /** *Generates a HTML fragment to allow to edit its property. *For example if current property is a color then this method will *inject a color picker in the specified DOMObject * *@param {HTMLElement} DOMObject - the div of the properties panel *@param {Number} figureId - the id of the figure we are using */ injectInputArea: function (DOMObject, figureId) { if (this.name == BuilderProperty.SEPARATOR) { DOMObject.appendChild(document.createElement("hr")); return; } else if (this.type == BuilderProperty.TYPE_COLOR) { this.generateColorCode(DOMObject, figureId); } else if (this.type == BuilderProperty.TYPE_TEXT) { this.generateTextCode(DOMObject, figureId); } else if (this.type == BuilderProperty.TYPE_SINGLE_TEXT) { this.generateSingleTextCode(DOMObject, figureId); } else if (this.type == BuilderProperty.TYPE_SINGLE_TEXT_ReadOnly) { this.generateSingleTextReadOnlyCode(DOMObject, figureId); } else if (this.type == BuilderProperty.TYPE_TEXT_FONT_SIZE) { this.generateArrayCode(DOMObject, figureId, BuilderProperty.FONT_SIZES); } else if (this.type == BuilderProperty.TYPE_TEXT_FONT_FAMILY) { this.generateArrayCode(DOMObject, figureId, Text.FONTS); } else if (this.type == BuilderProperty.TYPE_TEXT_FONT_ALIGNMENT) { this.generateArrayCode(DOMObject, figureId, Text.ALIGNMENTS); } else if (this.type == BuilderProperty.TYPE_TEXT_UNDERLINED) { this.generateButtonCheckerCode(DOMObject, figureId, BuilderProperty.TEXT_UNDERLINED_LABEL); } else if (this.type == BuilderProperty.TYPE_TEXT_FONTWEIGHT) { this.generateArrayCode(DOMObject, figureId, BuilderProperty.TYPE_TEXT_FONTWEIGHTS); } else if (this.type == BuilderProperty.TYPE_CONNECTOR_END) { this.generateArrayCode(DOMObject, figureId, BuilderProperty.CONNECTOR_ENDS); } else if (this.type == BuilderProperty.TYPE_LINE_WIDTH) { this.generateArrayCode(DOMObject, figureId, BuilderProperty.LINE_WIDTHS); } else if (this.type == BuilderProperty.TYPE_LINE_STYLE) { this.generateArrayCode(DOMObject, figureId, BuilderProperty.LINE_STYLES); } else if (this.type == BuilderProperty.TYPE_URL) { this.generateURLCode(DOMObject, figureId); } else if (this.type == BuilderProperty.TYPE_GROUP_LABEL) { this.generateGroupLabelCode(DOMObject, figureId); return; } else if (this.type == BuilderProperty.CCFormEnum) { var items = CCForm_Control_Enum[this.property]; this.generateArrayCode(DOMObject, figureId, items); } else if (this.type == BuilderProperty.CCFormUpload) { this.generateUploadCode(DOMObject, figureId); } else if (this.type == BuilderProperty.CCFormLink) { this.generateCCFormLinkCode(DOMObject, figureId); } //ccbpm 控件下面增加虚线 this.generateControlUnderLine(DOMObject); }, /** *Creates a boolean editor; usually a chechbox * *@param {HTMLElement} DOMObject - the div of the properties panel *@param {Number} figureId - the id of the figure we are using **/ generateBooleanCode: function (DOMObject, figureId) { var d = new Date(); var uniqueId = d.getTime(); var value = this.getValue(figureId); var div = document.createElement("div"); var labelDiv = document.createElement("div"); labelDiv.className = "label"; labelDiv.textContent = this.name; div.appendChild(labelDiv); var check = document.createElement("input"); check.type = "checkbox" check.className = "text"; //required for onkeydown check.checked = value; div.children[0].appendChild(check); check.onclick = function (figureId, property) { return function () { updateShape(figureId, property, this.checked) } } (figureId, this.property); DOMObject.appendChild(div); }, generateControlUnderLine: function (DOMObject) { var uLine = document.createElement("hr"); uLine.className = "editLine"; DOMObject.appendChild(uLine); }, /**Generate the code to edit the text. *The text got updated when you leave the input area * *@param {HTMLElement} DOMObject - the div of the properties panel *@param {Number} shapeId - the id of the {Figure} or {Connector} we are using **/ generateTextCode: function (DOMObject, shapeId) { var uniqueId = new Date().getTime(); var value = this.getValue(shapeId); var div = document.createElement("div"); div.className = "textLine"; var labelDiv = document.createElement("div"); labelDiv.className = "label"; labelDiv.textContent = this.name; div.appendChild(labelDiv); var text = document.createElement("textarea"); text.className = "text"; //required for onkeydown text.value = value; text.spellcheck = false; text.className = "textarea"; div.appendChild(document.createElement("br")); div.appendChild(text); // used to change Text property text.onchange = function (shapeId, property, scorpe) { return function () { // update shape but without adding {Command} to the {History} scorpe.PropertyValue = this.value; updateShape(shapeId, property, this.value); }; } (shapeId, this.property, this); text.onmouseout = text.onchange; text.onkeyup = text.onchange; DOMObject.appendChild(div); }, /**Generate the code to edit the text. *The text got updated when you leave the input area * *@param {HTMLElement} DOMObject - the div of the properties panel *@param {Number} figureId - the id of the figure we are using **/ generateSingleTextCode: function (DOMObject, figureId) { var uniqueId = new Date().getTime(); var value = this.getValue(figureId); var div = document.createElement("div"); div.className = "line"; var labelDiv = document.createElement("div"); labelDiv.className = "label"; labelDiv.textContent = this.name; // var icon = new Image(); // icon.className = 'prop-icon'; // icon.src = Builder.IMAGE_TEXT_ICON_PATH; // labelDiv.appendChild(icon); div.appendChild(labelDiv); var text = document.createElement("input"); text.type = "text"; text.className = "text"; //required for onkeydown text.value = value; div.appendChild(text); text.onchange = function (figureId, property, scop) { return function () { scop.PropertyValue = this.value; updateShape(figureId, property, this.value); } } (figureId, this.property, this); text.onmouseout = text.onchange; text.onkeyup = text.onchange; DOMObject.appendChild(div); }, generateSingleTextReadOnlyCode: function (DOMObject, figureId) { var uniqueId = new Date().getTime(); var value = this.getValue(figureId); var div = document.createElement("div"); div.className = "line"; var labelDiv = document.createElement("div"); labelDiv.className = "label"; labelDiv.textContent = this.name; div.appendChild(labelDiv); var text = document.createElement("input"); text.type = "text"; text.className = "textReadOnly"; //required for onkeydown text.value = value; text.setAttribute("readOnly", true); div.appendChild(text); text.onchange = function (figureId, property, scop) { return function () { scop.PropertyValue = this.value; updateShape(figureId, property, this.value); } } (figureId, this.property, this); text.onmouseout = text.onchange; text.onkeyup = text.onchange; DOMObject.appendChild(div); }, /**Generate the code to edit the URL. *The URL got updated when you leave the input area * *@param {HTMLElement} DOMObject - the div of the properties panel *@param {Number} figureId - the id of the figure we are using **/ generateURLCode: function (DOMObject, figureId) { var uniqueId = new Date().getTime(); var value = this.getValue(figureId); var div = document.createElement("div"); div.className = "line"; var labelDiv = document.createElement("div"); labelDiv.className = "label"; labelDiv.textContent = this.name; var icon = new Image(); icon.className = 'prop-icon'; icon.src = Builder.IMAGE_URL_ICON_PATH; labelDiv.appendChild(icon); div.appendChild(labelDiv); var text = document.createElement("input"); text.type = "text"; text.className = "text"; //required for onkeydown text.value = value; div.appendChild(text); text.onchange = function (figureId, property) { return function () { Log.info("Builder.generateURLCode() value: " + this.value); updateShape(figureId, property, this.value); } } (figureId, this.property); text.onmouseout = text.onchange; text.onkeyup = text.onchange; DOMObject.appendChild(div); }, generateGroupLabelCode: function (DOMObject, figureId) { var uniqueId = new Date().getTime(); var value = this.getValue(figureId); var div = document.createElement("div"); div.className = "line"; var labelDiv = document.createElement("div"); labelDiv.className = "GroupLabel"; labelDiv.textContent = this.name; div.appendChild(labelDiv); DOMObject.appendChild(div); }, /**Used to generate a upload file * *@param {HTMLElement} DOMObject - the div of the properties panel *@param {Number} figureId - the id of the figure we are using */ generateUploadCode: function (DOMObject, figureId) { var uniqueId = new Date().getTime(); var value = this.getValue(figureId); var div = document.createElement("div"); div.className = "line"; var labelDiv = document.createElement("div"); labelDiv.className = "label"; labelDiv.textContent = this.name; div.appendChild(labelDiv); var upload = document.createElement("input"); upload.type = "button"; upload.className = BuilderProperty.BUTTON_CHECKER_CLASS; upload.value = "选择文件"; div.appendChild(upload); upload.onclick = function (figureId, property) { return function () { showInsertImageDialog(); // property value stores in custom attribute var currentValue = this.getAttribute(BuilderProperty.BUTTON_CHECKED_ATTRIBUTE); // new value is inverse of the current one var newValue = currentValue == "true" ? false : true; // update control first this.setAttribute(BuilderProperty.BUTTON_CHECKED_ATTRIBUTE, newValue); Log.info("Builder.generateButtonCheckerCode() value: " + newValue); updateShape(figureId, property, newValue); }; } (figureId, this.property); DOMObject.appendChild(div); }, /**Used to generate a linkbutton for open window * *@param {HTMLElement} DOMObject - the div of the properties panel *@param {Number} figureId - the id of the figure we are using */ generateCCFormLinkCode: function (DOMObject, figureId) { var uniqueId = new Date().getTime(); var value = this.getValue(figureId); var div = document.createElement("div"); div.className = "line"; var href = document.createElement("a"); href.href = "javascript:CCForm_ShowDialog('" + this.PropertyValue + "','" + this.name + "');"; div.appendChild(href); var labelDiv = document.createElement("div"); labelDiv.className = "label"; labelDiv.textContent = this.name; href.appendChild(labelDiv); var icon = new Image(); icon.className = 'prop-icon'; icon.src = "Img/iconLink.png"; labelDiv.appendChild(icon); DOMObject.appendChild(div); }, /**Used to generate a drop down menu * *@param {HTMLElement} DOMObject - the div of the properties panel *@param {Number} figureId - the id of the figure we are using *@param {Array} v - a vector or hashes ex: [{Text:'Normal', Value:'Normal'},{Text:'Arrow', Value:'Arrow'}] */ generateArrayCode: function (DOMObject, figureId, v) { // Log.info("Font size length: " + v.length); var uniqueId = new Date().getTime(); var value = this.getValue(figureId); var div = document.createElement("div"); div.className = "line"; var labelDiv = document.createElement("div"); labelDiv.className = "label"; labelDiv.textContent = this.name; // get last name of property to define it's icon var propNames = this.property.split('.'); var propLastName = propNames.pop(); var icon; switch (propLastName) { case "lineWidth": icon = new Image(); icon.className = 'prop-icon'; icon.src = Builder.IMAGE_LINEWIDTH_ICON_PATH; labelDiv.appendChild(icon); break; case "startStyle": icon = new Image(); icon.className = 'prop-icon'; icon.src = Builder.IMAGE_STARTSTYLE_ICON_PATH; labelDiv.appendChild(icon); break; case "endStyle": icon = new Image(); icon.className = 'prop-icon'; icon.src = Builder.IMAGE_ENDSTYLE_ICON_PATH; labelDiv.appendChild(icon); break; case "lineStyle": icon = new Image(); icon.className = 'prop-icon'; icon.src = Builder.IMAGE_LINESTYLE_ICON_PATH; labelDiv.appendChild(icon); break; } div.appendChild(labelDiv); var select = document.createElement("select"); select.style.cssText = "float: right;"; select.id = this.property; // for DOM manipulation div.appendChild(select); for (var i = 0; i < v.length; i++) { var option = document.createElement("option"); option.value = v[i].Value; // Log.info("\t Text : " + v[i].Text + " Value : " + v[i].Value); option.text = v[i].Text; //see: http://www.w3schools.com/jsref/coll_select_options.asp select.options.add(option); //push does not exist in the options array if (option.value == value) { option.selected = true; } } select.onchange = function (figureId, property, scorpe) { return function () { var selecedValue = this.options[this.selectedIndex].value; scorpe.PropertyValue = selecedValue; updateShape(figureId, property, selecedValue); }; } (figureId, this.property, this); DOMObject.appendChild(div); }, /** *Used to generate a color picker * *@param{HTMLElement} DOMObject - the div of the properties panel *@param{Number} figureId - the id of the figure we are using */ generateColorCode: function (DOMObject, figureId) { var value = this.getValue(figureId); var uniqueId = new Date().getTime(); var div = document.createElement("div"); div.className = "line"; var labelDiv = document.createElement("div"); labelDiv.className = "label"; labelDiv.textContent = this.name; // get last name of property to define it's icon var propNames = this.property.split('.'); var propLastName = propNames.pop(); var icon; switch (propLastName) { case "fillStyle": icon = new Image(); icon.className = 'prop-icon'; icon.src = Builder.IMAGE_FILL_ICON_PATH; labelDiv.appendChild(icon); break; case "strokeStyle": icon = new Image(); icon.className = 'prop-icon'; icon.src = Builder.IMAGE_STROKE_ICON_PATH; labelDiv.appendChild(icon); break; } div.appendChild(labelDiv); var colorSelectorDiv = document.createElement("div"); colorSelectorDiv.id = 'colorSelector' + uniqueId; colorSelectorDiv.className = 'color-selector'; var colorInput = document.createElement("input"); colorInput.type = "text"; colorInput.id = 'colorpickerHolder' + uniqueId; colorInput.value = value; colorSelectorDiv.appendChild(colorInput); div.appendChild(colorSelectorDiv); DOMObject.appendChild(div); var colorPicker = document.getElementById('colorpickerHolder' + uniqueId); //let plugin do the job $(colorPicker).colorPicker(); //on change update the figure //var propExposedToAnonymous = this.property; colorPicker.onchange = function (figureId, property, scorpe) { return function () { var selecedValue = colorPicker.value.toUpperCase(); scorpe.PropertyValue = selecedValue; updateShape(figureId, property, selecedValue); }; } (figureId, this.property, this); }, /**Generate the code to edit the boolean property with button. *Result control has 2 modes: checked/unchecked. *The property got updated on click * *@param {HTMLElement} DOMObject - the div of the properties panel *@param {Number} figureId - the id of the figure we are using **/ generateButtonCheckerCode: function (DOMObject, figureId, buttonLabel) { var value = this.getValue(figureId); var div = document.createElement("div"); div.className = "line"; var labelDiv = document.createElement("div"); labelDiv.className = "label"; labelDiv.textContent = this.name; div.appendChild(labelDiv); var buttonChecker = document.createElement("input"); buttonChecker.type = "button"; buttonChecker.id = this.property; // for DOM manipulation buttonChecker.className = BuilderProperty.BUTTON_CHECKER_CLASS; buttonChecker.value = buttonLabel; // for now we have button checking only for underlined text property // property value stores in custom attribute buttonChecker.setAttribute(BuilderProperty.BUTTON_CHECKED_ATTRIBUTE, value); div.appendChild(buttonChecker); buttonChecker.onclick = function (figureId, property) { return function () { // property value stores in custom attribute var currentValue = this.getAttribute(BuilderProperty.BUTTON_CHECKED_ATTRIBUTE); // new value is inverse of the current one var newValue = currentValue == "true" ? false : true; // update control first this.setAttribute(BuilderProperty.BUTTON_CHECKED_ATTRIBUTE, newValue); Log.info("Builder.generateButtonCheckerCode() value: " + newValue); updateShape(figureId, property, newValue); }; } (figureId, this.property); DOMObject.appendChild(div); }, /**We use this to return a value of the property for a figure, *Similar to Javas Class.forname...sort of anyway *We need this because passing direct references to simple data types (including strings) *only passes the value, not a reference to that value (call by value not by reference) * *@param{Number} figureId - the id of the shape {Figure} or {Connector} we are using, could also be the canvas (figureId = 'a') */ getValue: function (figureId) { //Is it a Figure? var obj = STACK.figureGetById(figureId); //Is it a Connector ? if (obj == null) { //ok so it's not a Figure...so it should be a Connector obj = CONNECTOR_MANAGER.connectorGetById(figureId); } //Is it the Canvas? if (obj == null) { if (figureId == "canvas") { obj = canvas; } } //Is it a Container? if (obj == null) { obj = STACK.containerGetById(figureId); } Log.debug("Unsplit property: " + this.property); var propertyAccessors = this.property.split("."); // Log.info("BuilderProperty::getValue() : propertyAccessors : " + propertyAccessors ); for (var i = 0; i < propertyAccessors.length - 1; i++) { // Log.info("\tBuilderProperty::getValue() : i = " + i + ' name= ' + propertyAccessors[i]); obj = obj[propertyAccessors[i]]; } //Log.info("Object type: " + obj.oType); var propName = propertyAccessors[propertyAccessors.length - 1]; //Log.info("Property name: " + propName); var propGet = "get" + Util.capitaliseFirstLetter(propName); //null is allowed, undefined is not if (propGet in obj) { //@see https://developer.mozilla.org/en/JavaScript/Reference/Operators/Special_Operators/in_Operator var returnValue = obj[propGet](); if (typeof (returnValue) == "undefined") return this.PropertyValue; return obj[propGet](); } else { //Access the object property's var returnValue = obj[propertyAccessors[propertyAccessors.length - 1]]; if (typeof (returnValue) == "undefined") return this.PropertyValue; return obj[propertyAccessors[propertyAccessors.length - 1]]; } } }; /** * This instance is responsible for creating and updating Text Editor Popup. * Text Editor Popup is made out of: * - editor - a
(inside #container
) that contains the text and reflects the * - tools - a
(inside #container
) that will contain all the buttons and options to format text * * @constructor * @this {TextEditorPopup} * @param {HTMLElement} editor - the DOM object (a
inside editor page) to create Text Editor Popup * @param {HTMLElement} tools - the DOM object (a
inside editor page) to create Text Editor Tools * @param shape - the {Figure} or {Connector} - parent of Text primitive * @param {Number} textPrimitiveId - the id value of Text primitive child of shape for which the properties will be displayed * @author Artyom Pokatilov */ function TextEditorPopup(editor, tools, shape, textPrimitiveId) { this.editor = editor; this.tools = tools; this.shape = shape; this.textPrimitiveId = textPrimitiveId; /*We need to construct the full path to the properties of Text*/ // beginning of property string of BuilderProperty for primitive var propertyPrefix; if (this.shapeIsAConnector()) { // in case of connector with primitive = middleText propertyPrefix = "middleText."; } else { // in case of figure with primitive.id = textPrimitiveId propertyPrefix = "primitives." + this.textPrimitiveId + "."; } // value of BuiderProperty::property this.stringPropertyName = propertyPrefix + TextEditorPopup.STRING_PROPERTY_ENDING; this.sizePropertyName = propertyPrefix + TextEditorPopup.SIZE_PROPERTY_ENDING; this.fontPropertyName = propertyPrefix + TextEditorPopup.FONT_PROPERTY_ENDING; this.alignPropertyName = propertyPrefix + TextEditorPopup.ALIGN_PROPERTY_ENDING; this.colorPropertyName = propertyPrefix + TextEditorPopup.COLOR_PROPERTY_ENDING; this.underlinedPropertyName = propertyPrefix + TextEditorPopup.UNDERLINED_PROPERTY_ENDING; } /**A set of predefined properties fragments*/ TextEditorPopup.STRING_PROPERTY_ENDING = 'str'; TextEditorPopup.SIZE_PROPERTY_ENDING = 'size'; TextEditorPopup.FONT_PROPERTY_ENDING = 'font'; TextEditorPopup.ALIGN_PROPERTY_ENDING = 'align'; TextEditorPopup.COLOR_PROPERTY_ENDING = 'style.fillStyle'; TextEditorPopup.UNDERLINED_PROPERTY_ENDING = 'underlined'; TextEditorPopup.prototype = { constructor: TextEditorPopup, /** *Returns true if target shape of TextEditorPopup is a Connector *@return {Boolean} - true shape property is a connector *@author Artyom Pokatilov **/ shapeIsAConnector: function () { return this.shape.oType === "Connector"; }, /** *Creates DOM structure and bind events *@author Artyom Pokatilov **/ init: function () { var textarea; //
for text tools contains font size, font family, alignment and color for (var i = 0; i < this.shape.properties.length; i++) { var curProperty = this.shape.properties[i].property; //get property in long format ex: primitives.1.style.fillStyle if (curProperty != null) { var curValue = this.shape.properties[i].getValue(this.shape.id); switch (curProperty) { case this.stringPropertyName: this.shape.properties[i].injectInputArea(this.editor, this.shape.id); textarea = this.editor.getElementsByTagName('textarea')[0]; // remove all
tags from text-editor as they were added by injectInputArea method removeNodeList(this.editor.getElementsByTagName('br')); //defined in util.js // set Text editor properties on initialization this.setProperty(curProperty, curValue); break; case this.sizePropertyName: case this.fontPropertyName: case this.alignPropertyName: case this.colorPropertyName: case this.underlinedPropertyName: this.shape.properties[i].injectInputArea(this.tools, this.shape.id); // set Text editor properties on initialization this.setProperty(curProperty, curValue); break; } } } this.editor.className = 'active'; this.tools.className = 'active'; this.placeAndAutoSize(); // select all text inside textarea (like in Visio) setSelectionRange(textarea, 0, textarea.value.length); }, /** * Changing property inside Text Editor * provides WYSIWYG functionality * @param {String} property - property name that is being edited (in dotted notation) * @param {Object} value - the value to set the property to * @author Artyom Pokatilov **/ setProperty: function (property, value) { var textarea = this.editor.getElementsByTagName('textarea')[0]; switch (property) { case this.sizePropertyName: // set new property value to editor's textarea textarea.style.fontSize = value + 'px'; // set new property value to editor's tool document.getElementById(property).value = value; break; case this.fontPropertyName: // set new property value to editor's textarea textarea.style.fontFamily = value; // set new property value to editor's tool document.getElementById(property).value = value.toLowerCase(); break; case this.alignPropertyName: // set new property value to editor's textarea textarea.style.textAlign = value; // IE doesn't apply text-align property correctly to all lines of the textarea on a fly // that is why we just copy it's text and paste it back to refresh text rendering if (Browser.msie) { textarea.value = textarea.value; } // set new property value to editor's tool document.getElementById(property).value = value; break; case this.underlinedPropertyName: // set new property value to editor's textarea textarea.style.textDecoration = value == true ? 'underline' : ''; // set new property value to editor's tool document.getElementById(property).setAttribute(BuilderProperty.BUTTON_CHECKED_ATTRIBUTE, value); break; case this.colorPropertyName: // set new property value to editor's textarea textarea.style['color'] = value; // set new property value to editor's tool (colorPicker) var colorPicker = this.tools.getElementsByClassName('color_picker')[0]; colorPicker.style['background-color'] = value; //change the color to the proper one colorPicker.previousSibling.value = value; //set the value to the "hidden" text field break; } this.placeAndAutoSize(); }, /** *Places and sets size to the property panel *@author Artyom Pokatilov **/ placeAndAutoSize: function () { var textarea = this.editor.getElementsByTagName('textarea')[0]; // set edit dialog position to top left (first) bound point of Text primitive var textBounds; if (this.shapeIsAConnector()) { // in case of connector primitive is a middleText property textBounds = this.shape.middleText.getBounds(); } else { // in case of connector primitive is a primitives[this.textPrimitiveId] property textBounds = this.shape.primitives[this.textPrimitiveId].getBounds(); } // change coordinates of editing Text primitive to include padding and border of Text Editor var leftCoord = textBounds[0] - defaultEditorBorderWidth - defaultEditorPadding; var topCoord = textBounds[1] - defaultEditorBorderWidth - defaultEditorPadding; var textareaWidth = textBounds[2] - textBounds[0]; var textareaHeight = textBounds[3] - textBounds[1]; // Firefox includes border & padding as part of width and height, // so width and height should additionally include border and padding twice // (similar to "feather" option in Fireworks) if (Browser.mozilla) { textareaHeight += (defaultEditorPadding) * 2; topCoord -= (defaultEditorPadding); textareaWidth += (defaultEditorPadding) * 2; leftCoord -= (defaultEditorPadding); } // some of IE magic: // enough to add half of font-size to textarea's width to prevent auto-breaking to next line // which is wrong in our case // (similar to "feather" option in Fireworks) if (Browser.msie) { var fontSize = parseInt(textarea.style['font-size'], 10); textareaWidth += fontSize / 2; leftCoord -= fontSize / 4; } this.editor.style.left = leftCoord + "px"; this.editor.style.top = topCoord + "px"; // visibility: 'hidden' allows us to get proper size but // without getting strange visual artefacts (tiggered by settings positions & other) this.tools.style.visibility = 'hidden'; // We set it to the left upper corner to get it's objective size this.tools.style.left = '0px'; this.tools.style.top = '0px'; // Get toolbox height and width. Notice that clientHeight differs from offsetHeight. //@see https://developer.mozilla.org/en/docs/DOM/element.offsetHeight //@see http://stackoverflow.com/questions/4106538/difference-between-offsetheight-and-clientheight var toolboxHeight = this.tools.offsetHeight; var toolboxWidth = this.tools.offsetWidth; // define toolbox left position var toolboxLeft = leftCoord; // get width of work area (#container
from editor) var workAreaWidth = getWorkAreaContainer().offsetWidth; // If it's not enough place for toolbox at the page right side if (toolboxLeft + toolboxWidth >= workAreaWidth - scrollBarWidth) { // then shift toolbox to left before it can be placed toolboxLeft = workAreaWidth - toolboxWidth - scrollBarWidth; } // define toolbox top position var toolboxTop = topCoord - toolboxHeight; // If it's not enough place for toolbox at the page top if (toolboxTop <= 0) { // then place toolbox below textarea toolboxTop = topCoord + toolboxHeight + defaultEditorBorderWidth + defaultEditorPadding; } this.tools.style.left = toolboxLeft + "px"; this.tools.style.top = toolboxTop + "px"; // return normal visibility to toolbox this.tools.style.visibility = 'visible'; textarea.style.width = textareaWidth + "px"; textarea.style.height = textareaHeight + "px"; }, /** *Removes DOM structure of editor and it's tools *@author Artyom Pokatilov **/ destroy: function () { this.editor.className = ''; this.editor.style.cssText = ''; this.editor.innerHTML = ''; this.tools.className = ''; this.tools.style.cssText = ''; this.tools.innerHTML = ''; }, /** *Returns true if mouse clicked inside TextEditorPopup *@param {Event} e - mouseDown event object *@return {boolean} - true if clicked inside *@author Artyom Pokatilov **/ mouseClickedInside: function (e) { var target = e.target; // check if user fired mouse down on the part of editor, it's tools or active color picker // actually active color picker in that moment can be only for Text edit var inside = target.id === this.editor.id || target.parentNode.id === this.editor.id || target.parentNode.parentNode.id === this.editor.id || target.id === this.tools.id || target.parentNode.id === this.tools.id || target.parentNode.parentNode.id === this.tools.id || target.className === 'color_picker' || target.id === 'color_selector' || target.parentNode.id === 'color_selector' || target.parentNode.parentNode.id === 'color_selector'; return inside; }, /** * Checks if TextEditorPopup refers to target shape and id of Text primitive * @param shape - target figure or connector to check * @param {Number} textPrimitiveId - the id value of a target Text primitive * *@return {Boolean} - true if refers to target objects *@author Artyom Pokatilov **/ refersTo: function (shape, textPrimitiveId) { var result = this.shape.equals(shape); // in case of connector textPrimitiveId will be underfined if (textPrimitiveId != null) { result &= this.textPrimitiveId === textPrimitiveId; } return result; }, /** * Manually triggers onblur event of textarea inside TextEditor. * @author Artyom Pokatilov **/ blurTextArea: function () { var textarea = this.editor.getElementsByTagName('textarea')[0]; textarea.onblur(); } };