function ytknsRequestJson(url, callback, method, data, headers, handleErr, upload) { if(!callback) callback = function(){}; var xhr = new XMLHttpRequest; if(xhr.upload && upload) { xhr.upload.onloadstart = function(ev) { upload('loadstart', ev); }; xhr.upload.onprogress = function(ev) { upload('progress', ev) }; xhr.upload.onload = function(ev) { upload('load', ev); }; } xhr.onreadystatechange = function() { if(xhr.readyState !== 4) return; var json = JSON.parse(xhr.responseText); if(json.err && !handleErr) { alert(json.err); callback([]); } else callback(json); }; xhr.open(method || 'GET', url); if(headers && headers.length > 0) for(var i = 0; i < headers.length; i++) xhr.setRequestHeader(headers[i].name, headers[i].value); xhr.send(data || null); } function ytknsEditorLoadEffects(callback) { ytknsRequestJson('/zones/_effects', callback); } function ytknsLoadZoneInfo(zoneId, callback) { ytknsRequestJson('/zones/' + parseInt(zoneId).toString() + '.json', callback); } function ytknsLoadUploadInfo(uploadId, callback) { ytknsRequestJson('/uploads/' + uploadId.toString() + '.json', callback); } function ytknsCreateUrlString(set) { var parts = []; for(var i = 0; i < set.length; i++) parts.push(encodeURIComponent(set[i].name) + '=' + encodeURIComponent(set[i].value)); return parts.join('&'); } function ytknsCreateUrlStringPart(name, value) { return { 'name': name.toString(), 'value': ( (typeof value).toLowerCase() === 'object' ? JSON.stringify(value) : value.toString() ) }; } function ytknsZoneInfoToUrlString(zoneInfo) { return ytknsCreateUrlString(ytknsZoneInfoSerialise(zoneInfo, ytknsCreateUrlStringPart)); } function ytknsZoneInfoSerialise(zoneInfo, callback) { var set = []; if(zoneInfo) { set.push(callback('zone_token', ytknsEditorToken || '')); set.push(callback('zone_id', zoneInfo.id)); set.push(callback('zone_name', zoneInfo.name)); set.push(callback('zone_title', zoneInfo.title)); if(zoneInfo.effects && zoneInfo.effects.length > 0) { for(var i = 0; i < zoneInfo.effects.length; i++) { var format = 'zone_effect[%1][%2]'.replace('%1', zoneInfo.effects[i].type), keys = Object.keys(zoneInfo.effects[i].values); if(keys.length < 1) { set.push(callback(format.replace('%2', '_'), '_')); } else for(var j = 0; j < keys.length; j++) { var key = format.replace('%2', keys[j]); set.push(callback(key, zoneInfo.effects[i].values[keys[j]] || '')); } } } } return set; } function ytknsSaveZoneInfo(zoneInfo, callback) { if(!zoneInfo || !zoneInfo.id) return; ytknsRequestJson( '/zones/' + parseInt(zoneInfo.id).toString() + '.json', callback, 'POST', ytknsZoneInfoToUrlString(zoneInfo), [{ name: 'Content-Type', value: 'application/x-www-form-urlencoded' }] ); } function ytknsEditorPreview(zoneInfo) { var formElement = document.createElement('form'); formElement.action = '/zones/_preview'; formElement.method = 'post'; formElement.target = '_blank'; formElement.style.display = 'none'; var elements = ytknsZoneInfoSerialise(zoneInfo, function(name, value) { var inputElement = document.createElement('input'); inputElement.name = name; inputElement.type = 'hidden'; inputElement.value = ( (typeof value).toLowerCase() === 'object' ? JSON.stringify(value) : value.toString() ); formElement.appendChild(inputElement); }); document.body.appendChild(formElement); formElement.submit(); formElement.parentNode.removeChild(formElement); } function ytknsEditorCreateUpload(file, callback, status) { var formData = new FormData; formData.append('upload_token', ytknsEditorUploadToken || ''); formData.append('upload_file', file); ytknsRequestJson( '/uploads', callback, 'POST', formData, null, true, status ); } var ytknsZoneInfo = null, ytknsEditorToken = '', ytknsEditorUploadToken = '', ytknsEditorElemSidebar = null, ytknsEditorElemSidebarButtons = null, ytknsEditorElemSidebarEffects = null, ytknsEditorElemMain = null, ytknsEditorElemMainTitle = null, ytknsEditorElemMainContainer = null, ytknsEditorEffects = [], ytknsEditorIgnoreHashChange = false, ytknsEditorCleanExit = true; function ytknsEditorChangeHash(hash) { ytknsEditorIgnoreHashChange = true; location.hash = hash; } function ytknsEditorBeforeUnload(ev) { if(!ytknsEditorCleanExit) { ev.preventDefault(); ev.returnValue = ''; } } function ytknsGetEffectInfoByType(type) { for(var i = 0; i < ytknsEditorEffects.length; i++) if(ytknsEditorEffects[i].type === type) return ytknsEditorEffects[i]; return null; } function ytknsGetEffectValueByType(type) { if(ytknsZoneInfo === null || ytknsZoneInfo.effects === null || ytknsZoneInfo.effects.length < 1) return null; for(var i = 0; i < ytknsZoneInfo.effects.length; i++) if(ytknsZoneInfo.effects[i].type === type) return ytknsZoneInfo.effects[i]; return null; } function ytknsRemoveEffectValueByType(type) { var info = ytknsGetEffectValueByType(type); if(!info) { alert('Tried to remove an effect that doesn\'t exist.'); return; } var index = ytknsZoneInfo.effects.indexOf(info); if(index >= 0) ytknsZoneInfo.effects.splice(index, 1); ytknsEditorUpdateSidebarEffects(); } function ytknsEditorFilePickerDoUpload(files, callback, status) { if(files.length < 1) { alert('No file selected.'); return; } var file = files[0]; ytknsEditorCreateUpload(file, function(resp) { if(resp.file) { ytknsLoadUploadInfo(resp.file, function(uploadInfo) { callback(uploadInfo); }); } else if(resp.err) { alert(resp.err); if(status) status(-1, -1); } }, function(type, ev) { if(status) status(ev.loaded, ev.total); }); } function ytknsEditorShowFilePicker(mimes, title, callback) { var container = document.createElement('div'); container.classList.add('ye-applet-uploads'); var progressBarContainer = document.createElement('div'); progressBarContainer.classList.add('ye-applet-uploads-progress'); progressBarContainer.classList.add('ye-applet-uploads-hidden'); container.appendChild(progressBarContainer); var progressBar = document.createElement('progress'); progressBar.min = 0; progressBar.max = 100; progressBar.value = 0; progressBar.classList.add('ye-applet-uploads-progress-bar'); progressBarContainer.appendChild(progressBar); var dropZone = document.createElement('div'), dropZoneInner = document.createElement('div'); dropZone.classList.add('ye-applet-uploads-dropzone'); dropZoneInner.classList.add('ye-applet-uploads-dropzone-inner'); dropZoneInner.textContent = 'Drop a file or click here!'; dropZone.appendChild(dropZoneInner); container.appendChild(dropZone); var cancel = document.createElement('input'); cancel.classList.add('ye-applet-uploads-cancel'); cancel.type = 'button'; cancel.value = 'Cancel'; cancel.onclick = function() { callback(null); }; container.appendChild(cancel); var doUpload = function(files) { dropZone.classList.add('ye-applet-uploads-hidden'); cancel.classList.add('ye-applet-uploads-hidden'); progressBarContainer.classList.remove('ye-applet-uploads-hidden'); ytknsEditorFilePickerDoUpload(files, function(resp) { dropZone.classList.remove('ye-applet-uploads-hidden'); cancel.classList.remove('ye-applet-uploads-hidden'); progressBarContainer.classList.add('ye-applet-uploads-hidden'); callback(resp); }, function(loaded, total) { if(loaded < 0 && total < 0) { dropZone.classList.remove('ye-applet-uploads-hidden'); cancel.classList.remove('ye-applet-uploads-hidden'); progressBarContainer.classList.add('ye-applet-uploads-hidden'); return; } progressBar.value = Math.ceil((loaded / total) * 100); }); }; dropZone.onclick = function() { var selector = document.createElement('input'); selector.type = 'file'; selector.onchange = function() { doUpload(selector.files); }; if(mimes) selector.accept = mimes.join(','); selector.click(); }; dropZone.ondragenter = function(ev) { ev.preventDefault(); ev.stopPropagation(); dropZone.classList.add('ye-applet-uploads-dropzone--active'); }; dropZone.ondragleave = function(ev) { ev.preventDefault(); ev.stopPropagation(); dropZone.classList.remove('ye-applet-uploads-dropzone--active'); }; dropZone.ondragover = function(ev) { ev.preventDefault(); ev.stopPropagation(); dropZone.classList.add('ye-applet-uploads-dropzone--active'); }; dropZone.ondrop = function(ev) { ev.preventDefault(); ev.stopPropagation(); dropZone.classList.remove('ye-applet-uploads-dropzone--active'); doUpload(ev.dataTransfer.files); }; ytknsEditorMainSetContainer(container, title || 'Upload File'); } function ytknsLoadEffectEditor(effectInfo, effectValue) { ytknsEditorChangeHash(effectInfo.type); var editor = document.createElement('div'); editor.classList.add('ye-applet-editor'); if(effectInfo.props.length < 1) { var empty = document.createElement('div'); editor.classList.add('ye-applet-editor--fill'); empty.classList.add('ye-applet-editor-empty'); empty.textContent = 'This effect has no properties.'; editor.appendChild(empty); } else { var properties = document.createElement('div'); properties.classList.add('ye-applet-editor-properties'); editor.appendChild(properties); for(var i = 0; i < effectInfo.props.length; i++) { ytknsAddEffectEditorProperty( properties, effectInfo, effectValue, effectInfo.props[i], function() { ytknsLoadEffectEditor(effectInfo, effectValue); } // there's gotta be a better way ); } } ytknsEditorMainSetContainer(editor, effectInfo.name); } function ytknsAddEffectEditorProperty(listContainer, effectInfo, effectValue, propInfo, redraw) { var container = document.createElement('div'); container.classList.add('ye-applet-editor-properties-property'); listContainer.appendChild(container); var propTitle = document.createElement('div'); propTitle.classList.add('ye-applet-editor-properties-property-title'); propTitle.textContent = propInfo.title; container.appendChild(propTitle); var propWrap = document.createElement('div'); propWrap.classList.add('ye-applet-editor-properties-property-wrap'); container.appendChild(propWrap); switch(propInfo.type.name) { case 'upload': var propUpload = document.createElement('div'); propUpload.classList.add('ye-applet-editor-properties-property-upload'); propWrap.appendChild(propUpload); var propUploadId = document.createElement('div'); propUploadId.classList.add('ye-applet-editor-properties-property-upload-id'); propUploadId.textContent = effectValue.values[propInfo.name] || '(none)'; propUpload.appendChild(propUploadId); var propUploadSelect = document.createElement('div'); propUploadSelect.classList.add('ye-applet-editor-properties-property-upload-select'); propUploadSelect.title = 'Upload file'; propUploadSelect.onclick = function() { ytknsEditorShowFilePicker(propInfo.type.allowed || [], ('Selecting ' + propInfo.title + ' for ' + effectInfo.name), function(uploadInfo) { if(uploadInfo) effectValue.values[propInfo.name] = uploadInfo.id || null; redraw(); }); }; propUpload.appendChild(propUploadSelect); break; case 'bool': var propBool = document.createElement('label'); propBool.classList.add('ye-applet-editor-properties-property-bool'); propWrap.appendChild(propBool); var propBoolToggle = document.createElement('input'); propBoolToggle.classList.add('ye-applet-editor-properties-property-bool-toggle'); propBoolToggle.type = 'checkbox'; propBoolToggle.checked = (typeof effectValue.values[propInfo.name]).toLowerCase() === 'undefined' ? (propInfo.default || false) : effectValue.values[propInfo.name]; propBoolToggle.onchange = function() { effectValue.values[propInfo.name] = propBoolToggle.checked; redraw(); }; propBool.appendChild(propBoolToggle); break; case 'int': case 'float': var propInt = document.createElement('label'); propInt.classList.add('ye-applet-editor-properties-property-int'); propWrap.appendChild(propInt); var propIntInput = document.createElement('input'); propIntInput.classList.add('ye-applet-editor-properties-property-int-input'); propIntInput.type = 'number'; if(propInfo.min) propIntInput.min = propInfo.min; if(propInfo.max) propIntInput.max = propInfo.max; if(propInfo.type.name === 'float') propIntInput.step = 0.01; propIntInput.value = effectValue.values[propInfo.name] || propInfo.default || 0; propIntInput.onchange = function() { effectValue.values[propInfo.name] = propInfo.type.name === 'int' ? parseInt(propIntInput.value) : parseFloat(propIntInput.value); redraw(); }; propInt.appendChild(propIntInput); break; case 'select': var propSelect = document.createElement('label'); propSelect.classList.add('ye-applet-editor-properties-property-select'); propWrap.appendChild(propSelect); var propSelectInput = document.createElement('select'); propSelectInput.classList.add('ye-applet-editor-properties-property-select-input'); propSelectInput.onchange = function() { effectValue.values[propInfo.name] = propSelectInput.value; redraw(); }; propSelect.appendChild(propSelectInput); var propSelectOptionsKeys = Object.keys(propInfo.type.options), propSelectOptionsValues = Object.values(propInfo.type.options); for(var i = 0; i < propSelectOptionsKeys.length; i++) { var propSelectOption = document.createElement('option'); propSelectOption.value = propSelectOptionsKeys[i]; propSelectOption.textContent = propSelectOptionsValues[i]; propSelectOption.selected = (effectValue.values[propInfo.name] || propInfo.default) === propSelectOptionsKeys[i]; propSelectInput.appendChild(propSelectOption); } break; case 'colour': var propColour = document.createElement('label'); propColour.classList.add('ye-applet-editor-properties-property-colour'); propWrap.appendChild(propColour); var propColourInput = document.createElement('input'); propColourInput.type = 'color'; propColourInput.value = '#' + (effectValue.values[propInfo.name] || 0).toString(16).padStart(6, '0'); propColourInput.classList.add('ye-applet-editor-properties-property-colour-input'); propColourInput.onchange = function() { effectValue.values[propInfo.name] = parseInt(propColourInput.value.substring(1), 16); redraw(); }; propColour.appendChild(propColourInput); break; case 'string': var propString = document.createElement('label'); propString.classList.add('ye-applet-editor-properties-property-string'); propWrap.appendChild(propString); var propStringInput = document.createElement('input'); propStringInput.type = 'text'; if(propInfo.type.min) propStringInput.minLength = propInfo.type.min; if(propInfo.type.max) propStringInput.maxLength = propInfo.type.max; propStringInput.value = effectValue.values[propInfo.name] || ''; propStringInput.classList.add('ye-applet-editor-properties-property-string-input'); propStringInput.onchange = function() { effectValue.values[propInfo.name] = propStringInput.value; redraw(); }; propString.appendChild(propStringInput); break; case 'gradient': if(!effectValue.values[propInfo.name]) effectValue.values[propInfo.name] = {}; var propGrad = document.createElement('div'); propGrad.classList.add('ye-applet-editor-properties-property-gradient'); propWrap.appendChild(propGrad); var propGradPreview = document.createElement('div'); propGradPreview.classList.add('ye-applet-editor-properties-property-gradient-preview'); propGrad.appendChild(propGradPreview); var propGradDirection = document.createElement('div'); propGradDirection.classList.add('ye-applet-editor-properties-property-gradient-direction'); propGrad.appendChild(propGradDirection); var propGradDirectionCircle = document.createElement('div'); propGradDirectionCircle.classList.add('ye-applet-editor-properties-property-gradient-direction-circle'); propGradDirection.appendChild(propGradDirectionCircle); var propGradDirectionCircleValue = document.createElement('div'); propGradDirectionCircleValue.classList.add('ye-applet-editor-properties-property-gradient-direction-circle-value'); propGradDirectionCircle.appendChild(propGradDirectionCircleValue); var propGradDirectionCircleIndicator = document.createElement('div'); propGradDirectionCircleIndicator.classList.add('ye-applet-editor-properties-property-gradient-direction-circle-indicator'); propGradDirectionCircleValue.appendChild(propGradDirectionCircleIndicator); var propGradDirectionValue = document.createElement('input'); propGradDirectionValue.classList.add('ye-applet-editor-properties-property-gradient-direction-input'); propGradDirectionValue.type = 'number'; propGradDirectionValue.min = 0; propGradDirectionValue.max = 359; propGradDirectionValue.value = 0; propGradDirection.appendChild(propGradDirectionValue); var propGradRedrawPreview = function() { propGradPreview.style.backgroundImage = ytknsEditorGradientCSS(effectValue.values[propInfo.name]); }; var propGradDirectionSet = function(val) { val = parseInt(val); effectValue.values[propInfo.name].d = val; if(propGradDirectionValue.value != val) propGradDirectionValue.value = val; propGradDirectionCircleValue.style.transform = 'rotate(' + val + 'deg)'; propGradRedrawPreview(); }; propGradDirectionSet(effectValue.values[propInfo.name].d || 0); propGradDirectionValue.onchange = function() { propGradDirectionSet(propGradDirectionValue.value); }; var propGradDirectionCircleMouseEvent = function(ev) { if((ev.buttons & 1) < 1) return; var rect = ev.target.getBoundingClientRect(), y = ev.layerY - (rect.height / 2), x = ev.layerX - (rect.width / 2), deg = Math.atan2(x, y) * 180 / Math.PI, val = 180 - parseInt(deg); propGradDirectionSet(val); }; propGradDirectionCircle.onmousedown = propGradDirectionCircleMouseEvent; propGradDirectionCircle.onmousemove = propGradDirectionCircleMouseEvent; var propGradPoints = document.createElement('div'); propGradPoints.classList.add('ye-applet-editor-properties-property-gradient-points'); propGrad.appendChild(propGradPoints); var propGradPointAdd = function(point) { if(!point) point = {}; if((typeof point.c).toLowerCase() === 'undefined') point.c = parseInt(0xFFFFFF * Math.random()); if((typeof point.o).toLowerCase() === 'undefined') point.o = parseInt(100 * Math.random()); var propGradPoint = document.createElement('div'); propGradPoint.classList.add('ye-applet-editor-properties-property-gradient-points-point'); propGradPoints.appendChild(propGradPoint); var propGradPointActions = document.createElement('div'); propGradPointActions.classList.add('ye-applet-editor-properties-property-gradient-points-point-actions'); propGradPoint.appendChild(propGradPointActions); var propGradPointActionsAdd = function(name, title, action) { var propGradPointAction = document.createElement('div'); propGradPointAction.classList.add('ye-applet-editor-properties-property-gradient-points-point-actions-action'); propGradPointAction.classList.add('ye-applet-editor-properties-property-gradient-points-point-actions-action--' + name); propGradPointAction.title = title; propGradPointAction.onclick = function() { if(action) action(); }; propGradPointActions.appendChild(propGradPointAction); }; propGradPointActionsAdd('delete', 'Delete', function() { var index = effectValue.values[propInfo.name].p.indexOf(point); if(index < 0) return; effectValue.values[propInfo.name].p.splice(index, 1); propGradPoints.removeChild(propGradPoint); propGradRedrawPreview(); }); var propGradPointColour = document.createElement('label'); propGradPointColour.classList.add('ye-applet-editor-properties-property-gradient-points-point-colour'); propGradPoint.appendChild(propGradPointColour); var propGradPointColourValue = document.createElement('input'); propGradPointColourValue.type = 'color'; propGradPointColourValue.value = '#' + point.c.toString(16).padStart(6, '0'); propGradPointColourValue.classList.add('ye-applet-editor-properties-property-gradient-points-point-colour-value'); propGradPointColourValue.onchange = function() { point.c = parseInt(propGradPointColourValue.value.substring(1), 16); propGradPointColour.style.backgroundColor = propGradPointColourValue.value; propGradRedrawPreview(); }; propGradPointColour.appendChild(propGradPointColourValue); propGradPointColour.style.backgroundColor = propGradPointColourValue.value; var propGradPointOffset = document.createElement('input'); propGradPointOffset.type = 'range'; propGradPointOffset.classList.add('ye-applet-editor-properties-property-gradient-points-point-offset-range'); propGradPointOffset.value = 0; propGradPointOffset.min = 0; propGradPointOffset.max = 100; propGradPoint.appendChild(propGradPointOffset); var propGradPointOffsetNum = document.createElement('input'); propGradPointOffsetNum.type = 'number'; propGradPointOffsetNum.classList.add('ye-applet-editor-properties-property-gradient-points-point-offset-numeric'); propGradPointOffsetNum.value = 0; propGradPointOffsetNum.min = 0; propGradPointOffsetNum.max = 100; propGradPoint.appendChild(propGradPointOffsetNum); var propGradPointOffsetIsSetting = false, propGradPointOffsetSet = function(val) { if(propGradPointOffsetIsSetting) return; propGradPointOffsetIsSetting = true; val = parseInt(val); propGradPointOffset.value = propGradPointOffsetNum.value = point.o = val; propGradRedrawPreview(); propGradPointOffsetIsSetting = false; }; propGradPointOffsetSet(point.o); propGradPointOffset.onchange = function() { propGradPointOffsetSet(propGradPointOffset.value); }; propGradPointOffsetNum.onchange = function() { propGradPointOffsetSet(propGradPointOffsetNum.value); }; }; var propGradPointAddButton = document.createElement('button'); propGradPointAddButton.classList.add('ye-applet-editor-properties-property-gradient-points-add'); propGradPointAddButton.onclick = function() { var point = {}; if(!effectValue.values[propInfo.name].p) effectValue.values[propInfo.name].p = []; effectValue.values[propInfo.name].p.push(point); propGradPointAdd(point); propGradRedrawPreview(); }; propGradDirection.appendChild(propGradPointAddButton); var propGradPointAddButtonIcon = document.createElement('div'); propGradPointAddButtonIcon.classList.add('ye-applet-editor-properties-property-gradient-points-add-icon'); propGradPointAddButton.appendChild(propGradPointAddButtonIcon); var propGradPointAddButtonText = document.createElement('div'); propGradPointAddButtonText.classList.add('ye-applet-editor-properties-property-gradient-points-add-text'); propGradPointAddButtonText.textContent = 'Add gradient point'; propGradPointAddButton.appendChild(propGradPointAddButtonText); if(effectValue.values[propInfo.name].p) for(var i = 0; i < effectValue.values[propInfo.name].p.length; i++) propGradPointAdd(effectValue.values[propInfo.name].p[i]); break; default: var propNone = document.createElement('div'); propNone.classList.add('ye-applet-editor-properties-property-none'); propNone.textContent = 'There is no handler available for this property type.'; propWrap.appendChild(propNone); break; } var propDefault = document.createElement('div'); propDefault.classList.add('ye-applet-editor-properties-property-reset'); propDefault.title = 'Reset'; propDefault.onclick = function() { effectValue.values[propInfo.name] = propInfo.default; redraw(); }; propWrap.appendChild(propDefault); } function ytknsEditorGradientCSS(obj) { var str = 'linear-gradient(' + (obj.d || 0).toString() + 'deg, ', points = []; if(obj.p) for(var i = 0; i < obj.p.length; i++) points.push('#' + (obj.p[i].c || 0).toString(16).padStart(6, '0') + ' ' + (obj.p[i].o || 0).toString() + '%'); return str + points.join(', ') + ')'; } function ytknsEditorAddSidebarButton(className, title, callback) { var button = document.createElement('div'); button.classList.add('ye-sidebar-buttons-button'); button.classList.add('ye-sidebar-buttons-button--' + className); button.title = title; button.onclick = callback; ytknsEditorElemSidebarButtons.appendChild(button); } function ytknsEditorAddSidebarButtonSeparator() { var separator = document.createElement('div'); separator.classList.add('ye-sidebar-buttons-separator'); ytknsEditorElemSidebarButtons.appendChild(separator); } function ytknsEditorAddSidebarEffect(effectValue) { var effectInfo = ytknsGetEffectInfoByType(effectValue.type); if(!effectInfo) { alert('Attempted to add unregistered effect.'); return; } var effect = document.createElement('div'); effect.classList.add('ye-sidebar-effects-effect'); ytknsEditorElemSidebarEffects.appendChild(effect); var effectName = document.createElement('div'); effectName.classList.add('ye-sidebar-effects-effect-name'); effectName.textContent = effectInfo.name; effect.appendChild(effectName); var effectActions = document.createElement('div'); effectActions.classList.add('ye-sidebar-effects-effect-actions'); effect.appendChild(effectActions); ytknsEditorAddSidebarEffectAction(effectActions, 'edit', 'Edit Effect', function() { ytknsLoadEffectEditor(effectInfo, effectValue); }); ytknsEditorAddSidebarEffectAction(effectActions, 'delete', 'Delete Effect', function() { var previous = ytknsEditorMainClone(); ytknsEditorMainShowEffectDeleteConfirm(effectInfo, function(accept) { if(accept) { ytknsRemoveEffectValueByType(effectValue.type); ytknsEditorMainShowWelcome(); } else { ytknsEditorMainRestore(previous); } }); }); } function ytknsEditorMainShowEffectDeleteConfirm(effectInfo, callback) { var confirmation = document.createElement('div'), text = confirmation.appendChild(document.createElement('div')), actions = confirmation.appendChild(document.createElement('div')); confirmation.classList.add('ye-main-effect-delete-confirm'); text.classList.add('ye-main-effect-delete-confirm-text'); text.textContent = 'Are you sure you want to delete "' + effectInfo.name + '"?'; actions.classList.add('ye-main-effect-delete-confirm-actions'); var accept = document.createElement('div'); accept.classList.add('ye-main-effect-delete-confirm-actions-action'); accept.classList.add('ye-main-effect-delete-confirm-actions-action--accept'); accept.textContent = 'Yes'; accept.onclick = function() { callback(true); }; actions.appendChild(accept); var deny = document.createElement('div'); deny.classList.add('ye-main-effect-delete-confirm-actions-action'); deny.classList.add('ye-main-effect-delete-confirm-actions-action--deny'); deny.textContent = 'No'; deny.onclick = function() { callback(false); }; actions.appendChild(deny); ytknsEditorMainSetContainer(confirmation, 'Delete effect'); } function ytknsEditorAddSidebarEffectAction(container, className, title, callback) { var action = document.createElement('div'); action.classList.add('ye-sidebar-effects-effect-actions-action'); action.classList.add('ye-sidebar-effects-effect-actions-action--' + className); action.title = title; action.onclick = callback; container.appendChild(action); } function ytknsEditorUpdateSidebarEffects() { ytknsEditorElemSidebarEffects.innerHTML = ''; if(ytknsZoneInfo == null || ytknsZoneInfo.effects == null || ytknsZoneInfo.effects.length < 1) { var nothing = document.createElement('div'); nothing.classList.add('ye-sidebar-effects-empty'); nothing.textContent = 'You have not added any effects yet.'; ytknsEditorElemSidebarEffects.appendChild(nothing); } else { for(var i = 0; i < ytknsZoneInfo.effects.length; i++) ytknsEditorAddSidebarEffect(ytknsZoneInfo.effects[i]); } } function ytknsShowDetailsEdit() { ytknsEditorChangeHash('details'); var container = document.createElement('div'); container.classList.add('ye-main-details'); var fieldsElem = document.createElement('div'); fieldsElem.classList.add('ye-main-details-fields'); container.appendChild(fieldsElem); var fields = [ { name: 'Zone ID', value: ytknsZoneInfo.id }, { name: 'Subdomain', value: ytknsZoneInfo.name, suffix: '.' + location.host }, { name: 'Title', value: ytknsZoneInfo.title, max: 255, edit: function(val) { ytknsZoneInfo.title = val; } }, ]; for(var i = 0; i < fields.length; i++) { var field = fields[i], fieldElem = document.createElement('div'); fieldElem.classList.add('ye-main-details-fields-field'); var fieldName = document.createElement('div'); fieldName.classList.add('ye-main-details-fields-field-name'); fieldName.textContent = field.name + ':'; fieldElem.appendChild(fieldName); var fieldWrap = document.createElement('div'); fieldWrap.classList.add('ye-main-details-fields-field-wrap'); fieldElem.appendChild(fieldWrap); var fieldInput = document.createElement('input'); fieldInput.classList.add('ye-main-details-fields-field-input'); fieldInput.type = 'text'; fieldInput.value = field.value; if(field.max) fieldInput.maxLength = field.max; if(field.edit) { var onedit = field.edit; fieldInput.onchange = function(ev) { onedit(ev.target.value); }; } else { fieldInput.readOnly = true; fieldElem.classList.add('ye-main-details-fields-field--readonly'); } fieldWrap.appendChild(fieldInput); if(field.suffix) fieldWrap.appendChild(document.createTextNode(field.suffix)); fieldsElem.appendChild(fieldElem); } ytknsEditorMainSetContainer(container, 'Zone information'); } function ytknsEditorMainGetTitle() { return ytknsEditorElemMainTitle.textContent; } function ytknsEditorMainSetTitle(title) { ytknsEditorElemMainTitle.textContent = title; } function ytknsEditorMainGetContainer() { return ytknsEditorElemMainContainer.firstChild; } function ytknsEditorMainSetContainer(child, title) { if(title) ytknsEditorMainSetTitle(title); ytknsEditorElemMainContainer.innerHTML = ''; ytknsEditorElemMainContainer.appendChild(child); } function ytknsEditorMainClone() { return { 'title': ytknsEditorMainGetTitle(), 'content': ytknsEditorMainGetContainer().cloneNode(true), }; } function ytknsEditorMainRestore(clone) { ytknsEditorMainSetContainer(clone.content, clone.title); } function ytknsEditorMainShowWelcome() { var welcome = document.createElement('div'); welcome.classList.add('ye-main-welcome'); var welcomeH1 = document.createElement('h1'); welcomeH1.classList.add('ye-main-welcome-h1'); welcomeH1.textContent = 'Welcome!'; welcome.appendChild(welcomeH1); var welcomeP = document.createElement('p'); welcomeP.classList.add('ye-main-welcome-p'); welcomeP.textContent = 'Select a tool in the sidebar to get started.'; welcome.appendChild(welcomeP); ytknsEditorMainSetContainer(welcome, 'Welcome to the YTKNS Editor'); } function ytknsEditorMainShowEffectList() { ytknsEditorChangeHash('add'); var container = document.createElement('div'); container.classList.add('ye-applet-effects'); var effects = document.createElement('div'); effects.classList.add('ye-applet-effects-list'); container.appendChild(effects); for(var i = 0; i < ytknsEditorEffects.length; i++) { var effectInfo = ytknsEditorEffects[i], effectValue = ytknsGetEffectValueByType(effectInfo.type), effectElement = document.createElement('div'), effectUsed = effectValue !== null; effectElement.classList.add('ye-applet-effects-list-item'); if(effectUsed) effectElement.classList.add('ye-applet-effects-list-item--used'); var effectName = document.createElement('div'); effectName.classList.add('ye-applet-effects-list-item-name'); effectName.textContent = effectInfo.name; effectElement.appendChild(effectName); var effectActions = document.createElement('div'); effectActions.classList.add('ye-applet-effects-list-item-actions'); effectElement.appendChild(effectActions); if(!effectUsed) { var effectActionAdd = document.createElement('div'); effectActionAdd.classList.add('ye-applet-effects-list-item-actions-action'); effectActionAdd.classList.add('ye-applet-effects-list-item-actions-action--add'); effectActionAdd.title = 'Add Effect'; (function() { // javascript is very cool and good var effectInfoCopy = effectInfo; effectActionAdd.onclick = function() { var effectValueNew = ytknsEditorAddNewEffect(effectInfoCopy); ytknsEditorUpdateSidebarEffects(); ytknsLoadEffectEditor(effectInfoCopy, effectValueNew); }; })(); effectActions.appendChild(effectActionAdd); } effects.appendChild(effectElement); } ytknsEditorMainSetContainer(container, 'Available effects'); } function ytknsEditorAddNewEffect(effectInfo) { var effectValue = { type: effectInfo.type, values: {}, }; for(var i = 0; i < effectInfo.props.length; i++) { var propInfo = effectInfo.props[i]; effectValue.values[propInfo.name] = propInfo.default || ''; } ytknsZoneInfo.effects.push(effectValue); return effectValue; } function ytknsEditorConfirmReloadZoneInfo(zoneId) { if(confirm('Are you sure you want to undo any changes you\'ve made?')) ytknsEditorReloadZoneInfo(zoneId); } function ytknsEditorReloadZoneInfo(zoneId, onComplete) { ytknsEditorChangeHash(''); ytknsEditorMainShowWelcome(); ytknsEditorLoadEffects(function(effects) { ytknsEditorEffects = effects; ytknsLoadZoneInfo(zoneId, function(zoneInfo) { ytknsZoneInfo = zoneInfo; ytknsEditorUpdateSidebarEffects(); if(onComplete) onComplete(); }); }); } function ytknsEditorSwitchString(str) { switch(str) { case '': ytknsEditorMainShowWelcome(); break; case 'add': ytknsEditorMainShowEffectList(); break; case 'details': ytknsShowDetailsEdit(); break; default: var openEffectInfo = ytknsGetEffectInfoByType(str); if(openEffectInfo) { var openEffectValue = ytknsGetEffectValueByType(str); ytknsLoadEffectEditor(openEffectInfo, openEffectValue); } break; } } function ytknsEditorHashChange(ev) { if(!ytknsEditorIgnoreHashChange) ytknsEditorSwitchString(location.hash.substring(1)); ytknsEditorIgnoreHashChange = false; } function ytknsEditorMain(container, zoneId, editorToken, uploadToken) { if(navigator.userAgent.match(/mobile/gi) && !confirm("The editor is not designed to be used on phones whatsoever.\r\nHit OK to continue anyway or cancel to whereever you came from.")) { history.go(-1); return; } window.onhashchange = ytknsEditorHashChange; window.onbeforeunload = ytknsEditorBeforeUnload; ytknsEditorToken = editorToken; ytknsEditorUploadToken = uploadToken; container.innerHTML = ''; container.classList.add('ye'); ytknsEditorElemSidebar = container.appendChild(document.createElement('div')); ytknsEditorElemSidebar.classList.add('ye-sidebar'); ytknsEditorElemSidebarButtons = ytknsEditorElemSidebar.appendChild(document.createElement('div')); ytknsEditorElemSidebarButtons.classList.add('ye-sidebar-buttons'); ytknsEditorAddSidebarButton('save', 'Save', function() { ytknsSaveZoneInfo(ytknsZoneInfo, function(res) { if(res.msg) alert(res.msg); }); }); ytknsEditorAddSidebarButton('cancel', 'Cancel', function() { location.assign('/zones?f=my'); }); ytknsEditorAddSidebarButton('reset', 'Undo Changes', function() { ytknsEditorConfirmReloadZoneInfo(zoneId); }); ytknsEditorAddSidebarButton('edit', 'Edit Details', function() { ytknsShowDetailsEdit(); }); ytknsEditorAddSidebarButtonSeparator(); ytknsEditorAddSidebarButton('preview', 'Show Preview', function() { ytknsEditorPreview(ytknsZoneInfo); }); ytknsEditorAddSidebarButton('live', 'View Live', function() { window.open('//%1.%2'.replace('%2', location.host).replace('%1', ytknsZoneInfo.name)); }); ytknsEditorAddSidebarButtonSeparator(); ytknsEditorAddSidebarButton('add', 'Add Effect', function() { ytknsEditorMainShowEffectList(); }); ytknsEditorElemSidebarEffects = ytknsEditorElemSidebar.appendChild(document.createElement('div')); ytknsEditorElemSidebarEffects.classList.add('ye-sidebar-effects'); ytknsEditorUpdateSidebarEffects(); ytknsEditorElemMain = container.appendChild(document.createElement('div')); ytknsEditorElemMain.classList.add('ye-main'); ytknsEditorElemMainTitle = ytknsEditorElemMain.appendChild(document.createElement('div')); ytknsEditorElemMainTitle.classList.add('ye-main-title'); ytknsEditorElemMainContainer = ytknsEditorElemMain.appendChild(document.createElement('div')); ytknsEditorElemMainContainer.classList.add('ye-main-container'); var goToEditor = location.hash.substring(1); ytknsEditorReloadZoneInfo(zoneId, function() { ytknsEditorSwitchString(goToEditor); }); }