1 /**
  2 * @fileOverview 
  3 * @author <a href="mailto:ovaraksin@googlemail.com">Oleg Varaksin</a>
  4 * @version 0.2
  5 */
  6 
  7 /**
  8 * Initialization function for the entire whiteboard application.
  9 * @function
 10 * @param jsWhiteboard current whiteboard in JSON format
 11 * @param whiteboardId whiteboard's id
 12 * @param user user (user name) working with this whiteboard
 13 * @param usersCount number of users working with this whiteboard 
 14 * @param pubSubUrl URL for bidirectional communication
 15 * @param pubSubTransport transport protocol "long-polling" | "streaming" | "websocket" 
 16 */
 17 function initWhiteboard(jsWhiteboard, whiteboardId, user, usersCount, pubSubUrl, pubSubTransport) {
 18     // bind onclick handler for toolbox items
 19     bindOnclickToolboxItems();
 20 
 21     // configure validator
 22     jQuery.validator.addMethod("imageSize", function(value, element, param) {
 23         if (jQuery.find(param).length < 1) {
 24             return true;
 25         }
 26 
 27         return !this.optional(element) && jQuery.validator.methods['digits'].call(this, value, element) && parseInt(value) > 0;
 28     }, "Please enter a valid image size (only positive digits are allowed).");
 29 
 30     var dimensionRules = {
 31         required: true,
 32         digits: true,
 33         min: 1
 34     };
 35 
 36     // create validator
 37     dialogValidator = jQuery("#mainForm").validate({
 38         onfocusout: false,
 39         onkeyup: false,
 40         errorPlacement: function(label, elem) {
 41             elem.closest(".validatable").find(".errormsg").append(label);
 42         },
 43         wrapper: "li",
 44         rules: {
 45             inputUrl: {
 46                 url: true
 47             },
 48             imgWidth: {
 49                 imageSize: "#inputUrl:filled"
 50             },
 51             imgHeight: {
 52                 imageSize: "#inputUrl:filled"
 53             },
 54             wbWidth: dimensionRules,
 55             wbHeight: dimensionRules
 56         },
 57         messages: {
 58             inputUrl: "Please enter a valid image URL.",
 59             imgWidth: "Please enter a valid image width (only positive digits are allowed).",
 60             imgHeight: "Please enter a valid image height (only positive digits are allowed).",
 61             wbWidth: "Please enter a valid whiteboard width (only positive digits are allowed).",
 62             wbHeight: "Please enter a valid whiteboard height (only positive digits are allowed)."
 63         }
 64     });
 65 
 66     // configure dialogs
 67     jQuery("#dialogInputText").dialog("option", "buttons", {
 68         "Accept": function() {
 69             var inputText = jQuery(this).find("#textArea").val();
 70             jQuery(this).dialog("close");
 71             whiteboardDesigner.drawText(inputText);
 72         },
 73         "Close": function() {
 74             jQuery(this).dialog("close");
 75         }
 76     }).bind("dialogclose", function(event, ui) {
 77         jQuery(this).find("#textArea").val('');
 78     });
 79 
 80     jQuery("#dialogInputImage").dialog("option", "buttons", {
 81         "Accept": function() {
 82             var isValid1 = dialogValidator.element("#inputUrl");
 83             var isValid2 = dialogValidator.element("#imgWidth");
 84             var isValid3 = dialogValidator.element("#imgHeight");
 85 
 86             if ((typeof isValid1 !== 'undefined' && !isValid1) || (typeof isValid2 !== 'undefined' && !isValid2) || (typeof isValid3 !== 'undefined' && !isValid3)) {
 87                 // validation failed
 88                 return false;
 89             }
 90 
 91             var jq = jQuery(this);
 92             var inputUrl = jq.find("#inputUrl").val();
 93             var imgWidth = jq.find("#imgWidth").val();
 94             var imgHeight = jq.find("#imgHeight").val();
 95             jq.dialog("close");
 96             whiteboardDesigner.drawImage(inputUrl, imgWidth, imgHeight);
 97         },
 98         "Close": function() {
 99             jQuery(this).dialog("close");
100         }
101     }).bind("dialogopen", function(event, ui) {
102         // show default width / height
103         jQuery(this).find("#imgWidth").val(whiteboardDesigner.config.properties.image.width);
104         jQuery(this).find("#imgHeight").val(whiteboardDesigner.config.properties.image.height);
105     }).bind("dialogclose", function(event, ui) {
106         // reset input
107         jQuery(this).find("#inputUrl").val('');
108         // clean up validation messages
109         jQuery("#errorImageUl").html('');
110     });
111 
112     jQuery("#dialogResize").dialog("option", "buttons", {
113         "Accept": function() {
114             var isValid1 = dialogValidator.element("#wbWidth");
115             var isValid2 = dialogValidator.element("#wbHeight");
116 
117             if ((typeof isValid1 !== 'undefined' && !isValid1) || (typeof isValid2 !== 'undefined' && !isValid2)) {
118                 // validation failed
119                 return false;
120             }
121 
122             var jq = jQuery(this);
123             var wbWidth = jq.find("#wbWidth").val();
124             var wbHeight = jq.find("#wbHeight").val();
125             jq.dialog("close");
126             whiteboardDesigner.resizeWhiteboard(wbWidth, wbHeight);
127         },
128         "Close": function() {
129             jQuery(this).dialog("close");
130         }
131     }).bind("dialogclose", function(event, ui) {
132         //var jq = jQuery(this);
133         //jq.find("#wbWidth").val('');
134         //jq.find("#wbHeight").val('');
135         // clean up validation messages
136         jQuery("#errorResizeUl").html('');
137     });
138 
139     jQuery("#dialogIcons").dialog("option", "buttons", {
140         "Close": function() {
141             jQuery(this).dialog("close");
142         }
143     });
144 
145     // create a global whiteboard designer instance
146     whiteboardDesigner = new WhiteboardDesigner(new WhiteboardConfig(), whiteboardId, user, pubSubUrl, pubSubTransport);
147 
148     // restore existing whiteboard if any exists
149     if (!isBlankObject(jsWhiteboard)) {
150         whiteboardDesigner.restoreWhiteboard(jsWhiteboard);
151     }
152 
153     // subscribe to bidirectional channel
154     whiteboardDesigner.subscribePubSub();
155 
156     if (usersCount > 1) {
157         // notificate subscribers about new user
158         setTimeout(function() {
159             whiteboardDesigner.joinUser(usersCount);
160         }, 1000);
161     }
162 }
163 
164 /**
165 * Checks whether an JavaScript object is null or empty.
166 * @function
167 * @param obj any JavaScript object  
168 */
169 function isBlankObject(obj) {
170     if (obj == null) {
171         return true;
172     }
173 
174     for (var prop in obj) {
175         if (obj.hasOwnProperty(prop)) {
176             return false;
177         }
178     }
179 
180     return true;
181 }
182 
183 /**
184 * Sets auto width of the given dialog on dialog's show event.
185 * @function
186 * @param jqDialog jQuery object for dialog  
187 */
188 function onShowAutoWidthDialog(jqDialog) {
189     // fix for auto width in IE
190     var parent = jqDialog.parent();
191     var contentWidth = jqDialog.width();
192     parent.find('.ui-dialog-titlebar').each(function() {
193         jQuery(this).width(contentWidth);
194 
195     });
196     parent.removeClass("autoWidthDialog").width(contentWidth + 26);
197     jqDialog.dialog('option', 'position', 'center');
198 
199     // fix for scrollbars in IE
200     jQuery('body').css('overflow', 'hidden');
201     jQuery('.ui-widget-overlay').css('width', '100%');
202 }
203 
204 /**
205 * Sets auto width of the given dialog on dialog's hide event.
206 * @function
207 * @param jqDialog jQuery object for dialog  
208 */
209 function onHideAutoWidthDialog(jqDialog) {
210     // fix for auto width in IE
211     var parent = jqDialog.parent();
212     parent.find('.ui-dialog-titlebar').each(function() {
213         // reset titlebar width
214         jQuery(this).css('width', '');
215     });
216     parent.addClass("autoWidthDialog");
217 
218     // fix for scrollbars in IE
219     jQuery('body').css('overflow', 'auto');
220 }
221 
222 /**
223 * Adjusts width of the given dialog on dialog's show event.
224 * @function
225 * @param jqDialog jQuery object for dialog  
226 */
227 function adjustOpenAutoWidthDialog(dialogId) {
228     var jqDialog = jQuery('#' + dialogId);
229     var parent = jqDialog.parent();
230     parent.find('.ui-dialog-titlebar').each(function() {
231         jQuery(this).css('width', '');
232     });
233     parent.addClass('autoWidthDialog');
234 
235     onShowAutoWidthDialog(jqDialog);
236 }
237 
238 /**
239 * Removes unused panels after "pin panels".
240 * @function
241 */
242 function removeUnusedDialogs() {
243     jQuery('#toolboxDialog').remove();
244     jQuery('#propertiesDialog').remove();
245     jQuery('#monitoringDialog').remove();
246 }
247 
248 /**
249 * Binds onlick event handlers to all items in the "Toolbox" panel.
250 * @function
251 */
252 function bindOnclickToolboxItems() {
253     var toolboxItems = jQuery('.toolboxItem');
254     toolboxItems.bind('click', function() {
255         toolboxItems.removeClass('ui-state-selected');
256         jQuery(this).addClass('ui-state-selected');
257     });
258 }
259 
260 /**
261 * Pins panels.
262 * @function
263 */
264 function pinPanels() {
265     removeUnusedDialogs();
266     bindOnclickToolboxItems();
267     whiteboardDesigner.setIdSubviewProperties('#pinnedSubview');
268 
269     // show properties of last selected element
270     if (whiteboardDesigner.getSelectedObject() != null) {
271         whiteboardDesigner.showSelectedProperties(whiteboardDesigner.getSelectedObject());
272     }
273 
274     // hide loading dialog
275     loadingDialogWidget.hide();
276 }
277 
278 /**
279 * Unpins panels.
280 * @function
281 */
282 function unpinPanels() {
283     bindOnclickToolboxItems();
284     whiteboardDesigner.setIdSubviewProperties('#unpinnedSubview');
285 
286     // show properties of last selected element
287     if (whiteboardDesigner.getSelectedObject() != null) {
288         whiteboardDesigner.showSelectedProperties(whiteboardDesigner.getSelectedObject());
289     }
290 
291     // hide loading dialog
292     loadingDialogWidget.hide();
293 }
294 
295 /**
296 * Converts RGB color to Hex color. This is an utility function for Color Picker.
297 * @function
298 * @param color color as RGB 
299 */
300 function colorToHex(color) {
301     if (color.substr(0, 1) === '#') {
302         return color;
303     }
304     var digits = /(.*?)rgb\((\d+), (\d+), (\d+)\)/.exec(color);
305 
306     var red = parseInt(digits[2]);
307     var green = parseInt(digits[3]);
308     var blue = parseInt(digits[4]);
309 
310     var rgb = blue | (green << 8) | (red << 16);
311     return digits[1] + '#' + rgb.toString(16);
312 }
313 
314 /**
315 * Sends changed properties to {@link WhiteboardDesigner} which sends them to the server.
316 * @function
317 * @param type element type
318 * @param resize boolean flag, true - resize element, false - otherwise.
319 * @param resize boolean flag, true - rotate element, false - otherwise. 
320 */
321 function sendPropertiesChanges(type, resize, rotate) {
322     whiteboardDesigner.sendPropertiesChanges(type, resize, rotate);
323 }
324 
325 /**
326 * Toggles logging component.
327 * @function
328 */
329 function toggleLogging() {
330     log.toggle();
331     whiteboardDesigner.logging = !whiteboardDesigner.logging;
332 }
333