| <!DOCTYPE html> |
| <!-- |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| --> |
| <div id="bodydiv" class="body"> |
| |
| <div id="viewform" class="viewform"> |
| |
| <form id="appForm"> |
| <table style="width: 100%;"> |
| <tr><td class="label">URL:</td></tr> |
| <tr><td><input type="text" id="appURL" class="readentry" size="30" readonly="readonly" placeholder="App URL" style="width: 300px;"/></td></tr> |
| <tr><td class="label">Icon:</td></tr> |
| <tr><td><img id="appIcon" style="width: 50px; height: 50px; vertical-align: top;"/><input id="uploadIcon" type="button" class="lightbutton" value="Upload" style="display: none;"/><input id="uploadFile" type="file" accept="image/*" style="visibility: hidden;"/><span id="refreshingIcon" class="refreshing" style="display:none;"/></td></tr> |
| <tr><td class="label">Author:</td></tr> |
| <tr><td><img id="authorPicture" style="width: 50px; height: 50px; vertical-align: middle;"/><input type="text" id="appAuthor" class="readentry" size="30" readonly="readonly" placeholder="Author of the app" style="width: 248px;"/></td></tr> |
| <tr><td class="label">Rating:</td></tr> |
| <tr><td><span id="appRating" class="ratings"> </span><input id="rateApp" type="button" class="lightbutton" value="Rate this app"/></td></tr> |
| <tr><td><input type="text" id="appRatings" class="readentry" size="20" readonly="readonly" placeholder="Number of ratings" style="font-size: 12px;"/></td></tr> |
| <tr><td class="label">Updated:</td></tr> |
| <tr><td><input type="text" id="appUpdated" class="readentry" size="30" readonly="readonly" placeholder="App update date" style="width: 300px;"/></td></tr> |
| <tr><td class="label">Description:</td></tr> |
| <tr><td><textarea id="appDescription" class="readentry" cols="40" rows="3" readonly="readonly" placeholder="Short description of the app" style="width: 300px;"></textarea></td></tr> |
| </table> |
| </form> |
| <br/> |
| |
| </div> |
| |
| <script type="text/javascript"> |
| (function infobody() { |
| |
| /** |
| * Get the app name. |
| */ |
| var appname = ui.fragmentParams(location)['app']; |
| |
| /** |
| * Setup page layout. |
| */ |
| (function layout() { |
| document.title = config.windowtitle() + ' - Info - ' + appname; |
| $('viewhead').innerHTML = '<span id="appname" class="cmenu">' + appname + |
| '<input type="button" class="redbutton plusminus" style="position: absolute; top: 4px; left: 2px;" id="deleteApp" value="-" title="Delete this app" disabled="true"/>' + |
| '<input type="button" class="bluebutton" id="editApp" style="position: absolute; top: 4px; right: 72px;" value="Edit" title="Edit this app" disabled="true"/>' + |
| '<input type="button" class="greenbutton plusminus" id="runApp" style="position: absolute; top: 4px; right: 37px;" value=">" title="Run this app"/>' + |
| '<input type="button" class="bluebutton" style="position: absolute; top: 4px; right: 2px; font-size: 16px;" id="cloneApp" value="C" title="' + config.clone() + ' this app"/>'; |
| if (!ui.isMobile()) |
| $('viewform').className = 'viewform flatscrollbars'; |
| $('appURL').value = window.location.hostname + '/' + appname + '/'; |
| |
| $('viewform').appendChild(ui.declareCSS( |
| '.ratings { ' + |
| 'background: url(\'' + ui.b64png(appcache.get('/public/ratings.b64')) + '\'); ' + |
| 'vertical-align: middle; width: 50px; height: 14px; display: inline-block; ' + |
| ' }')); |
| })(); |
| |
| /** |
| * Set images. |
| */ |
| (function drawImages() { |
| $('appIcon').src = ui.b64png(appcache.get('/public/app.b64')); |
| $('authorPicture').src = ui.b64png(appcache.get('/public/user.b64')); |
| })(); |
| |
| /** |
| * Initialize service references. |
| */ |
| var editorComp = sca.component("Editor"); |
| var apps = sca.reference(editorComp, "apps"); |
| var icons = sca.reference(editorComp, "icons"); |
| var pictures = sca.reference(editorComp, "pictures"); |
| var ratings = sca.reference(editorComp, "ratings"); |
| |
| /** |
| * The current app entry, author and saved XML content. |
| */ |
| var savedappxml = ''; |
| var author; |
| var savediconxml; |
| |
| /** |
| * Get and display the requested app. |
| */ |
| (function getapp() { |
| if (isNull(appname)) |
| return false; |
| workingstatus(true); |
| showstatus('Loading'); |
| |
| return apps.get(appname, function(doc) { |
| |
| // Stop now if we didn't get the app |
| if (doc == null) { |
| errorstatus('Couldn\'t get the app info'); |
| workingstatus(false); |
| return false; |
| } |
| |
| var appentry = car(elementsToValues(atom.readATOMEntry(mklist(doc)))); |
| author = cadr(assoc("'author", appentry)); |
| $('appAuthor').value = author.split('@')[0]; |
| var updated = assoc("'updated", appentry); |
| $('appUpdated').value = isNull(updated)? '' : xmldatetime(cadr(updated)).toLocaleDateString(); |
| var content = cadr(assoc("'content", appentry)); |
| var description = assoc("'description", content); |
| $('appDescription').value = isNull(description) || isNull(cadr(description))? '' : cadr(description); |
| //var ratingy = -20 * (4 - Math.floor(Math.random() * 4)); |
| //$('appRating').style.backgroundPosition = '0px ' + ratingy + 'px'; |
| //$('appRatings').value = ''; |
| savedappxml = car(atom.writeATOMEntry(valuesToElements(mklist(appentry)))); |
| |
| // Enable author to edit and delete the app |
| if (username == author) { |
| $('appDescription').readOnly = false; |
| $('appDescription').className = 'flatentry'; |
| $('uploadIcon').style.display = 'inline'; |
| $('deleteApp').disabled = false; |
| $('editApp').disabled = false; |
| ui.onclick($('editApp'), function(e) { |
| return ui.navigate('/#view=page&app=' + appname, '_view'); |
| }); |
| ui.onclick($('deleteApp'), function(e) { |
| return ui.navigate('/#view=delete&app=' + appname, '_view'); |
| }); |
| onlinestatus(); |
| } else { |
| showstatus('Read only'); |
| } |
| workingstatus(false); |
| return true; |
| }); |
| })(); |
| |
| /** |
| * Get and display the author's picture. |
| */ |
| (function getpic(author) { |
| workingstatus(true); |
| showstatus('Loading'); |
| |
| return pictures.get(author, function(doc) { |
| |
| // Stop now if we didn't get a picture |
| if (doc == null) { |
| errorstatus('Author picture not available'); |
| workingstatus(false); |
| return false; |
| } |
| |
| var picentry = car(elementsToValues(atom.readATOMEntry(mklist(doc)))); |
| var content = assoc("'content", picentry); |
| var picture = assoc("'picture", content); |
| var img = assoc("'image", picture); |
| if (!isNull(img)) |
| $('authorPicture').src = cadr(img); |
| |
| onlinestatus(); |
| workingstatus(false); |
| return true; |
| }); |
| return true; |
| })(); |
| |
| /** |
| * Get and display the app icon. |
| */ |
| (function geticon() { |
| if (isNull(appname)) |
| return false; |
| workingstatus(true); |
| showstatus('Loading'); |
| |
| return icons.get(appname, function(doc) { |
| // Stop now if we didn't get an icon |
| if (doc == null) { |
| errorstatus('Icon not available'); |
| workingstatus(false); |
| return false; |
| } |
| |
| var iconentry = car(elementsToValues(atom.readATOMEntry(mklist(doc)))); |
| savediconxml = car(atom.writeATOMEntry(valuesToElements(mklist(iconentry)))); |
| var content = assoc("'content", iconentry); |
| var icon = assoc("'icon", content); |
| var img = assoc("'image", icon); |
| if (!isNull(img)) |
| $('appIcon').src = cadr(img); |
| |
| onlinestatus(); |
| workingstatus(false); |
| return true; |
| }); |
| return true; |
| })(); |
| |
| /** |
| * Refresh icon. |
| */ |
| var refreshingicon = false; |
| function refreshicon() { |
| if (isNull(appname)) |
| return false; |
| if (!refreshingicon) |
| return false; |
| $('refreshingIcon').style.display = 'inline-block'; |
| return icons.get(appname, function(doc) { |
| if (doc == null) { |
| errorstatus('Icon not available'); |
| $('refreshingIcon').style.display = 'none'; |
| refreshingicon = false; |
| return false; |
| } |
| |
| var iconentry = car(elementsToValues(atom.readATOMEntry(mklist(doc)))); |
| var content = assoc("'content", iconentry); |
| var icon = assoc("'icon", content); |
| var token = assoc("'token", icon); |
| |
| // Update icon |
| if (isNull(token)) { |
| var entryxml = car(atom.writeATOMEntry(valuesToElements(mklist(iconentry)))); |
| savediconxml = entryxml; |
| var img = assoc("'image", icon); |
| if (!isNull(img)) |
| $('appIcon').src = cadr(img); |
| $('refreshingIcon').style.display = 'none'; |
| refreshingicon = false; |
| return true; |
| } |
| |
| // Refresh in 2 secs |
| return ui.delay(refreshicon, 2000); |
| }, 'remote'); |
| return true; |
| } |
| |
| /** |
| * Get and display the app ratings. |
| */ |
| (function getratings() { |
| if (isNull(appname)) |
| return false; |
| workingstatus(true); |
| showstatus('Loading'); |
| |
| return ratings.get(appname, function(doc) { |
| // Stop now if we didn't get an icon |
| if (doc == null) { |
| errorstatus('Ratings not available'); |
| workingstatus(false); |
| return false; |
| } |
| |
| var ratingsentry = car(elementsToValues(atom.readATOMEntry(mklist(doc)))); |
| var aratings = assoc("'ratings", assoc("'content", ratingsentry)); |
| var ar = assoc("'rating", aratings); |
| var ar1 = assoc("'rating1", aratings); |
| var ar2 = assoc("'rating2", aratings); |
| var ar3 = assoc("'rating3", aratings); |
| var ar4 = assoc("'rating4", aratings); |
| var rating = isNull(ar)? 0 : Number(cadr(ar)); |
| var reviews = (isNull(ar1)? 0 : Number(cadr(ar1))) + (isNull(ar2)? 0 : Number(cadr(ar2))) + (isNull(ar3)? 0 : Number(cadr(ar3))) + (isNull(ar4)? 0 : Number(cadr(ar4))); |
| |
| var ratingy = -20 * (4 - Math.floor(rating)); |
| $('appRating').style.backgroundPosition = '0px ' + ratingy + 'px'; |
| $('appRatings').value = reviews + (reviews > 1? ' ratings' : ' rating'); |
| |
| onlinestatus(); |
| workingstatus(false); |
| return true; |
| }); |
| return true; |
| })(); |
| |
| /** |
| * Save the current app. |
| */ |
| function saveapp(entryxml) { |
| workingstatus(true); |
| showstatus('Saving'); |
| savedappxml = entryxml; |
| apps.put(appname, savedappxml, function(e) { |
| if (e) { |
| showstatus('Local copy'); |
| workingstatus(false); |
| return false; |
| } |
| |
| showstatus('Saved'); |
| workingstatus(false); |
| return false; |
| }); |
| return true; |
| } |
| |
| /** |
| * Save the app icon. |
| */ |
| function saveicon(entryxml) { |
| workingstatus(true); |
| showstatus('Uploading'); |
| savedappxml = entryxml; |
| icons.put(appname, savedappxml, function(e) { |
| if (e) { |
| showstatus('Local copy'); |
| workingstatus(false); |
| return false; |
| } |
| |
| showstatus('Uploaded'); |
| workingstatus(false); |
| return true; |
| }); |
| return true; |
| } |
| |
| /** |
| * Handle a change event |
| */ |
| function onappchange() { |
| if (username != author) |
| return false; |
| |
| // Validate user input |
| var description = $('appDescription').value; |
| if (description.length > 120) { |
| errorstatus('Description cannot be longer than 120 characters'); |
| return false; |
| } |
| |
| // Save the changes |
| var appentry = mklist("'entry", mklist("'title", appname), mklist("'id", appname), mklist("'content", mklist("'info", mklist("'description", description)))); |
| var entryxml = car(atom.writeATOMEntry(valuesToElements(mklist(appentry)))); |
| if (savedappxml == entryxml) |
| return false; |
| showstatus('Modified'); |
| return saveapp(entryxml); |
| } |
| |
| $('appDescription').onchange = onappchange; |
| |
| /** |
| * Handle a key event. |
| */ |
| var lastkeyup = null; |
| $('appDescription').onkeyup = function() { |
| var t = new Date().getTime(); |
| lastkeyup = t; |
| ui.async(function() { |
| return t == lastkeyup? onappchange() : true; |
| }, 2000); |
| }; |
| |
| /** |
| * Handle a form submit event. |
| */ |
| $('appForm').onsubmit = function() { |
| onappchange(); |
| return false; |
| }; |
| |
| /** |
| * Handle Clone button event. |
| */ |
| ui.onclick($('cloneApp'), function(e) { |
| return ui.navigate('/#view=clone&app=' + appname, '_view'); |
| }); |
| |
| /** |
| * Handle Run button event. |
| */ |
| ui.onclick($('runApp'), function(e) { |
| return ui.navigate('/' + appname + '/', '_blank'); |
| }); |
| |
| /** |
| * Read and upload icon file. |
| */ |
| function uploadicon(files) { |
| if (username != author) |
| return false; |
| if (!files || files.length == 0) |
| return false; |
| if (!files[0].type.match('image.*')) { |
| errorstatus('Please select an image'); |
| return false; |
| } |
| workingstatus(true); |
| showstatus('Loading'); |
| |
| // Read the selected file into a 50x50 image |
| return ui.readimage(files[0], |
| function(e) { |
| errorstatus('Couldn\'t read the file'); |
| workingstatus(false); |
| }, |
| function(p) { |
| showstatus('Loading ' + p + '%'); |
| }, |
| function(url) { |
| // Update the app icon |
| $('appIcon').src = url; |
| showstatus('Loaded'); |
| |
| // Now upload it |
| ui.async(function() { |
| var iconentry = mklist("'entry", mklist("'title", appname), mklist("'id", appname), mklist("'author", username), mklist("'content", mklist("'icon", mklist("'image", url)))); |
| var entryxml = car(atom.writeATOMEntry(valuesToElements(mklist(iconentry)))); |
| if (savediconxml == entryxml) { |
| onlinestatus(); |
| workingstatus(false); |
| return false; |
| } |
| return saveicon(entryxml); |
| }); |
| }, 50, 50); |
| } |
| |
| /** |
| * Upload an icon in an email. |
| */ |
| function emailicon() { |
| |
| // Generate and put an icon email upload token |
| workingstatus(true); |
| showstatus('Uploading'); |
| var token = uuid4(); |
| var iconentry = mklist("'entry", mklist("'title", appname), mklist("'id", appname), mklist("'author", username), mklist("'content", mklist("'icon", mklist("'token", token)))); |
| var entryxml = car(atom.writeATOMEntry(valuesToElements(mklist(iconentry)))); |
| icons.put(appname, entryxml, function(e) { |
| if (e) { |
| showstatus('Local copy'); |
| workingstatus(false); |
| return false; |
| } |
| workingstatus(false); |
| |
| // Open the email app |
| var mailto = safeb64encode('i/' + appname + '/' + token); |
| ui.navigate('mailto:' + mailto + '@' + topdomainname(window.location.hostname) + '?subject=Uploading icon&body=Paste icon here', '_self'); |
| |
| // Refresh app icon |
| refreshingicon = true; |
| return ui.delay(refreshicon, 500); |
| }, 'remote'); |
| } |
| |
| /** |
| * Handle icon upload events. |
| */ |
| ui.onclick($('uploadIcon'), function(e) { |
| if (ui.isMobile() && ((ui.isWebkit() && ui.browserVersion() < 6.0) || (ui.isAndroid() && ui.browserVersion() < 2.2))) |
| return ui.delay(function() { return emailicon(); }); |
| return ui.delay(function() { return $('uploadFile').click(); }); |
| }); |
| $('uploadFile').onchange = function(e) { |
| return uploadicon(e.target.files); |
| }; |
| $('appIcon').ondrag = function(e) { |
| e.stopPropagation(); |
| e.preventDefault(); |
| e.dataTransfer.dropEffect = 'copy'; |
| }; |
| $('appIcon').ondrop = function(e) { |
| e.stopPropagation(); |
| e.preventDefault(); |
| return uploadicon(e.dataTransfer.files); |
| }; |
| |
| /** |
| * Handle rate button event. |
| */ |
| ui.onclick($('rateApp'), function(e) { |
| return ui.navigate('/#view=rate&app=' + appname, '_view'); |
| }); |
| |
| })(); |
| </script> |
| |
| </div> |