diff options
Diffstat (limited to 'themes')
| -rw-r--r-- | themes/okadmin/public/css/main.css | 238 | ||||
| -rw-r--r-- | themes/okadmin/public/js/app.js | 346 | ||||
| -rw-r--r-- | themes/okadmin/public/js/parser.js | 49 | ||||
| -rw-r--r-- | themes/okadmin/public/js/upload.js | 225 | ||||
| -rw-r--r-- | themes/okadmin/templates/404.liquid | 54 | ||||
| -rw-r--r-- | themes/okadmin/templates/5xx.liquid | 54 | ||||
| -rw-r--r-- | themes/okadmin/templates/index.liquid | 82 | ||||
| -rw-r--r-- | themes/okadmin/templates/partials/errors.liquid | 10 | ||||
| -rw-r--r-- | themes/okadmin/templates/partials/flash.liquid | 20 | ||||
| -rw-r--r-- | themes/okadmin/templates/partials/head.liquid | 6 | ||||
| -rw-r--r-- | themes/okadmin/templates/partials/inputs.liquid | 346 | ||||
| -rw-r--r-- | themes/okadmin/templates/partials/tail.liquid | 13 | ||||
| -rw-r--r-- | themes/okadmin/templates/resource.liquid | 18 | ||||
| -rw-r--r-- | themes/okadmin/templates/resource_new.liquid | 6 |
14 files changed, 1277 insertions, 190 deletions
diff --git a/themes/okadmin/public/css/main.css b/themes/okadmin/public/css/main.css index a1e20a0..6a48e94 100644 --- a/themes/okadmin/public/css/main.css +++ b/themes/okadmin/public/css/main.css @@ -14,11 +14,12 @@ html, body { background-attachment: scroll; } -ul { +ul, ol { padding: 0; list-style: none; } +.main.index .resource-category button, a { color: #A200FF; text-decoration: none; @@ -26,6 +27,7 @@ a { text-transform: uppercase; } +.main.index .resource-category button:hover, a:hover { border-bottom: 1px solid #A200FF; } @@ -41,11 +43,14 @@ a:visited { } .admin-header .breadcrumb { - margin-left: 1em; + margin-left: 0.5em; font-size: 2em; color: rgba(0, 0, 0, 0.25); line-height: 50px; } +.admin-header .breadcrumb b { + color: #333; +} .admin-header .site-link { float: right; @@ -62,7 +67,12 @@ a:visited { border: 2px solid #ddd; } -nav { +.main.index .resource-category.active li:before { + content: "፧"; + margin-right: 1em; +} + +.resource-nav { background: white; width: 10%; margin: 2.5em 1em; @@ -86,25 +96,69 @@ h2 { transform: rotate(-1deg); } -.main.index .resource-category a.add-new { - border-bottom: 3px solid rgba(0, 0, 0, 0); +.main.index .resource-category nav { float: right; - font-size: 1.5em; +} + +.main.index .resource-category.active ol { + cursor: -webkit-grab; + cursor: grab; +} + +.main.index .resource-category.active li a { + pointer-events: none; +} + +/* Makes the button look like a link */ +.main.index .resource-category button { + background: none !important; + height: 1.5em; + border: none; + padding: 0 !important; + font: inherit; + cursor: pointer; + font-family: Monaco, monospace; + text-transform: uppercase; +} + +.main.index .resource-category .btn { + border-bottom: 3px solid rgba(0, 0, 0, 0); color: rgba(0, 0, 0, 0.25); + line-height: 20px; +} + +.main.index .resource-category .btn { + display: none; +} + +.main.index .resource-category .btn.active { + display: inline; +} + +.main.index .resource-category .btn:hover { + border-bottom: 1px solid rgba(0, 0, 0, 0.25); +} + +.main.index .resource-category .btn { + margin-right: 1em; +} + +.main.index .resource-category .btn:last-child { + margin-right: 0; +} +.main.index .resource-category .add-btn { + font-size: 20px; } .main.index .resource-category li { margin: 1em 0; } -.main.index .resource-category a.add-new:hover { - border-bottom: 3px solid rgba(0, 0, 0, 0.25); -} .main.resource { float: left; margin-top: 2em; - width: 80%; + width: 85%; } .main.resource > * { @@ -151,7 +205,18 @@ label { padding: 0 0.5em; margin-bottom: 1em; } +.main.resource form input[name=id] { + width: 15em; +} +button, input[type=submit] { + cursor: pointer; +} +.main.resource .date input { + /* date inputs need font family override */ + font-family: "Helvetica", sans-serif; +} .main.resource form .group { + position: relative; display: block; float: left; width: 31em; @@ -168,27 +233,57 @@ label { border: 0; display: none; } -.main.resource form .group.loaded input:first-child, +.main.resource form .group.image input, +.main.resource form .group.video input[type=text]:first-child, +.main.resource form .group.loaded.video input[type=text], .main.resource form .group input:first-child { display: block; width: 25em; } +.main.resource form .group .checkboxes, +.main.resource form .group.loaded .checkboxes { + clear: left; + display: block; + max-width: 250px; + padding-top: 5px; +} +.main.resource form .group .checkboxes input.flag, +.main.resource form .group.loaded .checkboxes input.flag { + display: inline-block; + max-width: 20px; + float: none; +} +.main.resource form .group .checkboxes label, +.main.resource form .group.loaded .checkboxes label { + display: inline-block; + float: none; + width: 70px; + margin: 0; + text-align: left; +} .main.resource form .group.loaded label { display: block; } .main.resource form .group.loaded input { display: block; - width: 20.05em; } -.main.resource form .group input { - display: none; +.main.resource form .group input[type=text] { + width: 20.05em; margin-bottom: 0.1em; } +.main.resource form .group.image .image-element, +.main.resource form .group.video input[type=text], .main.resource form .group.loaded input[hidden], +.main.resource form .group.image.loaded .fields, .main.resource form input[hidden] { display: none; } - +.main.resource form .group.image.loaded .image-element { + display: block; +} +.main.resource form .fields { + height: 3em; +} .main.resource form textarea { padding: 0.5em; height: 15em; @@ -202,6 +297,9 @@ label { font-size: 1.0em; margin-top: 1.0em; } +.main.resource form#delete_form button { + float: right; +} .main.resource form ol { margin: 0; @@ -211,43 +309,118 @@ label { list-style-type: none; display: block; clear: both; - height: 10em; + height: 7em; } .main.resource form img { - width: 10em; - max-height: 10em; + width: auto; + height: auto; + max-width: 10em; + max-height: 6em; border: 0; +} +.main.resource form .images img { cursor: -webkit-grab; cursor: grab; } .main.resource form textarea.caption { width: 15em; - height: 9em; + height: 6em; +} +.main.resource form .audio-element input[type=text], +.main.resource form .video-element input[type=text] { + width: 15em; +} +.main.resource form .group input[type=text].link-input, +.main.resource form .group input[type=text].link-input-new { + width: 13.05em; + padding: 0 0 0 0.5em; +} +.handle { + display: block; + width: 1em; + height: 2em; + background: #ddd; + float: left; +} +.main.resource form .links li { + height: 2em; +} +.main .link-list .add-link-btn, +.main .link-list .remove-link-btn { + margin: 0; + height: 2em; + line-height: 1em; } .add-image-button { background: #ddd; clear: left; text-align: left; - padding: 10px; - width: 15em; + float: left; + margin-right: 1em; position: relative; + overflow: hidden; + cursor: pointer; } .add-image-button:hover { background: #def; } +.main.resource form .add-image-button button { + margin: 0; + pointer-events: none; + width: 100%; height: 100%; +} .add-image-button input[type=file] { opacity: 0; position: absolute; top: 0; left: 0; width: 100%; height: 100%; + margin: 0; padding: 0; cursor: pointer; } -li.image-element:hover .remove-image { +.audio-element:hover .remove, +.video-element:hover .remove, +.image-element:hover .remove { display: block; } -.remove-image { +.audio-element .remove:hover, +.video-element .remove:hover, +.image-element .remove:hover { + color: red; +} + +.progress { + position: absolute; + top: 0; right: -100px; + padding: 10px 10px 0 10px; + margin: 0px; + width: 100px; + pointer-events: none; + background: #eee; + opacity: 0; + transition: opacity 0.3s; +} +.progress .xhr { + height: 10px; + background: black; + margin-bottom: 10px; + transition: all 0.2s; +} +.progress .xhr div { + background: white; + transition: all 0.2s; + height: 100%; +} +.progress.loading { + opacity: 1; +} + +/* +.remove { display: none; } + */ + +#delete_form button:hover { color: red } .template { display: none; @@ -257,16 +430,27 @@ li.image-element:hover .remove-image { } -.errors { +.success, .errors { background: white; - padding: 10px; - width: 100%; + padding: 9px 8px 7px; + width: 50%; line-height: 1.4em; + border: 1px solid; + margin: 1em; + border-radius: 2px; } -.errors .message { + +.success { + color: green; +} +.errors { color: red; } .clear { clear: both; } + +.hidden { + display: none; +} diff --git a/themes/okadmin/public/js/app.js b/themes/okadmin/public/js/app.js index 4b8d98f..9891298 100644 --- a/themes/okadmin/public/js/app.js +++ b/themes/okadmin/public/js/app.js @@ -1,61 +1,333 @@ var OKAdmin = function(){ - - OKUpload.bind() - OKUpload.add = function(data){ - var url = data[0].extra.Location - add_image(url) - } - function add_image(url){ - var imageTemplate = $("#captioned-image-template").html() - var $el = $(imageTemplate) - $el.find(".uri").val(url) - $el.find("img").attr("src", url) - $(".captioned-image-list ol").append($el) - } - $(".captioned-image-list ol").sortable() - $(".captioned-image-list ol").disableSelection() - - $("#add-image-url").keydown(pressEnter(function(e){ - var url = $(this).val() - $(this).val("") - add_image(url) - })}) - - $(document).on("click", ".remove-image", function(){ - if (confirm("Delete this image?")) { + + // initialize our multi-image uploader with an element and a template + $(".group.image-list").each(function(){ + var parent = this + var uploader = new OKUpload () + uploader.bind( this ) + uploader.add = function(media){ + var url = media.url + var imageTemplate = $(".image-template", parent).html() + var $el = $(imageTemplate) + $el.find(".uri").val(media.url) + $el.find(".image-width").val(media.width) + $el.find(".image-height").val(media.height) + $el.find("img").attr("src", media.url) + $("ol", parent).prepend($el) + } + }) + // delete image from gallery + $(document).on("mousedown", ".image-list .remove", function(){ + if (confirm("Remove this image?")) { $(this).parent().remove() } }) - - $(".video .url").keydown(pressEnter(function(){ + + // Add default date + $('.date input').each(function(i, el){ + var value = el.getAttribute('value') + if (!value) { + el.setAttribute('value', toDateInputValue(new Date)) + } + + function toDateInputValue (date) { + var local = new Date(date); + local.setMinutes(date.getMinutes() - date.getTimezoneOffset()); + return local.toJSON().slice(0,10); + } + }) + + // initialize our multimedia uploader with an element and a template + $(".group.media-list").each(function(){ + var parent = this + var uploader = new OKUpload () + uploader.bind( this ) + uploader.add = function(media){ + var url = media.url + var imageTemplate = $(".image-template", parent).html() + var $el = $(imageTemplate) + $el.find(".uri").val(media.url) + $el.find(".image-width").val(media.width) + $el.find(".image-height").val(media.height) + $el.find("img").attr("src", media.url) + $("ol", parent).prepend($el) + } + uploader.addMedia = function(media){ + switch (media.type) { + case 'youtube': + case 'vimeo': + case 'video': + var videoTemplate = $(".video-template", parent).html() + var $el = $(videoTemplate) + $el.addClass("loaded") + $el.find(".video-type").val( media.type ) + $el.find(".video-token").val( media.token ) + $el.find(".video-uri").val( media.uri ) + $el.find(".video-title").val( media.title ) + $el.find(".video-thumb").val( media.thumbnail ) + $el.find(".video-width").val( media.width ) + $el.find(".video-height").val( media.height ) + $el.find("img").attr("src", media.thumbnail ) + $("ol", parent).prepend($el) + break + case 'audio': + var audioTemplate = $(".audio-template", parent).html() + var $el = $(audioTemplate) + $el.addClass("loaded") + $el.find(".audio-type").val( media.type ) + $el.find(".audio-token").val( media.token ) + $el.find(".audio-uri").val( media.uri ) + $el.find(".audio-title").val( media.title ) + $el.find(".audio-thumb").val( media.thumbnail ) + $el.find(".audio-duration").val( media.duration ) + $el.find("img").attr("src", media.thumbnail ) + $("ol", parent).prepend($el) + break + case 'link': + var linkTemplate = $(".link-template", parent).html() + var $el = $(linkTemplate) + $el.addClass("loaded") + $el.find(".uri").val( media.url ) + $("ol", parent).prepend($el) + break + default: + alert("Unsupported link type!") + } + } + }) + // delete image from gallery + $(document).on("mousedown", ".media-list .remove", function(){ + if (confirm("Remove this media?")) { + $(this).parent().remove() + } + }) + + // initialize our single image uploader with existing DOM + $(".group.image").each(function(){ + var $el = $(this) + var uploader = new OKUpload () + uploader.bind( this ) + uploader.add = function(media){ + console.log(media) + $el.find(".uri").val(media.url) + $el.find(".caption").val("") + $el.find(".image-width").val(media.width) + $el.find(".image-height").val(media.height) + $el.find("img").attr("src", media.url).show() + $el.addClass("loaded") + } + }) + // delete image from single image entry + $(document).on("mousedown", ".image .remove", function(){ + if (confirm("Remove this image?")) { + var $el = $(this).closest(".image") + $el.removeClass('loaded') + $el.find(".uri").val("") + $el.find(".image-width").val("") + $el.find(".image-height").val("") + $el.find(".caption").val("") + $el.find("img").attr("src", "") + } + }) + + // make the region sortable with drag-and-drop + $(".media-list ol, .image-list ol, .link-list .links").sortable() + $(".media-list ol, .image-list ol").disableSelection() + + // populate a video field with info from our url parser + var last_url + $(".video .url").on("focus", function(){ + var $el = $(this) + last_url = $el.val() + }) + $(".video .url").on("keydown blur", pressEnter(function(){ var $el = $(this) var url = $el.val() + if (url == last_url) { return } Parser.parse( url, function(media){ console.log(url,media) $el.parent().addClass("loaded") $el.parent().find(".video-type").val( media.type ) $el.parent().find(".video-token").val( media.token ) + $el.parent().find(".video-uri").val( media.url ) $el.parent().find(".video-title").val( media.title ) $el.parent().find(".video-thumb").val( media.thumbnail ) + $el.parent().find(".video-width").val( media.width ) + $el.parent().find(".video-height").val( media.height ) }) - }})) - - $("form").submit(function(){ - $(".image-element").each(function(index){ - $(this).find("input,textarea").each(function(){ - var field = $(this).attr("name").replace(/\[\]/, "[" + index + "]") - $(this).attr("name", field) + })) + + // Add a new link to the list + $('.link-list').on('click', '.add-link-btn', function addNewLink (e) { + e.preventDefault && e.preventDefault() + e.stopPropagation && e.stopPropagation() + var $delegate = $(e.delegateTarget) + var $list = $delegate.find('.links') + var linkCount = $list.find("li").length + + var $linkText = $delegate.find(".link-input-new.link-text") + var $linkURI = $delegate.find(".link-input-new.link-uri") + + var template = $delegate.find(".link-template").html() + template = template.replace(/\[\]/g, "[" + linkCount + "]") + var $el = $(template) + $el.find(".link-text").val( $linkText.val() ) + $el.find(".link-uri").val( $linkURI.val() ) + $list.append($el) + $linkText.val("") + $linkURI.val("") + }) + + // Remove a link from the list + $('.link-list').on('click', '.remove-link-btn', function(e) { + e.preventDefault() + e.stopPropagation() + var $target = $(e.target) + $target.closest("li").remove() + }) + + // fix post indexing in list-driven inputs + $(".main.resource form").submit(function(e){ + var $id = $("[name=id]"), $title = $("[name=title]"), $menu = $("[name=menu]"), $section = $(".resource.main") + var id = $section.data("id"), type = $section.data("type") + + if ($title.length && ! $title.val()) { + $title.focus() + alert("Please enter a title") + e.preventDefault() + return + } + + if ($menu.length && ! $menu.val()) { + $menu.val( $title.val() ) + } + + // TODO: pass through whether this page is static + if (type === "page" && (id == "contact" || id == "about")) { + ; + } + else { + var slug = slugify( $title.val() ) + $id.val( slug ) + } + + // Parse date input + $('.property .date').each(function(i, el) { + var name = $(el).parent('.property').data('name') + var $input = $(el).find('input') + var date = new Date($input.val()) + // Set to middle of day so it is the same date + // for all locales + date.setUTCHours(12) + var dateString = date.toUTCString() + var normalizedInput = document.createElement('input') + $(normalizedInput).attr({ + name: name, + type: 'text', + value: dateString + }) + $input.remove() + $(el).append(normalizedInput) + }) + + // Modify flags checkboxes such that unchecked ones return "false" + // instead of nothing + $('.property input[type=checkbox]').each(function(i, el) { + var checked = !!el.checked + if (!checked) { + el.value = 'false' + el.setAttribute('checked', true) + } + }) + + $(".link-list").each(function(){ + var $inputs = $(this).find(".link-input-new") + if ($inputs.eq(0).val() && $inputs.eq(1).val()) { + $(this).find(".add-link-btn").trigger("click") + } + }) + + $("ol").each(function(){ + $("li", this).each(function(index){ + $(this).find("input,textarea").each(function(){ + var field = $(this).attr("name").replace(/\[[0-9]*\]/, "[" + index + "]") + $(this).attr("name", field) + }) }) }) }) + + // delete individual records + $("#delete_form").submit(function(e){ + if (confirm("Are you sure you want to delete this record?")) { + return + } + else { + e.preventDefault() + } + }) + + // reorder items in categories + $(".resource-category:not(.grouped)").on("click", ".edit-btn", function(e) { + e.preventDefault(); + var $parent = $(e.delegateTarget) + var $editBtn = $parent.find(".edit-btn"); + var $cancelBtn = $parent.find(".cancel-btn"); + var $saveBtn = $parent.find(".save-btn"); + var $ol = $parent.find("ol"); + var toggles = [$parent, $cancelBtn, $saveBtn, $editBtn]; + + $ol.sortable(); + $ol.disableSelection(); + toggle(); + + $cancelBtn.one("click", function(e) { + $ol.sortable("cancel"); + $ol.enableSelection(); + toggle(); + }); + + $saveBtn.one("click", function(e) { + $ol.sortable(); + toggle(); + }); + + function toggle() { + toggles.forEach(function($el) { + $el.toggleClass('active'); + }) + } + }); + + // save new category order + $(".resource-category.root").on("submit", "form", function(e) { + var $parent = $(e.delegateTarget); + var $resources = $parent.find(".resource-input") + var isDescending = $parent.hasClass("descending") + $resources.each(function(index) { + var $input = $(this); + var parsed = JSON.parse($input.val()); + if (isDescending) { + parsed.__index = $resources.length - index; + } + else { + parsed.__index = index; + } + $input.val(JSON.stringify(parsed)); + }) + }); - function pressEnter(fn){ - return function(e){ - if (e.keyCode !== 13) return + $(window).on('keydown', function(e){ + if ( (e.ctrlKey || e.altKey || e.metaKey) && e.keyCode == 83 ) { e.preventDefault() + $("#resource_form").submit() } - } + }) +} $(function(){ window.app = new OKAdmin () }) + + +function slugify (s){ return (s || "").toLowerCase().replace(/\s/g,"-").replace(/[^-_a-zA-Z0-9]/g, '-').replace(/-+/g, "-") } diff --git a/themes/okadmin/public/js/parser.js b/themes/okadmin/public/js/parser.js index 411f425..81bba2d 100644 --- a/themes/okadmin/public/js/parser.js +++ b/themes/okadmin/public/js/parser.js @@ -31,15 +31,17 @@ var Parser = { regex: /\.(mp4|webm)(\?.*)?$/i, fetch: function(url, done) { var video = document.createElement("video") + var url_parts = url.replace(/\?.*$/, "").split("/") + var filename = url_parts[ url_parts.length-1 ] video.addEventListener("loadedmetadata", function(){ var width = video.videoWidth, height = video.videoHeight video = null done({ url: url, type: "video", - token: "", - thumbnail: "", - title: "", + token: url, + thumbnail: "http://okfocus.s3.amazonaws.com/misc/okcms/video.png", + title: filename, width: width, height: height, }) @@ -51,6 +53,31 @@ var Parser = { return '<video src="' + media.url + '">'; } }, { + type: 'audio', + regex: /\.(wav|mp3)(\?.*)?$/i, + fetch: function(url, done) { + var audio = document.createElement("audio") + var url_parts = url.replace(/\?.*$/, "").split("/") + var filename = url_parts[ url_parts.length-1 ] + audio.addEventListener("loadedmetadata", function(){ + var duration = audio.duration + audio = null + done({ + url: url, + type: "audio", + token: url, + thumbnail: "http://okfocus.s3.amazonaws.com/misc/okcms/audio.png", + title: filename, + duration: duration, + }) + }) + audio.src = url + audio.load() + }, + tag: function (media) { + return '<audio src="' + media.url + '">'; + } + }, { type: 'youtube', regex: /(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/ ]{11})/i, fetch: function(url, done) { @@ -73,6 +100,8 @@ var Parser = { token: id, thumbnail: thumb, title: res.snippet.title, + autoplay: false, + loop: false, width: 640, height: 360, }) @@ -106,6 +135,8 @@ var Parser = { title: res.title, width: res.width, height: res.height, + autoplay: false, + loop: false, }) } }) @@ -114,8 +145,7 @@ var Parser = { // return '<img class="video" type="vimeo" vid="'+media.token+'" src="'+media.thumbnail+'"><span class="playvid">▶</span>'; return '<div class="video" style="width: ' + media.width + 'px; height: ' + media.height + 'px; overflow: hidden; position: relative;"><iframe frameborder="0" scrolling="no" seamless="seamless" webkitallowfullscreen="webkitAllowFullScreen" mozallowfullscreen="mozallowfullscreen" allowfullscreen="allowfullscreen" id="okplayer" src="http://player.vimeo.com/video/' + media.token + '?api=1&title=0&byline=0&portrait=0&playbar=0&player_id=okplayer&loop=0&autoplay=0" width="' + media.width + '" height="' + media.height + '" style="position: absolute; top: 0px; left: 0px; width: ' + media.width + 'px; height: ' + media.height + 'px;"></iframe></div>' } - }, - { + }, { type: 'soundcloud', regex: /soundcloud.com\/[-a-zA-Z0-9]+\/[-a-zA-Z0-9]+\/?$/i, fetch: function (url, done) { @@ -126,13 +156,14 @@ var Parser = { + '&client_id=' + '0673fbe6fc794a7750f680747e863b10', success: function(result) { - // console.log(result) + console.log(result) done({ url: url, type: "soundcloud", token: result.id, thumbnail: result.artwork_url || result.user.avatar_url, title: result.user.username + " - " + result.title, + duration: result.duration, width: 166, height: 166, }) @@ -145,7 +176,6 @@ var Parser = { '&color=ff6600&auto_play=false&show_artwork=true"></iframe>' } }, - /* { type: 'link', regex: /^http.+/i, @@ -163,8 +193,7 @@ var Parser = { tag: function (media) { return '<a href="' + media.url + '" target="_blank">' + media.url + '</a>' } - } - */ + }, ], tumblr: function(url, cb){ @@ -232,6 +261,8 @@ var Parser = { title: stripHTML(post['video-caption']), width: 640, height: 360, + autoplay: false, + loop: false, } media_list.push(media) } diff --git a/themes/okadmin/public/js/upload.js b/themes/okadmin/public/js/upload.js index d9fd5ed..6ff7ac9 100644 --- a/themes/okadmin/public/js/upload.js +++ b/themes/okadmin/public/js/upload.js @@ -1,56 +1,191 @@ -var OKUpload = { - action: "/_services/image", - - bind: function(){ - var el = document.getElementById("file") - if (! el) return - el.addEventListener("change", OKUpload.handleFileSelect) - }, +var OKUpload = function(){ + this.config = $("#uploadConfig").data() + this.imageAction = "/_services/s3/image" + this.videoAction = "/_services/s3/video" + this.audioAction = "/_services/s3/audio" +} +OKUpload.prototype.bind = function(rapper){ + var uploader = this + this.rapper = rapper + this.$progress = $("<div class='progress'>") + this.xhrCount = 0 + this.loadCount = 0 + + $(this.rapper).append(this.$progress) + $(".add-image-button input", rapper).change( uploader.handleFileSelect.bind(uploader) ) + $(".add-url", rapper).on("keydown blur", pressEnter( function(e){ + var url = $(this).val() + $(this).val("") + uploader.parse(url) + })) +} +OKUpload.prototype.parse = function(url){ + if (! url) return + var uploader = this + Parser.parse( url, function(media){ + console.log(url, media) + if (! media) { + alert("Not a valid link") + } + else if (media.type == "image") { + uploader.add(media) + } + else { + uploader.addMedia(media) + } + }) +} +OKUpload.prototype.handleFileSelect = function(e) { + e.stopPropagation(); + e.preventDefault(); - handleFileSelect: function(e) { - e.stopPropagation(); - e.preventDefault(); + var files = e.dataTransfer ? e.dataTransfer.files : e.target.files; - var files = e.dataTransfer ? e.dataTransfer.files : e.target.files; + for (var i = 0, f; f = files[i]; i++) { + this.upload(f) + } +} +OKUpload.prototype.largeFileError = function(file, maxSize) { + var your_bytes = bytesToString(file.size) + var max_bytes = bytesToString(maxSize) + alert("Sorry, your file is too big.\n\n" + file.name + "\n\nYour file: " + your_bytes + "\nMax size: " + max_bytes) + function bytesToString (n) { + if (n < 1024) return n + " bytes" + n /= 1024 + if (n < 1024) return n.toFixed(1) + " kb" + n /= 1024 + if (n < 1024) return n.toFixed(1) + " mb" + } +} +OKUpload.prototype.upload = function(f){ - for (var i = 0, f; f = files[i]; i++) { - if ( ! f.type.match('image.*')) { - continue; - } - OKUpload.upload(f) + var field, action + + if ( f.type.match('video.*') ) { + if (this.config.videoMaxbytes && f.size > this.config.videoMaxbytes) { + return this.largeFileError(f, this.config.videoMaxbytes) + } + field = 'video' + action = this.videoAction + } + else if ( f.type.match('audio.*') ) { + if (this.config.audioMaxbytes && f.size > this.config.audioMaxbytes) { + return this.largeFileError(f, this.config.audioMaxbytes) } - }, + field = 'audio' + action = this.audioAction + } + else { + if (this.config.imageMaxbytes && f.size > this.config.imageMaxbytes) { + return this.largeFileError(f, this.config.imageMaxbytes) + } + field = 'image' + action = this.imageAction || this.action + } + + this.xhrCount += 1 - upload: function(f){ - var fd = new FormData() - fd.append('image', f) + this.$progress.addClass("loading") - var request = $.ajax({ - url: OKUpload.action, - type: "post", - data: fd, - dataType: "json", - processData: false, - contentType: false, - }) - request.done(OKUpload.success) - }, + var $loader = $("<div class='xhr'>") + var $loading_bar = $("<div>") + $loading_bar.css("width", "0%") + $loader.append( $loading_bar ) + this.$progress.append($loader) - success: function(media){ - if (media.error) { - console.log(media.error) - return + var fd = new FormData() + fd.append(field, f) + + var request = new XMLHttpRequest() + request.open("POST", action, true) + + request.addEventListener("progress", updateProgress.bind(this)); + request.addEventListener("load", transferComplete.bind(this)); + request.addEventListener("error", transferError.bind(this)); + request.addEventListener("abort", transferAbort.bind(this)); + + function updateProgress (e) { + if (e.lengthComputable) { + var percentComplete = Math.round( 100 * e.loaded / e.total ) + $loading_bar.css("width", percentComplete + "%") } - OKUpload.add(media) - }, - - add: function(media){ - console.log(media) - }, - - error: function(error){ - throw error - }, + } + function transferComplete (data) { + this.loadCount += 1 + this.hideUploadBars() + if (request.readyState == 4 && request.status == 200) { + var responseData + try { + responseData = JSON.parse( request.responseText ) + this.success( responseData ) + } + catch (e) { + console.log(request.responseText) + console.log("ERROR PARSING JSON") + } + } + console.log(arguments, request) + } + function transferError (data) { + console.log("Transfer error") + this.loadCount += 1 + this.hideUploadBars() + console.log(arguments) + } + function transferAbort (data) { + console.log("Transfer aborted") + this.loadCount += 1 + this.hideUploadBars() + console.log(arguments) + } + request.send(fd) + +/* + var request = $.ajax({ + url: this.action, + type: "post", + data: fd, + dataType: "json", + processData: false, + contentType: false, + }) + request.done(this.success.bind(this)) +*/ } +OKUpload.prototype.hideUploadBars = function () { + if (this.xhrCount == this.loadCount) { + this.$progress.removeClass("loading") + setTimeout(function(){ + this.$progress.empty() + }.bind(this), 300) + } +} +OKUpload.prototype.success = function(data){ + if (data.error) { + console.log(data.error) + return + } + var url = data.url + console.log(url) + this.parse(url) +} +OKUpload.prototype.add = function(media){ + console.log(media) +} +OKUpload.prototype.addMedia = function(media){ + console.log(media) +} +OKUpload.prototype.error = function(error){ + throw error +} + + +function pressEnter(fn){ + return function(e){ + if (e.keyCode && e.keyCode !== 13) return + e.preventDefault() + fn.apply(this) + } +}
\ No newline at end of file diff --git a/themes/okadmin/templates/404.liquid b/themes/okadmin/templates/404.liquid new file mode 100644 index 0000000..87f5342 --- /dev/null +++ b/themes/okadmin/templates/404.liquid @@ -0,0 +1,54 @@ +<!DOCTYPE html> +<html> + <head> + <title>404</title> + <style type="text/css"> + html, body { + margin: 0; + padding: 0; + font-family: "Helvetica", sans-serif; + background-image: url('http://okfoc.us/assets/images/photocopy.png'); + background-position: bottom center; + background-repeat: repeat; + background-attachment: scroll; + height: 100%; + font-size: 1.75em; + font-weight: bold; + color: #FFFFFF; + } + + a { + color: #8888FF; + text-decoration: none; + } + + a:hover { + border-bottom: 3px solid #8888FF; + } + + a:visited { + color: #8888FF; + } + + .message { + width: 700px; + padding: 1em 1em 1em 1em; + background-color: #0000FF; + margin: 0 auto; + margin-top: 1em; + } + + .message p:first-child { + margin-top: 0; + } + </style> + </head> + <body> + <div class="message"> + <p>¯\_(ツ)_/¯</p> + <p>We couldn't find that page.</p> + <p>Sure you have the right URL?</p> + <a href="javascript:history.back()">Back</a> + </div> + </body> +</html> diff --git a/themes/okadmin/templates/5xx.liquid b/themes/okadmin/templates/5xx.liquid new file mode 100644 index 0000000..f245545 --- /dev/null +++ b/themes/okadmin/templates/5xx.liquid @@ -0,0 +1,54 @@ +<!DOCTYPE html> +<html> + <head> + <title>404</title> + <style type="text/css"> + html, body { + margin: 0; + padding: 0; + font-family: "Helvetica", sans-serif; + background-image: url('http://okfoc.us/assets/images/photocopy.png'); + background-position: bottom center; + background-repeat: repeat; + background-attachment: scroll; + height: 100%; + font-size: 1.75em; + font-weight: bold; + color: #FFFFFF; + } + + a { + color: #8888FF; + text-decoration: none; + } + + a:hover { + border-bottom: 3px solid #8888FF; + } + + a:visited { + color: #8888FF; + } + + .message { + width: 700px; + padding: 0 1em 1em 1em; + background-color: #0000FF; + margin: 0 auto; + margin-top: 1em; + } + + .message p:first-child { + margin-top: 0; + } + </style> + </head> + <body> + <div class="message"> + <p>(;一_一)</p> + <p>Looks like we experienced an error.</p> + <p>Sorry about that. Maybe try again later.</p> + <a href="javascript:history.back()">Back</a> + </div> + </body> +</html> diff --git a/themes/okadmin/templates/index.liquid b/themes/okadmin/templates/index.liquid index 95c64dd..ebb1bde 100644 --- a/themes/okadmin/templates/index.liquid +++ b/themes/okadmin/templates/index.liquid @@ -1,25 +1,79 @@ {% include 'partials/head' %} +{% include 'partials/flash' %} + <section class="index main"> {% for pair in resources %} {% assign name = pair[0] %} {% assign resource = pair[1] %} - {% assign spec = resource.spec %} - <section class="resource-category {{name}}"> - <header> - <h2>{{name | capitalize}}</h2> - </header> - <ul class="resource-list"> - {% for data in resource.data %} - <li><a href="{{resource.type}}/{{data.id}}/">{{data.id}}</a></li> - {% endfor %} - </ul> - <footer> - <a class="add-new" href="{{resource.type}}/new/">+</a> - </footer> + <section class="resource-category root + {% if resource.groupBy %} grouped {% endif %} + {% if resource.descending %} descending {% endif %} + {{name}}"> + <form action="{{resource.type}}/__batch__/" method="POST"> + <header> + <h2>{{name | capitalize}}</h2> + </header> + <input type="hidden" name="_method" value="PUT"> + {% assign resourceJSON = resource.data[0][resource.groupBy] | stringify %} + {% if resource.groupBy and resourceJSON != "{}" %} + {% assign i = 0 %} + {% for item in resource.data %} + {% for pair in item[resource.groupBy] %} + {% assign group = pair[0] %} + {% assign members = pair[1] %} + <section class="resource-category {{group}}"> + <header> + <h2>{{group | capitalize}}</h2> + </header> + <ol class="resource-list"> + {% for data in members %} + <li> + {% if data.disabled %} <del> {% endif %} + <a href="{{resource.type}}/{{data.id}}/">{{data.title}}</a> + {% if data.disabled %} </del> {% endif %} + <input class="resource-input" type="hidden" name="{{resource.type}}[{{increment i}}]" + value='{{data | stringify | escape_once}}'> + </li> + {% endfor %} + </ol> + <footer> + <nav> + <a class="btn cancel-btn" href="#">cancel</a> + <button type="submit" + class="btn save-btn" href="#">save</button> + <a class="btn edit-btn active" href="#">sort</a> + <a class="btn add-btn active" href="{{resource.type}}/__new__/">+</a> + </nav> + </footer> + </section> + {% endfor %} + {% endfor %} + {% else %} + <ol class="resource-list"> + {% for data in resource.data %} + <li> + {% if data.disabled %} <del> {% endif %} + <a href="{{resource.type}}/{{data.id}}/">{{data.title}}</a> + {% if data.disabled %} </del> {% endif %} + <input class="resource-input" type="hidden" name="{{resource.type}}[{{forloop.index0}}]" + value='{{data | stringify | escape_once}}'> + </li> + {% endfor %} + </ol> + <footer> + <nav> + <a class="btn cancel-btn" href="#">cancel</a> + <button type="submit" + class="btn save-btn" href="#">save</button> + <a class="btn edit-btn active" href="#">sort</a> + <a class="btn add-btn active" href="{{resource.type}}/__new__/">+</a> + </nav> + </footer> + {% endif %} + </form> </section> - {% endfor %} </section> diff --git a/themes/okadmin/templates/partials/errors.liquid b/themes/okadmin/templates/partials/errors.liquid deleted file mode 100644 index cdb0b25..0000000 --- a/themes/okadmin/templates/partials/errors.liquid +++ /dev/null @@ -1,10 +0,0 @@ -<div class="errors"> - {% for error in errors %} - <div class="error"> - <div class="message">{{error.message}}</div> - <div class="assertion"> - Expected {{error.expected}} but got {{error.actual}} - </div> - </div> - {% endfor %} -</div> diff --git a/themes/okadmin/templates/partials/flash.liquid b/themes/okadmin/templates/partials/flash.liquid new file mode 100644 index 0000000..e51a86b --- /dev/null +++ b/themes/okadmin/templates/partials/flash.liquid @@ -0,0 +1,20 @@ +{% if success.length > 0 %} +<div class="success"> + <div class="message">Changes saved.</div> + <!-- + {% for info in success %} + <div class="message">{{info.action}}</div> + {% endfor %} + --> +</div> +{% endif %} + +{% if errors.length > 0 %} +<div class="errors"> + {% for error in errors %} + <div class="error"> + <div class="message">{{error.message}}</div> + </div> + {% endfor %} +</div> +{% endif %}
\ No newline at end of file diff --git a/themes/okadmin/templates/partials/head.liquid b/themes/okadmin/templates/partials/head.liquid index 3af59fd..e9c27dc 100644 --- a/themes/okadmin/templates/partials/head.liquid +++ b/themes/okadmin/templates/partials/head.liquid @@ -2,12 +2,12 @@ <html> <head> <meta charset="utf8"> - <title>{{meta.title}}</title> + <title>{{meta.project}} Admin</title> <link rel="stylesheet" href="{{meta.static}}/css/main.css"> </head> <body> <header class="admin-header"> - <span class="breadcrumb"><b>{{meta.title}}</b> Admin</span> + <span class="breadcrumb"><b>{{meta.project}}</b> Admin</span> <a class="site-link" href="/">View Site</a> </header> - <div class="container"> + <div class="container">
\ No newline at end of file diff --git a/themes/okadmin/templates/partials/inputs.liquid b/themes/okadmin/templates/partials/inputs.liquid index 7d23c9e..0fee61e 100644 --- a/themes/okadmin/templates/partials/inputs.liquid +++ b/themes/okadmin/templates/partials/inputs.liquid @@ -3,67 +3,349 @@ {% assign spec = pair[1] %} {% assign type = spec.type %} - <div class="property {{type}}"> + <div class="property {{type}} {% if spec.hidden %}hidden{% endif %}" + data-name="{{name}}"> <label for="{{name}}">{{name | capitalize}}</label> {% if type == 'string' %} <input - {% if spec.disabled %} - disabled="true" - {% endif %} - name="{{name}}" type="text" value="{{spec.value}}"> + name="{{name}}" type="text" value="{{spec.value | escape}}"> {% elsif type == 'text' %} <textarea - {% if spec.disabled %} - disabled="true" - {% endif %} - name="{{name}}">{{spec.value}}</textarea> - {% elsif type == 'enum' %} + name="{{name}}">{{spec.value | escape}}</textarea> + {% elsif type == 'number' %} + <input + type="number" + name="{{name}}" value="{{spec.value | escape}}"> + {% elsif type == 'enum' or type == 'foreign-key' %} <select - {% if spec.disabled %} - disabled="true" - {% endif %} name="{{name}}"> {% for option in spec.options %} - <option value="{{option}}" {% if option == spec.value %}selected{% endif %}>{{option}}</option> + <option value="{{option}}" {% if option == spec.value %}selected{% endif %}>{{option | capitalize}}</option> {% endfor %} </select> {% elsif type == 'video' %} <div class="video group {% if spec.value.url %}loaded{% endif %}"> <input name="{{name}}[url]" type="text" value="{{spec.value.url}}" class="url" placeholder="Enter a video URL"> - <input name="{{name}}[type]" type="text" value="{{spec.value.type}}" class="video-type" hidden> - <input name="{{name}}[token]" type="text" value="{{spec.value.token}}" class="video-token" hidden> + <input name="{{name}}[type]" type="hidden" value="{{spec.value.type}}" class="video-type" hidden> + <input name="{{name}}[token]" type="hidden" value="{{spec.value.token}}" class="video-token" hidden> + <input name="{{name}}[width]" value="{{spec.value.width}}" type="hidden" class="video-width" hidden> + <input name="{{name}}[height]" value="{{spec.value.height}}" type="hidden" class="video-height" hidden> <label>Title</label> - <input name="{{name}}[title]" type="text" value="{{spec.value.title}}" class="video-title"> + <input name="{{name}}[title]" type="text" value="{{spec.value.title | escape}}" class="video-title"> <label>Thumbnail</label> - <input name="{{name}}[thumb]" type="text" value="{{spec.value.thumb}}" class="video-thumb"> + <input name="{{name}}[thumb]" type="text" value="{{spec.value.thumb | escape}}" class="video-thumb"> + </div> + {% elsif type == 'image' %} + <div class="image group {% if spec.value.uri %}loaded{% endif %}"> + <div class="fields"> + <div class="add-image-button"> + <input type="file" accept="image/*"> + <button>+ Add image</button> + </div> + <input class="add-url" type="text" placeholder="+ Add URL"> + </div> + <div class="image-element"> + <input class="uri" type="hidden" name="{{name}}[uri]" value="{{spec.value.uri}}"> + <textarea class="caption" name="{{name}}[caption]">{{spec.value.caption | escape}}</textarea> + <input type="hidden" name="{{name}}[width]" value="{{spec.value.width}}" class="image-width"> + <input type="hidden" name="{{name}}[height]" value="{{spec.value.height}}" class="image-height"> + <img src="{{spec.value.uri}}" alt="{{spec.value.caption | escape}}"> + <button class="remove">x</button> + </div> + </div> + + {% elsif type == 'date' %} + + <div class="date"> + <input name="{{name}}" + type="date" + {% if spec.value %} + value="{{spec.value | date: '%Y-%m-%d'}}" + {% endif %} + > + </div> + + {% elsif type == 'flag' %} + + <div class="flag"> + <input name="{{name}}" + type="checkbox" + {% if spec.value %} + checked="true" + {% endif %} + value="true"> + </div> + + {% elsif type == 'tag-list' %} + <div class="tag-list"> + <input name="{{name}}" + value="{{spec.value | escape}}" + placeholder="Enter a comma separated list of tags."> </div> - {% elsif type == 'captioned-image-list' %} - <div class="image group loaded"> + + {% elsif type == 'link-list' %} + <div class="link-list group"> + <ol class="links"> + {% for link in spec.value %} + <li> + <div class="handle"></div> + <input + name="{{name}}[{{forloop.index0}}][text]" + value="{{link.text | escape}}" + type="text" + placeholder="Link text" + class="link-input link-text"> + <input + name="{{name}}[{{forloop.index0}}][uri]" + value="{{link.uri | escape}}" + type="text" + placeholder="URL" + class="link-input link-uri"> + <button class="remove-link-btn"> + - + </button> + </li> + {% endfor %} + </ol> + + <input type="text" + class="link-input-new link-text" + placeholder="Link text"> + <input type="text" + class="link-input-new link-uri" + placeholder="http://www.example.com"> + <button class="add-link-btn">+</button> + + <script type="text/html" class="link-template"> + <li> + <div class="handle"></div> + <input + name="{{name}}[][text]" + value="" + type="text" + placeholder="Link text" + class="link-input link-text"> + <input + name="{{name}}[][uri]" + value="" + type="text" + placeholder="URL" + class="link-input link-uri"> + <button class="remove-link-btn"> + - + </button> + </li> + </script> + + </div> + + {% elsif type == 'media-list' or type == 'media' %} + <div class="media-list group loaded"> + <div class="fields"> + <div class="add-image-button"> + <input type="file" accept="image/*,video/*,audio/*" multiple> + <button>+ Add media</button> + </div> + <input class="add-url" type="text" placeholder="+ Add Image/Video/Link URL"> + </div> + + <script type="text/html" class="image-template"> + <li class="image-element"> + <label>Caption</label> + <input class="uri" type="hidden" name="{{name}}[][uri]" value=""> + <textarea class="caption" name="{{name}}[][caption]"></textarea> + <input type="hidden" name="{{name}}[][type]" value="image"> + <input type="hidden" name="{{name}}[][width]" class="image-width" hidden> + <input type="hidden" name="{{name}}[][height]" class="image-height" hidden> + <img> + <button class="remove">x</button> + </li> + </script> + + <script type="text/html" class="video-template"> + <li class="video-element"> + <div style="float: left"> + <input name="{{name}}[][type]" type="hidden" class="video-type" hidden> + <input name="{{name}}[][token]" type="hidden" class="video-token" hidden> + <input name="{{name}}[][uri]" type="hidden" class="video-uri" hidden> + <input name="{{name}}[][width]" type="hidden" class="video-width" hidden> + <input name="{{name}}[][height]" type="hidden" class="video-height" hidden> + <label>Caption</label> + <input name="{{name}}[][title]" type="text" class="video-title"> + <label>Thumbnail</label> + <input name="{{name}}[][thumb]" type="text" class="video-thumb"> + <span class="checkboxes"> + <input name="{{name}}[][autoplay]" value="{{image.autoplay}}" type="checkbox" class="video-autoplay flag"> + <label>Autoplay</label> + <input name="{{name}}[][loop]" value="{{image.loop}}" type="checkbox" class="video-loop flag"> + <label>Loop</label> + </span> + </div> + <img> + <button class="remove">x</button> + </li> + </script> + + <script type="text/html" class="audio-template"> + <li class="audio-element"> + <div style="float: left"> + <input name="{{name}}[][type]" type="hidden" class="audio-type" hidden> + <input name="{{name}}[][token]" type="hidden" class="audio-token" hidden> + <input name="{{name}}[][uri]" type="hidden" class="audio-uri" hidden> + <input name="{{name}}[][duration]" value="{{image.duration}}" type="hidden" class="audio-duration" hidden> + <label>Caption</label> + <input name="{{name}}[][title]" type="text" class="audio-title"> + <label>Thumbnail</label> + <input name="{{name}}[][thumb]" type="text" class="audio-thumb"> + </div> + <img> + <button class="remove">x</button> + </li> + </script> + + <script type="text/html" class="link-template"> + <li class="link-element"> + <input class="uri" type="text" name="{{name}}[][uri]" value=""> + <textarea class="caption" name="{{name}}[][caption]" placeholder="Caption"></textarea> + <input type="hidden" name="{{name}}[][type]" value="link"> + <button class="remove">x</button> + </li> + </script> + + <ol> + {% for image in spec.value %} + {% if image.type and (image.type == "vimeo" or image.type == "youtube" or image.type == "video") %} + <li class="video-element"> + <div style="float: left"> + <input name="{{name}}[{{forloop.index0}}][type]" value="{{image.type}}" type="hidden" class="video-type" hidden> + <input name="{{name}}[{{forloop.index0}}][token]" value="{{image.token}}" type="hidden" class="video-token" hidden> + <input name="{{name}}[{{forloop.index0}}][uri]" value="{{image.uri}}" type="hidden" class="video-uri" hidden> + <input name="{{name}}[{{forloop.index0}}][width]" value="{{image.width}}" type="hidden" class="video-width" hidden> + <input name="{{name}}[{{forloop.index0}}][height]" value="{{image.height}}" type="hidden" class="video-height" hidden> + <label>Caption</label> + <input name="{{name}}[{{forloop.index0}}][title]" value="{{image.title | escape}}" type="text" class="video-title"> + <label>Thumbnail</label> + <input name="{{name}}[{{forloop.index0}}][thumb]" value="{{image.thumb}}" type="text" class="video-thumb"> + <span class="checkboxes"> + <input name="{{name}}[{{forloop.index0}}][autoplay]" value="true" {% if image.autoplay == "true" %}checked="true"{% endif %} type="checkbox" class="flag video-autoplay"> + <label>Autoplay</label> + <input name="{{name}}[{{forloop.index0}}][loop]" value="true" {% if image.loop == "true" %}checked="true"{% endif %} type="checkbox" class="flag video-loop"> + <label>Loop</label> + </span> + </div> + <img src="{{image.thumb}}"> + <button class="remove">x</button> + </li> + {% elsif image.type and (image.type == "audio" or image.type == "soundcloud") %} + <li class="audio-element"> + <div style="float: left"> + <input name="{{name}}[{{forloop.index0}}][type]" value="{{image.type}}" type="hidden" class="audio-type" hidden> + <input name="{{name}}[{{forloop.index0}}][token]" value="{{image.token}}" type="hidden" class="audio-token" hidden> + <input name="{{name}}[{{forloop.index0}}][uri]" value="{{image.uri}}" type="hidden" class="audio-uri" hidden> + <input name="{{name}}[{{forloop.index0}}][duration]" value="{{image.duration}}" type="hidden" class="audio-duration" hidden> + <label>Caption</label> + <input name="{{name}}[{{forloop.index0}}][title]" value="{{image.title | escape}}" type="text" class="audio-title"> + <label>Thumbnail</label> + <input name="{{name}}[{{forloop.index0}}][thumb]" value="{{image.thumb}}" type="text" class="audio-thumb"> + </div> + <img src="{{image.thumb}}"> + <button class="remove">x</button> + </li> + {% elsif image.type and image.type == "link" %} + <li class="link-element"> + <input class="uri" type="text" name="{{name}}[{{forloop.index0}}][uri]" value="{{image.uri}}"> + <textarea class="caption" name="{{name}}[{{forloop.index0}}][caption]" placeholder="Caption">{{image.caption | escape}}</textarea> + <input type="hidden" name="{{name}}[{{forloop.index0}}][type]" value="link"> + <button class="remove">x</button> + </li> + {% else %} + <li class="image-element"> + <label>Caption</label> + <input type="hidden" name="{{name}}[{{forloop.index0}}][uri]" value="{{image.uri}}"> + <input name="{{name}}[{{forloop.index0}}][width]" value="{{image.width}}" type="hidden" class="image-width" hidden> + <input name="{{name}}[{{forloop.index0}}][height]" value="{{image.height}}" type="hidden" class="image-height" hidden> + <textarea class="caption" name="{{name}}[{{forloop.index0}}][caption]">{{image.caption | escape}}</textarea> + <input type="hidden" name="{{name}}[{{forloop.index0}}][type]" value="image"> + <img src="{{image.uri}}" alt="{{image.caption | strip_html}}"> + <button class="remove">x</button> + </li> + {% endif %} + {% endfor %} + </ol> + </div> + {% elsif type == 'captioned-image-list' or type == 'gallery' %} + <div class="image-list group loaded"> + <div class="fields"> + <div class="add-image-button"> + <input type="file" accept="image/*" multiple> + <button>+ Add images</button> + </div> + <input class="add-url" type="text" placeholder="+ Add URL"> + </div> + + <script type="text/html" class="image-template"> + <li class="image-element"> + <input class="uri" type="hidden" name="{{name}}[][uri]" value=""> + <input type="hidden" name="{{name}}[][width]" class="image-width" hidden> + <input type="hidden" name="{{name}}[][height]" class="image-height" hidden> + <textarea class="caption" name="{{name}}[][caption]"></textarea> + <img> + <button class="remove">x</button> + </li> + </script> + <ol> {% for image in spec.value %} <li class="image-element"> - <input type="hidden" name="{{name}}[][uri]" value="{{image.uri}}"> - <textarea class="caption" name="{{name}}[][caption]">{{image.caption}}</textarea> - <img src="{{image.uri}}" alt="{{image.caption}}"> - <button class="remove-image">♲</button> + <input type="hidden" name="{{name}}[{{forloop.index0}}][uri]" value="{{image.uri}}"> + <input type="hidden" name="{{name}}[{{forloop.index0}}][width]" value="{{image.width}}" class="image-width"> + <input type="hidden" name="{{name}}[{{forloop.index0}}][height]" value="{{image.height}}" class="image-height"> + <textarea class="caption" name="{{name}}[{{forloop.index0}}][caption]">{{image.caption | escape}}</textarea> + <img src="{{image.uri}}" alt="{{image.caption | strip_html}}"> + <button class="remove">x</button> </li> {% endfor %} </ol> - <div class="add-image-button"> - <input id="file" type="file" accept="image/*" multiple> - <span>+ Add images</span> + </div> + {% elsif type == 'double-captioned-image-list' %} + <div class="image-list group loaded"> + <div class="fields"> + <div class="add-image-button"> + <input type="file" accept="image/*" multiple> + <button>+ Add images</button> + </div> + <input class="add-url" type="text" placeholder="+ Add URL"> </div> - <input id="add-image-url" type="text" placeholder="+ Add URL"> - <script type="text/html" id="captioned-image-template"> + + <script type="text/html" class="image-template"> <li class="image-element"> + <img> + <button class="remove">x</button> <input class="uri" type="hidden" name="{{name}}[][uri]" value=""> - <textarea class="caption" name="{{name}}[][caption]"></textarea> - <img alt="{{image.caption}}"> - <button class="remove-image">♲</button> + <input type="hidden" name="{{name}}[][width]" class="image-width"> + <input type="hidden" name="{{name}}[][height]" class="image-height"> + <input class="caption" name="{{name}}[][label]" placeholder="Name"> + <input class="caption" name="{{name}}[][caption]" placeholder="Email"> </li> </script> + + <ol> + {% for image in spec.value %} + <li class="image-element"> + <img src="{{image.uri}}" alt="{{image.caption | strip_html}}"> + <button class="remove">x</button> + <input type="hidden" name="{{name}}[{{forloop.index0}}][uri]" value="{{image.uri}}"> + <input type="hidden" name="{{name}}[{{forloop.index0}}][width]" value="{{image.width}}" class="image-width"> + <input type="hidden" name="{{name}}[{{forloop.index0}}][height]" value="{{image.height}}" class="image-height"> + <input class="caption" name="{{name}}[{{forloop.index0}}][label]" value="{{image.label | escape}}" placeholder="Name"> + <input class="caption" name="{{name}}[{{forloop.index0}}][caption]" value="{{image.caption | escape}}" placeholder="Email"> + </li> + {% endfor %} + </ol> </div> + {% elsif type == 'meta' %} + <input class="hidden" type="hidden" name="{{name}}" value="{{spec.value}}"> {% else %} <p><pre style="color: red">Admin template doesn't support '{{type}}' properties!</pre></p> {% endif %} diff --git a/themes/okadmin/templates/partials/tail.liquid b/themes/okadmin/templates/partials/tail.liquid index 88764a6..522023b 100644 --- a/themes/okadmin/templates/partials/tail.liquid +++ b/themes/okadmin/templates/partials/tail.liquid @@ -1,9 +1,14 @@ </div> {% comment %} closes container tag {% endcomment %} + <div id="progress"></div> + <div id="uploadConfig" + data-image-maxbytes="{{meta.services.s3.image.maxbytes}}" + data-audio-maxbytes="{{meta.services.s3.audio.maxbytes}}" + data-video-maxbytes="{{meta.services.s3.video.maxbytes}}"></div> </body> <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/3.6.0/lodash.min.js"></script> - <script src="/_admin/js/jqueryui-draggable.js"></script> - <script src="/_admin/js/upload.js"></script> - <script src="/_admin/js/parser.js"></script> - <script src="/_admin/js/app.js"></script> + <script src="/admin/js/jqueryui-draggable.js"></script> + <script src="/admin/js/upload.js"></script> + <script src="/admin/js/parser.js"></script> + <script src="/admin/js/app.js"></script> </html> diff --git a/themes/okadmin/templates/resource.liquid b/themes/okadmin/templates/resource.liquid index c0d348d..88f93bd 100644 --- a/themes/okadmin/templates/resource.liquid +++ b/themes/okadmin/templates/resource.liquid @@ -1,19 +1,25 @@ {% include 'partials/head' %} -{% include 'partials/errors' %} +{% include 'partials/flash' %} -<nav> - <a href="../..">Back</a> +<nav class="resource-nav"> + <a href="../..">Back</a><br> + <br> + <a href="/admin/{{ resource.type }}/__new__/">ADD ANOTHER</a> </nav> -<section class="resource main"> - <h2>EDIT {{ resource.type }} '{{ resource.id }}'</h2> - <form action="." method="POST"> +<section class="resource main" data-type="{{ resource.type }}" data-id="{{ resource.id }}"> + <h2>EDIT {{ resource.type }} '{{ resource.spec.title.value }}'</h2> + <form action="." method="POST" id="resource_form"> <input type="hidden" name="_method" value="PUT"> {% include 'partials/inputs' %} <label> </label><button type="submit">Save</button> <div class="clear"></div> </form> + <form action="." method="POST" id="delete_form"> + <input type="hidden" name="_method" value="DELETE"> + <button type="submit">Delete Record</button> + </form> </section> {% include 'partials/tail' %} diff --git a/themes/okadmin/templates/resource_new.liquid b/themes/okadmin/templates/resource_new.liquid index c57dd83..15d13ba 100644 --- a/themes/okadmin/templates/resource_new.liquid +++ b/themes/okadmin/templates/resource_new.liquid @@ -1,14 +1,14 @@ {% include 'partials/head' %} -{% include 'partials/errors' %} +{% include 'partials/flash' %} -<nav> +<nav class="resource-nav"> <a href="../..">Back</a> </nav> <section class="main resource resource-new"> <h2>NEW {{ resource.type }}</h2> - <form action=".." method="POST"> + <form action=".." method="POST" id="resource_form"> <input type="hidden" name="_method" value="POST"> {% include 'partials/inputs' %} <label> </label><button type="submit">Create</button> |
