function getXmlHttp(){ var xmlhttp; try { xmlhttp = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { try { xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } catch (E) { xmlhttp = false; } } if (!xmlhttp && typeof XMLHttpRequest!='undefined') { xmlhttp = new XMLHttpRequest(); } return xmlhttp; } var RedmineHelpdeskWidget = { widget: document.getElementById('helpdesk_widget'), widget_button: null, width: 400, height: 500, margin: 20, iframe: null, form: null, schema: null, reload: false, configuration: {}, attachment: null, base_url: '<%= Setting.protocol %>://<%= Setting.host_name %>', config: function(configuration){ this.configuration = configuration; this.apply_config(); }, apply_config: function(){ if (this.configuration['color']) { this.widget_button.style.backgroundColor = this.configuration['color']; } switch (this.configuration['position']) { case 'topLeft': this.widget.style.top = '20px'; this.widget.style.left = '20px'; break; case 'topRight': this.widget.style.top = '20px'; this.widget.style.right = '20px'; break; case 'bottomLeft': this.widget.style.bottom = '20px'; this.widget.style.left = '20px'; break; case 'bottomRight': this.widget.style.bottom = '20px'; this.widget.style.right = '20px'; break; default: widget.style.bottom = '20px'; widget.style.right = '20px'; } }, translation: function(field){ return this.configuration['translation'] && this.configuration['translation'][field] ? this.configuration['translation'][field] : null; }, identify: function(field){ return this.configuration['identify'] && this.configuration['identify'][field] ? this.configuration['identify'][field] : null }, load: function() { this.widget.addEventListener('click', function(){ RedmineHelpdeskWidget.toggle() }); this.create_widget_button(); this.decorate_widget_button(); this.create_iframe(); this.decorate_iframe(); this.load_schema(); this.created = true; }, load_schema: function() { var xmlhttp = getXmlHttp(); xmlhttp.open('GET', this.base_url + '/helpdesk_widget/load_form.json', true); xmlhttp.responseType = 'json'; xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4) { if (xmlhttp.status == 200 || xmlhttp.status == 304) { RedmineHelpdeskWidget.schema = xmlhttp.response; RedmineHelpdeskWidget.fill_form(); } else { RedmineHelpdeskWidget.schema = {}; } } }; xmlhttp.send(null); }, create_widget_button: function(){ button = document.createElement('div'); button.id = 'widget_button'; button.className = 'widget_button'; button.innerHTML = this.configuration['icon'] || '?'; button.setAttribute('name', 'helpdesk_widget_button'); button.style.backgroundColor = '#7E8387'; button.style.backgroundSize = '15px 15px'; button.style.cursor = 'pointer'; button.style.color = 'white'; button.style.textAlign = 'center'; button.style.fontSize = '32px'; button.style.verticalAlign = 'middle'; button.style.lineHeight = '54px'; button.style.borderRadius = '30px'; button.style.boxShadow = 'rgba(0, 0, 0, 0.258824) 0px 2px 5px 0px'; button.style.display = 'none'; button.style.webkitTransition = "transform 0.2s ease"; this.widget_button = button; this.widget.appendChild(button); }, decorate_widget_button: function(){ widget = this.widget; widget.style.position = 'fixed'; widget.style.bottom = '20px'; widget.style.right = '20px'; widget.style.width = '54px'; widget.style.height = '54px'; widget.style.zIndex = 9999; }, create_iframe: function(){ this.iframe = document.createElement('iframe'); this.widget.appendChild(this.iframe); }, decorate_iframe: function(){ iframe = this.iframe; iframe.setAttribute('id', 'helpdesk_ticket_container'); iframe.setAttribute('width', this.width); iframe.setAttribute('height', 0); iframe.setAttribute('frameborder', 0); iframe.style.visibility = 'hidden'; iframe.style.position = 'absolute'; iframe.style.opacity = '0'; iframe.style.width = this.width; iframe.style.backgroundColor = 'white'; iframe.style.webkitTransition = "opacity 0.2s ease"; iframe.style.boxShadow = 'rgba(0, 0, 0, 0.258824) 0px 1px 4px 0px'; iframe.setAttribute('name', 'helpdesk_widget_iframe'); }, fill_form: function(){ if (Object.keys(this.schema.projects).length > 0) { this.apply_avatar(); this.create_form(); this.create_form_title(); this.create_error_flash(); if (this.identify('redmineUserID')) { this.create_form_hidden(this.form, 'redmine_user', 'redmine_user', 'form-control', this.identify('redmineUserID')); } if (this.identify('nameValue')) { this.create_form_hidden(this.form, 'username', 'username', 'form-control', this.identify('nameValue')); } else { this.create_form_text(this.form, 'username', 'username', this.translation('nameLabel') || '<%= t(:label_helpdesk_widget_name) %>', 'form-control', this.identify('nameValue'), true); } if (this.identify('emailValue')) { this.create_form_hidden(this.form, 'email', 'email', 'form-control', this.identify('emailValue')); } else { this.create_form_text(this.form, 'email', 'email' , this.translation('emailLabel') || '<%= t(:label_helpdesk_widget_email) %>', 'form-control', this.identify('emailValue'), true); } if (this.identify('subjectValue')) { this.create_form_hidden(this.form, 'subject', 'issue[subject]', 'form-control', this.identify('subjectValue')); } else { this.create_form_text(this.form, 'subject', 'issue[subject]' , this.translation('subjectLabel') || '<%= t(:label_helpdesk_widget_subject) %>', 'form-control', this.identify('subjectValue'), true); } this.create_projects_selector(); this.create_form_area(this.form, 'description', 'issue[description]' , this.translation('descriptionLabel') || '<%= t(:label_helpdesk_widget_description) %>', 'form-control', true); var project_id = null; var tracker_id = null; if (RedmineHelpdeskWidget.configuration['identify']){ project_id = RedmineHelpdeskWidget.schema.projects[RedmineHelpdeskWidget.configuration['identify']['projectValue']]; if (project_id) { tracker_id = RedmineHelpdeskWidget.schema.projects_data[project_id].trackers[RedmineHelpdeskWidget.configuration['identify']['trackerValue']]; } } this.load_project_data(project_id || this.schema.projects[Object.keys(this.schema.projects)[0]], tracker_id); this.iframe.contentWindow.document.body.appendChild(this.form); this.append_stylesheets(); this.append_scripts(); this.create_message_listener(); } else { this.widget.style.display = 'none'; } }, apply_avatar: function(){ button = document.getElementById('widget_button'); avatar = RedmineHelpdeskWidget.configuration['user_avatar']; if (avatar && avatar.length > 0) { var xmlhttp = getXmlHttp(); xmlhttp.open('GET', RedmineHelpdeskWidget.base_url + '/helpdesk_widget/avatar/' + avatar, true); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4) { if (xmlhttp.status == 200 || xmlhttp.status == 304) { button.style.backgroundSize = 'cover'; button.style.backgroundImage = 'url(' + RedmineHelpdeskWidget.base_url + '/helpdesk_widget/avatar/' + avatar + ')' ; button.style.border = '2px solid'; button.innerHTML = ' '; } else { button.style.backgroundSize = '15px 15px'; button.innerHTML = '?'; } button.style.display = 'block'; button.style.lineHeight = '50px'; } }; xmlhttp.send(null); } else { button.style.lineHeight = '54px'; button.style.backgroundSize = '15px 15px'; button.innerHTML = '?'; button.style.display = 'block'; } }, append_stylesheets: function(){ if (this.configuration['styles']) { styles_css = document.createElement('style'); styles_css.innerHTML = this.configuration['styles']; styles_css.type = "text/css"; } widget_css = document.createElement('link'); widget_css.href = this.base_url + '/helpdesk_widget/widget.css'; widget_css.rel = "stylesheet"; widget_css.type = "text/css"; this.iframe.contentWindow.document.head.appendChild(widget_css); if (this.configuration['styles']) { this.iframe.contentWindow.document.head.appendChild(styles_css); } }, append_scripts: function(){ script = document.createElement('script'); script.type = 'text/javascript'; script.src = this.base_url + '/helpdesk_widget/iframe.js'; this.iframe.contentWindow.document.head.appendChild(script); config_script = document.createElement('script'); config_script.innerHTML = "var RedmineHelpdeskIframe = {configuration: "+ JSON.stringify(this.configuration) +"}"; this.iframe.contentWindow.document.head.appendChild(config_script); }, create_form: function(){ this.form = document.createElement('form'); this.form.action = this.base_url + '/helpdesk_widget/create_ticket'; this.form.acceptCharset = 'UTF-8'; this.form.method = 'post'; this.form.id = 'widget_form'; this.form.setAttribute('onSubmit', 'submitTicketForm(); return false;'); this.form.style.marginBottom = 0; }, create_form_title: function(){ if (this.configuration['title']) { title_div = document.createElement('div'); title_div.id = 'title'; title_div.className = 'title'; title_div.innerHTML = this.configuration['title']; this.form.appendChild(title_div); } }, create_error_flash: function(){ flash_div = document.createElement('div'); flash_div.id = 'flash'; flash_div.className = 'flash'; this.form.appendChild(flash_div); }, create_projects_selector: function(){ var project_id = null; if (RedmineHelpdeskWidget.configuration['identify']){ project_id = RedmineHelpdeskWidget.schema.projects[RedmineHelpdeskWidget.configuration['identify']['projectValue']]; } if (project_id) { this.create_form_hidden(this.form, 'project_id', 'project_id', 'form-control projects', project_id); } else { this.create_form_select(this.form, 'project_id', 'project_id', RedmineHelpdeskWidget.schema.projects, project_id, 'form-control projects'); } }, load_project_data: function(project_id, tracker_id){ container_div = this.form.getElementsByClassName('container')[0] if (container_div) { container_div.remove() }; container_div = document.createElement('div'); container_div.id = 'container'; container_div.className = 'container'; custom_div = document.createElement('div'); custom_div.id = 'custom_fields'; custom_div.className = 'custom_fields'; submit_div = document.createElement('div'); submit_div.id = 'submit_button'; submit_div.className = 'submit_button'; container_div.appendChild(custom_div); container_div.appendChild(submit_div); if (RedmineHelpdeskWidget.configuration['identify'] && RedmineHelpdeskWidget.schema.projects_data[project_id].trackers[RedmineHelpdeskWidget.configuration['identify']['trackerValue']]){ tracker_id = RedmineHelpdeskWidget.schema.projects_data[project_id].trackers[RedmineHelpdeskWidget.configuration['identify']['trackerValue']] this.create_form_hidden(custom_div, 'tracker_id', 'tracker_id', 'form-control trackers', tracker_id); } else { this.create_form_select(custom_div, 'tracker_id', 'tracker_id', this.schema.projects_data[project_id].trackers, tracker_id, 'form-control trackers'); tracker_id = custom_div.getElementsByClassName('trackers')[0].value; } this.load_custom_fields(custom_div, project_id, tracker_id); this.create_form_submit(submit_div, this.translation('createButtonLabel') || '<%= l(:label_helpdesk_widget_create_ticket) %>'); this.create_attch_link(submit_div); this.form.appendChild(container_div); }, reload_project_data: function(){ project_id = this.form.getElementsByClassName('projects')[0].value; tracker_id = container_div.getElementsByClassName('trackers')[0].value; this.load_project_data(project_id, tracker_id); this.positionate_iframe(); }, create_form_select: function(target, field_id, field_name, values, selected, field_class){ if (Object.keys(values).length == 1) { field = document.createElement('input'); field.type = 'hidden'; field.id = field_id; field.name = field_name; field.className = field_class; field.value = values[Object.keys(values)[0]]; } else { field = document.createElement('select'); field.id = field_id; field.name = field_name; field.className = field_class; for (var project in values) { option = document.createElement('option'); option.value = values[project] if(values[project] == selected) { option.selected = 'selected'; } option.innerHTML = project; field.appendChild(option); } } field.setAttribute('onChange', 'needReloadProjectData();'); target.appendChild(field); }, create_form_hidden: function(target, field_id, field_name, field_class, value){ field = document.createElement('input'); field.type = 'hidden'; field.id = field_id; field.name = field_name; field.value = value; field.className = field_class; target.appendChild(field); }, create_form_text: function(target, field_id, field_name, field_placeholder, field_class, value, required){ field = document.createElement('input'); field.type = 'text'; field.id = field_id; field.name = field_name; field.value = value; field.placeholder = field_placeholder; field.className = required ? field_class + ' required-field' : field_class; target.appendChild(field); }, create_form_area: function(target, field_id, field_name, field_placeholder, field_class, required){ field = document.createElement('textarea'); field.cols = 55; field.rows = 10; field.id = field_id; field.name = field_name; field.placeholder = field_placeholder; field.className = required ? field_class + ' required-field' : field_class; target.appendChild(field); }, create_form_submit: function(target, label){ field = document.createElement('input'); field.id = 'form-submit-btn'; field.type = 'submit'; field.name = 'submit'; field.className = 'btn'; field.value = label; field.title = this.translation('buttomLabel') || ''; if (RedmineHelpdeskWidget.configuration['color']) { field.style.background = RedmineHelpdeskWidget.configuration['color']; } target.appendChild(field); }, create_attch_link: function(target){ if (this.configuration['attachment'] != false ) { attach_div = document.createElement('div'); attach_div.className = 'attach_div'; attach_link = document.createElement('a'); attach_link.className = 'attach_link'; attach_link.href = 'javascript:void(0)'; attach_link.innerHTML = this.translation('attachmentLinkLabel') || 'Attach a file'; attach_div.appendChild(attach_link); attach_field = document.createElement('input'); attach_field.type = 'file'; attach_field.id = 'attachment'; attach_field.className = 'attach_field'; attach_field.name = 'attachment'; attach_field.attributes['data-max-size'] = <%= Setting[:attachment_max_size].to_i * 1024 %>; attach_field.addEventListener('change', function(){ RedmineHelpdeskWidget.upload_file() }); attach_div.appendChild(attach_field); this.attachment = attach_field; target.appendChild(attach_div); } }, upload_file: function(){ if (this.attachment.attributes['data-max-size'] > this.attachment.files[0].size) { this.read_file(this.attachment.files[0], function(e){ attach_field = RedmineHelpdeskWidget.form.getElementsByClassName('attach_field')[0] attach_field.attributes['data-value'] = e.target.result; displayed_name = (attach_field.files[0].name.length <= 20) ? attach_field.files[0].name : attach_field.files[0].name.substring(0, 20) + '...'; RedmineHelpdeskWidget.form.getElementsByClassName('attach_link')[0].innerHTML = displayed_name; }); } else { this.attachment.attributes['data-value'] = ''; RedmineHelpdeskWidget.form.getElementsByClassName('attach_link')[0].innerHTML = '<%= t(:label_helpdesk_widget_file_large) %>'; } }, read_file: function(file, callback){ var reader = new FileReader(); reader.onload = callback reader.readAsDataURL(file); }, load_custom_fields: function(target, project_id, tracker_id){ var xmlhttp = getXmlHttp(); var params = 'project_id=' + encodeURIComponent(project_id) + '&tracker_id=' + encodeURIComponent(tracker_id); custom_div = document.createElement('div'); xmlhttp.open('GET', this.base_url + '/helpdesk_widget/load_custom_fields?' + params, true); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4) { if (xmlhttp.status == 200 || xmlhttp.status == 304) { custom_div.innerHTML = xmlhttp.responseText; target.appendChild(custom_div); RedmineHelpdeskWidget.set_custom_values(); RedmineHelpdeskWidget.positionate_iframe(); } } }; xmlhttp.send(null); }, set_custom_values: function(){ if (this.configuration['identify'] && this.configuration['identify']['customFieldValues']){ for(var cf in this.configuration['identify']['customFieldValues']) { custom_field = this.form.querySelector('#issue_custom_field_values_' + this.schema.custom_fields[cf]) if (custom_field){ switch (custom_field.tagName){ case 'INPUT': custom_field.type = 'hidden'; custom_field.value = this.configuration['identify']['customFieldValues'][cf]; this.form.querySelector("[data-error-key='" + cf + "']").style.display = 'none'; break; case 'SELECT': options = custom_field.options; for(var option, index = 0; option = options[index]; index++) { if(option.value == this.configuration['identify']['customFieldValues'][cf]) { this.create_form_hidden(custom_field.parentElement, custom_field.id, custom_field.name, custom_field.classList.toString(), this.configuration['identify']['customFieldValues'][cf]); custom_field.remove(); this.form.querySelector("[data-error-key='" + cf + "']").style.display = 'none'; break; } } break; } } } } }, positionate_iframe: function(){ widget_height = this.form.offsetHeight > this.height ? this.height : this.form.offsetHeight; this.iframe.setAttribute('height', this.margin + widget_height); switch (this.configuration['position']) { case 'topLeft': this.iframe.style.top = (this.margin + this.widget_button.offsetWidth) + 'px'; iframe.style.left = (this.margin - this.widget_button.offsetWidth / 2) + 'px'; break; case 'topRight': this.iframe.style.top = (this.margin + this.widget_button.offsetWidth) + 'px'; iframe.style.left = (this.margin + this.widget_button.offsetWidth - this.width - 20) + 'px'; break; case 'bottomLeft': this.iframe.style.top = (- this.margin * 2 - widget_height) + 'px'; iframe.style.left = (this.margin - this.widget_button.offsetWidth / 2) + 'px'; break; case 'bottomRight': this.iframe.style.top = (- this.margin * 2 - widget_height) + 'px'; iframe.style.left = (this.margin + this.widget_button.offsetWidth - this.width - 20) + 'px'; break; default: this.iframe.style.top = (- this.margin * 2 - widget_height) + 'px'; iframe.style.left = (this.margin + this.widget_button.offsetWidth - this.width - 20) + 'px'; } }, create_message_listener: function(){ var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent"; var eventer = window[eventMethod]; var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message"; eventer(messageEvent,function(e) { data = JSON.parse(e.data); if (data['reload'] == true) { RedmineHelpdeskWidget.reload = true; } if (data['project_reload'] == true) { RedmineHelpdeskWidget.reload_project_data(); } },false); }, reload_form: function(){ this.iframe.remove(); this.create_iframe(); this.fill_form(); this.decorate_iframe(); this.reload = false; }, show: function() { this.iframe.style.visibility = 'visible'; this.iframe.style.opacity = '1'; this.positionate_iframe(); switch (this.configuration['position']) { case 'topLeft': case 'topRight': this.widget_button.style.borderRadius = '50% 50% 0%'; break; case 'bottomLeft': case 'bottomRight': this.widget_button.style.borderRadius = '0 100% 100%'; break; default: this.widget_button.style.borderRadius = '0 100% 100%'; } this.widget_button.style.webkitTransform = 'rotate(45deg)'; this.widget_button.style.mozTransform = 'rotate(45deg)'; this.widget_button.style.msTransform = 'rotate(45deg)'; this.widget_button.style.oTransform = 'rotate(45deg)'; }, hide: function() { if (this.reload == true) { this.reload_form(); } body = this.iframe.contentWindow.document.body; this.iframe.style.visibility = 'hidden'; this.iframe.style.opacity = '0'; this.widget_button.style.borderRadius = '30px'; this.widget_button.style.webkitTransform = ''; this.widget_button.style.mozTransform = ''; this.widget_button.style.msTransform = ''; this.widget_button.style.oTransform = ''; }, toggle: function() { (this.iframe.style.visibility == 'visible') ? this.hide() : this.show(); } } RedmineHelpdeskWidget.load();