summaryrefslogtreecommitdiff
path: root/themes/okadmin
diff options
context:
space:
mode:
Diffstat (limited to 'themes/okadmin')
-rw-r--r--themes/okadmin/public/css/main.css238
-rw-r--r--themes/okadmin/public/js/app.js346
-rw-r--r--themes/okadmin/public/js/parser.js49
-rw-r--r--themes/okadmin/public/js/upload.js225
-rw-r--r--themes/okadmin/templates/404.liquid54
-rw-r--r--themes/okadmin/templates/5xx.liquid54
-rw-r--r--themes/okadmin/templates/index.liquid82
-rw-r--r--themes/okadmin/templates/partials/errors.liquid10
-rw-r--r--themes/okadmin/templates/partials/flash.liquid20
-rw-r--r--themes/okadmin/templates/partials/head.liquid6
-rw-r--r--themes/okadmin/templates/partials/inputs.liquid346
-rw-r--r--themes/okadmin/templates/partials/tail.liquid13
-rw-r--r--themes/okadmin/templates/resource.liquid18
-rw-r--r--themes/okadmin/templates/resource_new.liquid6
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">&#9654;</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 = {
'&amp;color=ff6600&amp;auto_play=false&amp;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>&nbsp;</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>&nbsp;</label><button type="submit">Create</button>