diff --git a/src/Orchard.Web/Core/Orchard.Core.csproj b/src/Orchard.Web/Core/Orchard.Core.csproj index fce0058b5..977ea92a6 100644 --- a/src/Orchard.Web/Core/Orchard.Core.csproj +++ b/src/Orchard.Web/Core/Orchard.Core.csproj @@ -266,6 +266,7 @@ + diff --git a/src/Orchard.Web/Core/Shapes/ResourceManifest.cs b/src/Orchard.Web/Core/Shapes/ResourceManifest.cs index 656acaf5c..694539c5f 100644 --- a/src/Orchard.Web/Core/Shapes/ResourceManifest.cs +++ b/src/Orchard.Web/Core/Shapes/ResourceManifest.cs @@ -5,6 +5,7 @@ namespace Orchard.Core.Shapes { public void BuildManifests(ResourceManifestBuilder builder) { var manifest = builder.Add(); manifest.DefineScript("ShapesBase").SetUrl("base.js").SetDependencies("jQuery"); + manifest.DefineScript("OpenAjax").SetUrl("OpenAjax.js"); manifest.DefineStyle("Shapes").SetUrl("site.css"); // todo: missing manifest.DefineStyle("ShapesSpecial").SetUrl("special.css"); diff --git a/src/Orchard.Web/Core/Shapes/Scripts/OpenAjax.js b/src/Orchard.Web/Core/Shapes/Scripts/OpenAjax.js new file mode 100644 index 000000000..07efb1e77 --- /dev/null +++ b/src/Orchard.Web/Core/Shapes/Scripts/OpenAjax.js @@ -0,0 +1,191 @@ +/******************************************************************************* + * OpenAjax.js + * + * Reference implementation of the OpenAjax Hub, as specified by OpenAjax Alliance. + * Specification is under development at: + * + * http://www.openajax.org/member/wiki/OpenAjax_Hub_Specification + * + * Copyright 2006-2009 OpenAjax Alliance + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 . Unless + * required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + ******************************************************************************/ + +// prevent re-definition of the OpenAjax object +if(!window["OpenAjax"]){ + OpenAjax = new function(){ + var t = true; + var f = false; + var g = window; + var ooh = "org.openajax.hub."; + + var h = {}; + this.hub = h; + h.implementer = "http://openajax.org"; + h.implVersion = "2.0"; + h.specVersion = "2.0"; + h.implExtraData = {}; + var libs = {}; + h.libraries = libs; + + h.registerLibrary = function(prefix, nsURL, version, extra){ + libs[prefix] = { + prefix: prefix, + namespaceURI: nsURL, + version: version, + extraData: extra + }; + this.publish(ooh+"registerLibrary", libs[prefix]); + } + h.unregisterLibrary = function(prefix){ + this.publish(ooh+"unregisterLibrary", libs[prefix]); + delete libs[prefix]; + } + + h._subscriptions = { c:{}, s:[] }; + h._cleanup = []; + h._subIndex = 0; + h._pubDepth = 0; + + h.subscribe = function(name, callback, scope, subscriberData, filter) + { + if(!scope){ + scope = window; + } + var handle = name + "." + this._subIndex; + var sub = { scope: scope, cb: callback, fcb: filter, data: subscriberData, sid: this._subIndex++, hdl: handle }; + var path = name.split("."); + this._subscribe(this._subscriptions, path, 0, sub); + return handle; + } + + h.publish = function(name, message) + { + var path = name.split("."); + this._pubDepth++; + this._publish(this._subscriptions, path, 0, name, message); + this._pubDepth--; + if((this._cleanup.length > 0) && (this._pubDepth == 0)) { + for(var i = 0; i < this._cleanup.length; i++) + this.unsubscribe(this._cleanup[i].hdl); + delete(this._cleanup); + this._cleanup = []; + } + } + + h.unsubscribe = function(sub) + { + var path = sub.split("."); + var sid = path.pop(); + this._unsubscribe(this._subscriptions, path, 0, sid); + } + + h._subscribe = function(tree, path, index, sub) + { + var token = path[index]; + if(index == path.length) + tree.s.push(sub); + else { + if(typeof tree.c == "undefined") + tree.c = {}; + if(typeof tree.c[token] == "undefined") { + tree.c[token] = { c: {}, s: [] }; + this._subscribe(tree.c[token], path, index + 1, sub); + } + else + this._subscribe( tree.c[token], path, index + 1, sub); + } + } + + h._publish = function(tree, path, index, name, msg, pid) { + if(typeof tree != "undefined") { + var node; + if(index == path.length) { + node = tree; + } else { + this._publish(tree.c[path[index]], path, index + 1, name, msg, pid); + this._publish(tree.c["*"], path, index + 1, name, msg, pid); + node = tree.c["**"]; + } + if(typeof node != "undefined") { + var callbacks = node.s; + var max = callbacks.length; + for(var i = 0; i < max; i++) { + if(callbacks[i].cb) { + var sc = callbacks[i].scope; + var cb = callbacks[i].cb; + var fcb = callbacks[i].fcb; + var d = callbacks[i].data; + if(typeof cb == "string"){ + // get a function object + cb = sc[cb]; + } + if(typeof fcb == "string"){ + // get a function object + fcb = sc[fcb]; + } + if((!fcb) || (fcb.call(sc, name, msg, d))) { + cb.call(sc, name, msg, d, pid); + } + } + } + } + } + } + + h._unsubscribe = function(tree, path, index, sid) { + if(typeof tree != "undefined") { + if(index < path.length) { + var childNode = tree.c[path[index]]; + this._unsubscribe(childNode, path, index + 1, sid); + if(childNode.s.length == 0) { + for(var x in childNode.c) + return; + delete tree.c[path[index]]; + } + return; + } + else { + var callbacks = tree.s; + var max = callbacks.length; + for(var i = 0; i < max; i++) + if(sid == callbacks[i].sid) { + if(this._pubDepth > 0) { + callbacks[i].cb = null; + this._cleanup.push(callbacks[i]); + } + else + callbacks.splice(i, 1); + return; + } + } + } + } + // The following function is provided for automatic testing purposes. + // It is not expected to be deployed in run-time OpenAjax Hub implementations. + h.reinit = function() + { + for (var lib in OpenAjax.hub.libraries) { + delete OpenAjax.hub.libraries[lib]; + } + OpenAjax.hub.registerLibrary("OpenAjax", "http://openajax.org/hub", "1.0", {}); + + delete OpenAjax._subscriptions; + OpenAjax._subscriptions = {c:{},s:[]}; + delete OpenAjax._cleanup; + OpenAjax._cleanup = []; + OpenAjax._subIndex = 0; + OpenAjax._pubDepth = 0; + } + }; + // Register the OpenAjax Hub itself as a library. + OpenAjax.hub.registerLibrary("OpenAjax", "http://openajax.org/hub", "1.0", {}); + +} diff --git a/src/Orchard.Web/Modules/Orchard.MediaPicker/AdminFilter.cs b/src/Orchard.Web/Modules/Orchard.MediaPicker/AdminFilter.cs new file mode 100644 index 000000000..62b379218 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MediaPicker/AdminFilter.cs @@ -0,0 +1,26 @@ +using System; +using System.Web.Mvc; +using Orchard.Mvc.Filters; +using Orchard.UI.Resources; + +namespace Orchard.MediaPicker { + public class AdminFilter : FilterProvider, IResultFilter { + private readonly IResourceManager _resourceManager; + + public AdminFilter(IResourceManager resourceManager) { + _resourceManager = resourceManager; + } + + public void OnResultExecuting(ResultExecutingContext filterContext) { + // should only run on a full view rendering result + if (!(filterContext.Result is ViewResult) || !Orchard.UI.Admin.AdminFilter.IsApplied(filterContext.RequestContext)) + return; + _resourceManager.Require("script", "OpenAjax"); + _resourceManager.Require("script", "jQuery"); + _resourceManager.Include("script", "~/Modules/Orchard.MediaPicker/Scripts/MediaPicker.js", null); + } + + public void OnResultExecuted(ResultExecutedContext filterContext) { + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MediaPicker/Controllers/HomeController.cs b/src/Orchard.Web/Modules/Orchard.MediaPicker/Controllers/HomeController.cs new file mode 100644 index 000000000..f65e43524 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MediaPicker/Controllers/HomeController.cs @@ -0,0 +1,46 @@ +using System.Web.Mvc; +using Orchard.Localization; +using Orchard; +using System.Collections.Generic; +using Orchard.Media.Models; +using Orchard.Media.ViewModels; +using Orchard.Media.Services; +using Orchard.Security; +using Orchard.UI.Admin; +using Orchard.Themes; + +namespace Orchard.MediaPicker.Controllers { + [Themed(false)] + public class HomeController : Controller { + private readonly IMediaService _mediaService; + private readonly IAuthorizer _authorizer; + + public IOrchardServices Services { get; set; } + + public HomeController(IOrchardServices services, IMediaService mediaService, IAuthorizer authorizer) { + _authorizer = authorizer; + + Services = services; + _mediaService = mediaService; + + T = NullLocalizer.Instance; + } + + public Localizer T { get; set; } + + public ActionResult Index(string name, string mediaPath) { + // this controller should only be used from the admin panel. + // it is not itself an admincontroller, however, because it needs to be 'unthemed', + // which admincontrollers currently cannot be. + if (!_authorizer.Authorize(StandardPermissions.AccessAdminPanel, T("Can't access the admin"))) { + return new HttpUnauthorizedResult(); + } + + IEnumerable mediaFiles = _mediaService.GetMediaFiles(mediaPath); + IEnumerable mediaFolders = _mediaService.GetMediaFolders(mediaPath); + var model = new MediaFolderEditViewModel { FolderName = name, MediaFiles = mediaFiles, MediaFolders = mediaFolders, MediaPath = mediaPath }; + ViewData["Service"] = _mediaService; + return View(model); + } + } +} diff --git a/src/Orchard.Web/Modules/Orchard.MediaPicker/Module.txt b/src/Orchard.Web/Modules/Orchard.MediaPicker/Module.txt new file mode 100644 index 000000000..70b007878 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MediaPicker/Module.txt @@ -0,0 +1,13 @@ +Name: MediaPicker +AntiForgery: enabled +Author: The Orchard Team +Website: http://orchardproject.net +Version: 1.0 +OrchardVersion: 1.0 +Description: Description for the module +Features: + Orchard.MediaPicker: + Name: MediaPicker + Dependencies: Orchard.Media, Orchard.jQuery + Description: UI for browsing for, uploading, or selecting an image for an HTML editor. + Category: Input Editor \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MediaPicker/Orchard.MediaPicker.csproj b/src/Orchard.Web/Modules/Orchard.MediaPicker/Orchard.MediaPicker.csproj new file mode 100644 index 000000000..e2d3a3eaf --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MediaPicker/Orchard.MediaPicker.csproj @@ -0,0 +1,139 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {43D0EC0B-1955-4566-8D31-7B9102DA1703} + {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + Orchard.MediaPicker + Orchard.MediaPicker + v4.0 + false + + + 3.5 + + + + + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\ + TRACE + prompt + 4 + AllRules.ruleset + + + + + + + 3.5 + + + + False + ..\..\..\..\lib\aspnetmvc\System.Web.Mvc.dll + + + + + + + + + + + + + + + + + + + + + {2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6} + Orchard.Framework + + + {9916839C-39FC-4CEB-A5AF-89CA7E87119F} + Orchard.Core + + + {D9A7B330-CD22-4DA1-A95A-8DE1982AD8EB} + Orchard.Media + + + + + + + + + + + + + + + + + + + + + + + + $(ProjectDir)\..\Manifests + + + + + + + + + + + + False + True + 45979 + / + + + False + True + http://orchard.codeplex.com + False + + + + + \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MediaPicker/Properties/AssemblyInfo.cs b/src/Orchard.Web/Modules/Orchard.MediaPicker/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..9dd1bc09b --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MediaPicker/Properties/AssemblyInfo.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Orchard.MediaPicker")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("Orchard")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("d33b592c-133c-440b-8841-17e55eb08306")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Orchard.Web/Modules/Orchard.MediaPicker/Scripts/MediaBrowser.js b/src/Orchard.Web/Modules/Orchard.MediaPicker/Scripts/MediaBrowser.js new file mode 100644 index 000000000..0469532f9 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MediaPicker/Scripts/MediaBrowser.js @@ -0,0 +1,258 @@ +(function ($) { + var currentAspect = {}, + selectedItem, + suppressResize; + + $.extend({ + mediaPicker: { + init: function (data) { + // called by the opener to initiate dialog with existing image data. + // todo: data.img may contain existing image data, populate the fields + var img = data.img; + if (img) { + for (var name in img) { + $("#img-" + name).val(img[name]); + } + suppressResize = true; + $("#img-src").trigger("change"); + } + }, + uploadMedia: uploadMedia, + scalePreview: scalePreview + } + }); + + $("#img-cancel, #lib-cancel").live("click", function () { window.close(); }); + // when url changes, set the preview and loader src + $("#img-src").live("change", function () { + selectImage(getIdPrefix(this), this.value); + }); + $(".media-item").live("click", function () { + if (selectedItem) { + selectedItem.removeClass("selected"); + } + selectedItem = $(this); + selectedItem.addClass("selected"); + selectImage("#lib-", selectedItem.attr("data-imgsrc")); + }); + // maintain aspect ratio when width or height is changed + $("#img-width, #lib-width").live("change", fixAspectHeight); + $("#img-height, #lib-height").live("change", fixAspectWidth); + + $("#img-insert, #lib-insert").live("click", function () { + if ($(this).hasClass("disabled")) return; + publishInsertEvent(this); + }); + + $(function () { + $("#tabs").tabs({ selected: parseInt(location.hash.replace("#", "")) || 0 }); + + // populate width and height when image loads + // note: load event does not bubble so cannot be used with .live + $("#img-loader, #lib-loader").bind("load", syncImage); + + // edit mode has slightly different wording + // elements advertise this with data-edittext attributes, + // the edit text is the element's new val() unless -content is specified. + if (query("editmode") === "true") { + $("[data-edittext]").each(function () { + var self = $(this), + isContent = self.attr("data-edittext-content") === "true", + editText = self.attr("data-edittext"); + if (isContent) { + self.text(editText); + } + else { + self.attr("value", editText); + } + }); + } + + var data = window.mediaPickerData; + if (data) { + window.mediaPickerData = null; + $.mediaPicker.init(data); + } + }); + + function selectImage(prefix, src) { + $(prefix + "preview").width("").height("").attr("src", src); + $(prefix + "loader").attr("src", src); + $(prefix + "src").val(src); + + var disabled = src ? "" : "disabled"; + $(prefix + "insert").attr("disabled", disabled).toggleClass("disabled", !!disabled); + } + + function getIdPrefix(e) { + return "#" + e.id.substr(0, 4); + } + function publishInsertEvent(button) { + var prefix = getIdPrefix(button), + editorId = query("editorId"), + source = query("source"), + img = { + src: $(prefix + "src").val(), + alt: $(prefix + "alt").val(), + "class": $(prefix + "class").val(), + style: $(prefix + "style").val(), + align: $(prefix + "align").val(), + width: $(prefix + "width").val(), + height: $(prefix + "height").val() + }; + img.html = getImageHtml(img); + window.opener.OpenAjax.hub.publish("orchard.admin.pickimage-picked." + source, { + editorId: editorId, + img: img + }); + window.close(); + } + + function parseUnits(value) { + if (/\s*[0-9]+\s*(px)?\s*/i.test(value)) { + return parseInt(value); + } + return NaN; + } + + function fixAspectWidth() { + var prefix = getIdPrefix(this); + if (!$(prefix + "lock:checked").val()) return; + var height = parseUnits(this.value); + if (!isNaN(height)) { + $(prefix + "width").val(Math.round(height * currentAspect[prefix])); + } + } + + function fixAspectHeight() { + var prefix = getIdPrefix(this); + if (!$(prefix + "lock:checked").val()) return; + var width = parseUnits(this.value); + if (!isNaN(width)) { + $(prefix + "height").val(Math.round(width / currentAspect[prefix])); + } + } + + function scalePreview(img) { + // ensures the loaded image preview fits within the preview area + // by scaling it down if not. + var self = $(img), + width = self.width(), + height = self.height(), + aspect = width / height, + maxWidth = self.parent().width(), + maxHeight = self.parent().height(); + if (width > maxWidth) { + width = maxWidth; + height = Math.round(width / aspect); + } + if (height > maxHeight) { + height = maxHeight; + width = Math.round(width * aspect); + } + self.width(width).height(height); + } + + function syncImage() { + // when the image loader loads, we use it to calculate the current image + // aspect ratio, and update the width and height fields. + var prefix = getIdPrefix(this), + self = $(this), + width = self.width(), + height = self.height(); + currentAspect[prefix] = width / height; + // because we just loaded an edited image, leave the width/height + // at their configured values, not the natural size. + if (!suppressResize) { + $(prefix + "width").val(width); + $(prefix + "height").val(height); + } + suppressResize = false; + } + + function getAttr(name, value) { + // get an attribute value, escaping any necessary characters to html entities. + // not an exhastive list, but should cover all the necessary characters for this UI (e.g. you can't really put in newlines). + if (!value && name !== "alt") return ""; + return ' ' + name + '="' + value.replace(/&/g, "&").replace(/"/g, """).replace(//g, ">") + '"'; + } + + function getImageHtml(data) { + return html = '"; + } + + function uploadMedia(form) { + var name = "addmedia__" + (new Date()).getTime(); + $("", { + name: name, + src: "about:blank", + css: { display: "none" }, + load: iframeLoadHandler + }).appendTo(document.body); + form.target = name; + } + + // get a querystring value + function query(name) { + name = name.toLowerCase(); + var search = location.search; + var parts = search.replace("?", "").split("&"); + for (var i = 0, l = parts.length; i < l; i++) { + var part = parts[i]; + var eqIndex = part.indexOf("="); + if (eqIndex !== -1 && part.substr(0, eqIndex).toLowerCase() === name) { + return part.substr(eqIndex + 1); + } + } + return null; + } + + function iframeLoadHandler() { + try { + var self = $(this), + frame = window.frames[this.name]; + if (!frame.document || frame.document.URL == "about:blank") { + return true; + } + var result = frame.result; + if (result && result.url) { + // successfully uploaded image, response by setting the url + // to the new image. The change event will respond just as if + // the user typed the url. + $("#img-src").val(result.url).trigger("change"); + } + else if (result && result.error) { + alert(result.error); + } + else { + var somethingPotentiallyHorrible = ""; + try { + somethingPotentiallyHorrible = $("body", frame.document).html(); + } + catch (ex) { // some browsers flip out trying to access anything in the iframe when there's an error. + } + if (somethingPotentiallyHorrible) { + alert(somethingPotentiallyHorrible); + } + } + + //cleanup + window.setTimeout(function () { + self.remove(); + }, 123); + + } + catch (ex) { + alert(ex.message); + } + } + +})(jQuery); + + diff --git a/src/Orchard.Web/Modules/Orchard.MediaPicker/Scripts/MediaPicker.js b/src/Orchard.Web/Modules/Orchard.MediaPicker/Scripts/MediaPicker.js new file mode 100644 index 000000000..ecdbb69bc --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MediaPicker/Scripts/MediaPicker.js @@ -0,0 +1,40 @@ +(function () { + var eventPrefix = "orchard.admin.pickimage-open.", + pickerAction = "/MediaPicker", + defaultFeatures = "width=800,height=600,status=no,toolbar=no,location=no,menubar=no,resizable=no"; + + OpenAjax.hub.subscribe(eventPrefix + "*", function (name, data) { + var adminIndex = location.href.toLowerCase().indexOf("/admin/"); + if (adminIndex === -1) return; + var url = location.href.substr(0, adminIndex) + + pickerAction + "?source=" + name.substr(eventPrefix.length) + + "&upload=" + (data.uploadMediaAction || "") + + "&uploadpath=" + (data.uploadMediaPath || "") + + "&editmode=" + (!!(data.img && data.img.src)) + + "&editorId=" + data.editorId + "&" + (new Date() - 0); + var w = window.open(url, "_blank", data.windowFeatures || defaultFeatures); + if (w.jQuery && w.jQuery.mediaPicker) { + w.jQuery.mediaPicker.init(data); + } + else { + w.mediaPickerData = data; + } + }); +})(); + +//// Or, with jQuery +//(function ($) { +// $.orchardHub = $.orchardHub || {}; // this part would be built into the admin.js script or something + +// $($.orchardHub).bind("orchard-admin-pickimage", function(ev, data) { +// var adminIndex = location.href.toLowerCase().indexOf("/admin/"); +// if (adminIndex === -1) return; +// var url = location.href.substr(0, adminIndex) +// + "/Orchard.MediaPicker/MediaPicker/Index?source=" + data.source +// + "&upload=" + data.uploadMediaAction +// + "&editorId=" + data.editorId + "&" + (new Date() - 0); +// var w = window.open(url, "Orchard.MediaPicker", data.windowFeatures || "width=600,height=300,status=no,toolbar=no,location=no,menubar=no"); +// // in case it was already open, bring to the fore +// w.focus(); +// }); +//})(jQuery); diff --git a/src/Orchard.Web/Modules/Orchard.MediaPicker/Scripts/Web.config b/src/Orchard.Web/Modules/Orchard.MediaPicker/Scripts/Web.config new file mode 100644 index 000000000..178ff35ba --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MediaPicker/Scripts/Web.config @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/Orchard.Web/Modules/Orchard.MediaPicker/Styles/Web.config b/src/Orchard.Web/Modules/Orchard.MediaPicker/Styles/Web.config new file mode 100644 index 000000000..178ff35ba --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MediaPicker/Styles/Web.config @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/Orchard.Web/Modules/Orchard.MediaPicker/Styles/mediapicker.css b/src/Orchard.Web/Modules/Orchard.MediaPicker/Styles/mediapicker.css new file mode 100644 index 000000000..a9b3d5c3c --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MediaPicker/Styles/mediapicker.css @@ -0,0 +1,25 @@ +input[disabled], input[disabled="disabled"], input.disabled { + color: White +} +input[type="file"] { + border: 1px solid black; +} +.media-largepreview +{ + width: 300px; + height: 300px; +} +.media-thumbnail +{ + width: 200px; + height: 200px; +} +.media-thumbnail img +{ + text-align: center; + vertical-align: middle; +} +.selected +{ + background-color: Yellow; +} diff --git a/src/Orchard.Web/Modules/Orchard.MediaPicker/Views/Index.cshtml b/src/Orchard.Web/Modules/Orchard.MediaPicker/Views/Index.cshtml new file mode 100644 index 000000000..fcc07105f --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MediaPicker/Views/Index.cshtml @@ -0,0 +1,46 @@ +@model Orchard.Media.ViewModels.MediaFolderEditViewModel +@using Orchard.Media.Extensions; +@using Orchard.Media.Helpers; +@using Orchard.Media.Services; +@using Orchard.Media.Models; +@using Orchard.UI.Resources; +@{ + Style.Include("~/themes/theadmin/styles/site.css"); // todo: and the IE conditional ones + Style.Include("~/themes/theadmin/styles/ie.css").UseCondition("lte IE 8").Define(d => d.SetAttribute("media", "screen, projection")); + Style.Include("~/themes/theadmin/styles/ie6.css").UseCondition("lte IE 6").Define(d => d.SetAttribute("media", "screen, projection")); + Style.Include("mediapicker.css"); + + // these need to be in the head because MediaBrowser.js defines a callback that the thumbnail images call when they load, + // which could happen as soon as they render. + Style.Require("jQueryUI_Orchard").AtHead(); + Script.Require("jQueryUI_Tabs").AtHead(); + Script.Include("MediaBrowser.js").AtHead(); +} + + + + @T("Pick Image") + @Display.Metas() + @Display.HeadScripts() + @Display.HeadLinks() + @Display.StyleSheetLinks() + + + + + + @T("Insert/Upload Image") + @T("Browse Gallery") + + +@Html.Partial("Tab_Url", Model) + + +@Html.Partial("Tab_Gallery", Model) + + + + +@Display.FootScripts() + + diff --git a/src/Orchard.Web/Modules/Orchard.MediaPicker/Views/Tab_Gallery.cshtml b/src/Orchard.Web/Modules/Orchard.MediaPicker/Views/Tab_Gallery.cshtml new file mode 100644 index 000000000..278daa067 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MediaPicker/Views/Tab_Gallery.cshtml @@ -0,0 +1,109 @@ +@model Orchard.Media.ViewModels.MediaFolderEditViewModel +@using Orchard.Media.Extensions; +@using Orchard.Media.Helpers; +@using Orchard.Media.Services; +@using Orchard.Media.Models; +@using Orchard.UI.Resources; +@helper FolderLink(string folderName, string mediaPath) { + // querystring values need to persist after a new GET when clicking on the media browser's + // folders for navigation. + @Html.ActionLink(folderName, "Index", null, null, null, "1", new { + upload = Request["upload"], + uploadpath = Request["uploadpath"], + editmode = Request["editmode"], + source = Request["source"], + editorId = Request["editorId"], + name = folderName, + mediaPath = mediaPath }, null); +} + + + + @FolderLink(T("Media Folders").ToString(), "") + @foreach (var navigation in MediaHelpers.GetFolderNavigationHierarchy(Model.MediaPath)) { + > @FolderLink(navigation.FolderName, navigation.FolderPath) + } + + + + + @foreach (var mediaFolder in Model.MediaFolders) { + + + + + + @FolderLink(mediaFolder.Name, mediaFolder.MediaPath) + + + } + @foreach (var mediaFile in Model.MediaFiles) { + var src = (((IMediaService)ViewData["Service"]).GetPublicUrl(Path.Combine(Model.MediaPath, mediaFile.Name))); + + + + + + + + + @mediaFile.Name + @T("Added on"): @mediaFile.LastUpdated + @T("Size"): @mediaFile.Size.ToFriendlySizeString() + + + + } + + + + + + + + @T("URL") + + + @T("Alternative Text") + + + @T("Class") + + + @T("Style") + + + @T("Alignment") + + None + Left + Right + Top + Text Top + Middle + AbsMiddle + Bottom + AbsBottom + Baseline + + + @T("Width") + + X + @T("Height") + + + + @T("Lock Aspect Ratio") + + + + + + + + + + + + diff --git a/src/Orchard.Web/Modules/Orchard.MediaPicker/Views/Tab_Url.cshtml b/src/Orchard.Web/Modules/Orchard.MediaPicker/Views/Tab_Url.cshtml new file mode 100644 index 000000000..30e13f10c --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MediaPicker/Views/Tab_Url.cshtml @@ -0,0 +1,74 @@ +@model Orchard.Media.ViewModels.MediaFolderEditViewModel +@using Orchard.Media.Extensions; +@using Orchard.Media.Helpers; +@using Orchard.Media.Services; +@using Orchard.Media.Models; +@using Orchard.UI.Resources; +@{ + // only allow uploading to a local url + var uploadAction = Request["upload"]; + if (!Url.IsLocalUrl(uploadAction)) { + uploadAction = ""; + } + var mediaPath = Request["uploadpath"]; + if (!Url.IsLocalUrl(mediaPath)) { + mediaPath = ""; + } +} + + + + + + + @T("URL for the image resource") + + + + @using(Html.BeginFormAntiForgeryPost(uploadAction, FormMethod.Post, new { enctype = "multipart/form-data", onsubmit="jQuery.mediaPicker.uploadMedia(this)"})) { + + @T("Upload an image from your computer") + + + } + + + + + + @T("Alternative Text") + + + @T("Class") + + + @T("Style") + + + @T("Alignment") + + None + Left + Right + Top + Text Top + Middle + AbsMiddle + Bottom + AbsBottom + Baseline + + + @T("Width") + + X + @T("Height") + + + + @T("Lock Aspect Ratio") + + + + + diff --git a/src/Orchard.Web/Modules/Orchard.MediaPicker/Views/Web.config b/src/Orchard.Web/Modules/Orchard.MediaPicker/Views/Web.config new file mode 100644 index 000000000..b7d215131 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MediaPicker/Views/Web.config @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs b/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs index 50718f08b..b4864cf79 100644 --- a/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs +++ b/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs @@ -93,6 +93,7 @@ namespace Orchard.Setup.Services { "Orchard.jQuery", "Orchard.Lists", "Orchard.Media", + "Orchard.MediaPicker", "Orchard.Modules", "Orchard.Pages", "Orchard.Roles", diff --git a/src/Orchard.Web/Modules/Orchard.jQuery/Orchard.jQuery.csproj b/src/Orchard.Web/Modules/Orchard.jQuery/Orchard.jQuery.csproj index ce9fa2124..96f4d68d3 100644 --- a/src/Orchard.Web/Modules/Orchard.jQuery/Orchard.jQuery.csproj +++ b/src/Orchard.Web/Modules/Orchard.jQuery/Orchard.jQuery.csproj @@ -50,6 +50,8 @@ + + diff --git a/src/Orchard.Web/Modules/Orchard.jQuery/ResourceManifest.cs b/src/Orchard.Web/Modules/Orchard.jQuery/ResourceManifest.cs index 9b8948a2a..86acdd41f 100644 --- a/src/Orchard.Web/Modules/Orchard.jQuery/ResourceManifest.cs +++ b/src/Orchard.Web/Modules/Orchard.jQuery/ResourceManifest.cs @@ -8,6 +8,7 @@ namespace Orchard.UI.Resources { manifest.DefineScript("jQueryUtils").SetUrl("jquery.utils.js").SetDependencies("jQuery"); manifest.DefineScript("jQueryUI_Core").SetUrl("jquery.ui.core.js").SetVersion("1.8b1").SetDependencies("jQuery"); manifest.DefineScript("jQueryUI_Widget").SetUrl("jquery.ui.widget.js").SetVersion("1.8b1").SetDependencies("jQuery"); + manifest.DefineScript("jQueryUI_Tabs").SetUrl("jquery.ui.tabs.min.js", "jquery.ui.tabs.js").SetVersion("1.8.9").SetDependencies("jQuery", "jQueryUI_Core", "jQueryUI_Widget"); manifest.DefineScript("jQueryUI_DatePicker").SetUrl("jquery.ui.datepicker.js").SetVersion("1.8b1").SetDependencies("jQueryUI_Core", "jQueryUI_Widget"); manifest.DefineScript("jQueryUtils_TimePicker").SetUrl("ui.timepickr.js").SetVersion("0.7.0a").SetDependencies("jQueryUtils", "jQueryUI_Core", "jQueryUI_Widget"); diff --git a/src/Orchard.Web/Modules/Orchard.jQuery/Scripts/jquery.ui.tabs.js b/src/Orchard.Web/Modules/Orchard.jQuery/Scripts/jquery.ui.tabs.js new file mode 100644 index 000000000..be9db328d --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.jQuery/Scripts/jquery.ui.tabs.js @@ -0,0 +1,758 @@ +/* + * jQuery UI Tabs 1.8.9 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Tabs + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + */ +(function( $, undefined ) { + +var tabId = 0, + listId = 0; + +function getNextTabId() { + return ++tabId; +} + +function getNextListId() { + return ++listId; +} + +$.widget( "ui.tabs", { + options: { + add: null, + ajaxOptions: null, + cache: false, + cookie: null, // e.g. { expires: 7, path: '/', domain: 'jquery.com', secure: true } + collapsible: false, + disable: null, + disabled: [], + enable: null, + event: "click", + fx: null, // e.g. { height: 'toggle', opacity: 'toggle', duration: 200 } + idPrefix: "ui-tabs-", + load: null, + panelTemplate: "", + remove: null, + select: null, + show: null, + spinner: "Loading…", + tabTemplate: "#{label}" + }, + + _create: function() { + this._tabify( true ); + }, + + _setOption: function( key, value ) { + if ( key == "selected" ) { + if (this.options.collapsible && value == this.options.selected ) { + return; + } + this.select( value ); + } else { + this.options[ key ] = value; + this._tabify(); + } + }, + + _tabId: function( a ) { + return a.title && a.title.replace( /\s/g, "_" ).replace( /[^\w\u00c0-\uFFFF-]/g, "" ) || + this.options.idPrefix + getNextTabId(); + }, + + _sanitizeSelector: function( hash ) { + // we need this because an id may contain a ":" + return hash.replace( /:/g, "\\:" ); + }, + + _cookie: function() { + var cookie = this.cookie || + ( this.cookie = this.options.cookie.name || "ui-tabs-" + getNextListId() ); + return $.cookie.apply( null, [ cookie ].concat( $.makeArray( arguments ) ) ); + }, + + _ui: function( tab, panel ) { + return { + tab: tab, + panel: panel, + index: this.anchors.index( tab ) + }; + }, + + _cleanup: function() { + // restore all former loading tabs labels + this.lis.filter( ".ui-state-processing" ) + .removeClass( "ui-state-processing" ) + .find( "span:data(label.tabs)" ) + .each(function() { + var el = $( this ); + el.html( el.data( "label.tabs" ) ).removeData( "label.tabs" ); + }); + }, + + _tabify: function( init ) { + var self = this, + o = this.options, + fragmentId = /^#.+/; // Safari 2 reports '#' for an empty hash + + this.list = this.element.find( "ol,ul" ).eq( 0 ); + this.lis = $( " > li:has(a[href])", this.list ); + this.anchors = this.lis.map(function() { + return $( "a", this )[ 0 ]; + }); + this.panels = $( [] ); + + this.anchors.each(function( i, a ) { + var href = $( a ).attr( "href" ); + // For dynamically created HTML that contains a hash as href IE < 8 expands + // such href to the full page url with hash and then misinterprets tab as ajax. + // Same consideration applies for an added tab with a fragment identifier + // since a[href=#fragment-identifier] does unexpectedly not match. + // Thus normalize href attribute... + var hrefBase = href.split( "#" )[ 0 ], + baseEl; + if ( hrefBase && ( hrefBase === location.toString().split( "#" )[ 0 ] || + ( baseEl = $( "base" )[ 0 ]) && hrefBase === baseEl.href ) ) { + href = a.hash; + a.href = href; + } + + // inline tab + if ( fragmentId.test( href ) ) { + self.panels = self.panels.add( self.element.find( self._sanitizeSelector( href ) ) ); + // remote tab + // prevent loading the page itself if href is just "#" + } else if ( href && href !== "#" ) { + // required for restore on destroy + $.data( a, "href.tabs", href ); + + // TODO until #3808 is fixed strip fragment identifier from url + // (IE fails to load from such url) + $.data( a, "load.tabs", href.replace( /#.*$/, "" ) ); + + var id = self._tabId( a ); + a.href = "#" + id; + var $panel = self.element.find( "#" + id ); + if ( !$panel.length ) { + $panel = $( o.panelTemplate ) + .attr( "id", id ) + .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" ) + .insertAfter( self.panels[ i - 1 ] || self.list ); + $panel.data( "destroy.tabs", true ); + } + self.panels = self.panels.add( $panel ); + // invalid tab href + } else { + o.disabled.push( i ); + } + }); + + // initialization from scratch + if ( init ) { + // attach necessary classes for styling + this.element.addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" ); + this.list.addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" ); + this.lis.addClass( "ui-state-default ui-corner-top" ); + this.panels.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" ); + + // Selected tab + // use "selected" option or try to retrieve: + // 1. from fragment identifier in url + // 2. from cookie + // 3. from selected class attribute on + if ( o.selected === undefined ) { + if ( location.hash ) { + this.anchors.each(function( i, a ) { + if ( a.hash == location.hash ) { + o.selected = i; + return false; + } + }); + } + if ( typeof o.selected !== "number" && o.cookie ) { + o.selected = parseInt( self._cookie(), 10 ); + } + if ( typeof o.selected !== "number" && this.lis.filter( ".ui-tabs-selected" ).length ) { + o.selected = this.lis.index( this.lis.filter( ".ui-tabs-selected" ) ); + } + o.selected = o.selected || ( this.lis.length ? 0 : -1 ); + } else if ( o.selected === null ) { // usage of null is deprecated, TODO remove in next release + o.selected = -1; + } + + // sanity check - default to first tab... + o.selected = ( ( o.selected >= 0 && this.anchors[ o.selected ] ) || o.selected < 0 ) + ? o.selected + : 0; + + // Take disabling tabs via class attribute from HTML + // into account and update option properly. + // A selected tab cannot become disabled. + o.disabled = $.unique( o.disabled.concat( + $.map( this.lis.filter( ".ui-state-disabled" ), function( n, i ) { + return self.lis.index( n ); + }) + ) ).sort(); + + if ( $.inArray( o.selected, o.disabled ) != -1 ) { + o.disabled.splice( $.inArray( o.selected, o.disabled ), 1 ); + } + + // highlight selected tab + this.panels.addClass( "ui-tabs-hide" ); + this.lis.removeClass( "ui-tabs-selected ui-state-active" ); + // check for length avoids error when initializing empty list + if ( o.selected >= 0 && this.anchors.length ) { + self.element.find( self._sanitizeSelector( self.anchors[ o.selected ].hash ) ).removeClass( "ui-tabs-hide" ); + this.lis.eq( o.selected ).addClass( "ui-tabs-selected ui-state-active" ); + + // seems to be expected behavior that the show callback is fired + self.element.queue( "tabs", function() { + self._trigger( "show", null, + self._ui( self.anchors[ o.selected ], self.element.find( self._sanitizeSelector( self.anchors[ o.selected ].hash ) )[ 0 ] ) ); + }); + + this.load( o.selected ); + } + + // clean up to avoid memory leaks in certain versions of IE 6 + // TODO: namespace this event + $( window ).bind( "unload", function() { + self.lis.add( self.anchors ).unbind( ".tabs" ); + self.lis = self.anchors = self.panels = null; + }); + // update selected after add/remove + } else { + o.selected = this.lis.index( this.lis.filter( ".ui-tabs-selected" ) ); + } + + // update collapsible + // TODO: use .toggleClass() + this.element[ o.collapsible ? "addClass" : "removeClass" ]( "ui-tabs-collapsible" ); + + // set or update cookie after init and add/remove respectively + if ( o.cookie ) { + this._cookie( o.selected, o.cookie ); + } + + // disable tabs + for ( var i = 0, li; ( li = this.lis[ i ] ); i++ ) { + $( li )[ $.inArray( i, o.disabled ) != -1 && + // TODO: use .toggleClass() + !$( li ).hasClass( "ui-tabs-selected" ) ? "addClass" : "removeClass" ]( "ui-state-disabled" ); + } + + // reset cache if switching from cached to not cached + if ( o.cache === false ) { + this.anchors.removeData( "cache.tabs" ); + } + + // remove all handlers before, tabify may run on existing tabs after add or option change + this.lis.add( this.anchors ).unbind( ".tabs" ); + + if ( o.event !== "mouseover" ) { + var addState = function( state, el ) { + if ( el.is( ":not(.ui-state-disabled)" ) ) { + el.addClass( "ui-state-" + state ); + } + }; + var removeState = function( state, el ) { + el.removeClass( "ui-state-" + state ); + }; + this.lis.bind( "mouseover.tabs" , function() { + addState( "hover", $( this ) ); + }); + this.lis.bind( "mouseout.tabs", function() { + removeState( "hover", $( this ) ); + }); + this.anchors.bind( "focus.tabs", function() { + addState( "focus", $( this ).closest( "li" ) ); + }); + this.anchors.bind( "blur.tabs", function() { + removeState( "focus", $( this ).closest( "li" ) ); + }); + } + + // set up animations + var hideFx, showFx; + if ( o.fx ) { + if ( $.isArray( o.fx ) ) { + hideFx = o.fx[ 0 ]; + showFx = o.fx[ 1 ]; + } else { + hideFx = showFx = o.fx; + } + } + + // Reset certain styles left over from animation + // and prevent IE's ClearType bug... + function resetStyle( $el, fx ) { + $el.css( "display", "" ); + if ( !$.support.opacity && fx.opacity ) { + $el[ 0 ].style.removeAttribute( "filter" ); + } + } + + // Show a tab... + var showTab = showFx + ? function( clicked, $show ) { + $( clicked ).closest( "li" ).addClass( "ui-tabs-selected ui-state-active" ); + $show.hide().removeClass( "ui-tabs-hide" ) // avoid flicker that way + .animate( showFx, showFx.duration || "normal", function() { + resetStyle( $show, showFx ); + self._trigger( "show", null, self._ui( clicked, $show[ 0 ] ) ); + }); + } + : function( clicked, $show ) { + $( clicked ).closest( "li" ).addClass( "ui-tabs-selected ui-state-active" ); + $show.removeClass( "ui-tabs-hide" ); + self._trigger( "show", null, self._ui( clicked, $show[ 0 ] ) ); + }; + + // Hide a tab, $show is optional... + var hideTab = hideFx + ? function( clicked, $hide ) { + $hide.animate( hideFx, hideFx.duration || "normal", function() { + self.lis.removeClass( "ui-tabs-selected ui-state-active" ); + $hide.addClass( "ui-tabs-hide" ); + resetStyle( $hide, hideFx ); + self.element.dequeue( "tabs" ); + }); + } + : function( clicked, $hide, $show ) { + self.lis.removeClass( "ui-tabs-selected ui-state-active" ); + $hide.addClass( "ui-tabs-hide" ); + self.element.dequeue( "tabs" ); + }; + + // attach tab event handler, unbind to avoid duplicates from former tabifying... + this.anchors.bind( o.event + ".tabs", function() { + var el = this, + $li = $(el).closest( "li" ), + $hide = self.panels.filter( ":not(.ui-tabs-hide)" ), + $show = self.element.find( self._sanitizeSelector( el.hash ) ); + + // If tab is already selected and not collapsible or tab disabled or + // or is already loading or click callback returns false stop here. + // Check if click handler returns false last so that it is not executed + // for a disabled or loading tab! + if ( ( $li.hasClass( "ui-tabs-selected" ) && !o.collapsible) || + $li.hasClass( "ui-state-disabled" ) || + $li.hasClass( "ui-state-processing" ) || + self.panels.filter( ":animated" ).length || + self._trigger( "select", null, self._ui( this, $show[ 0 ] ) ) === false ) { + this.blur(); + return false; + } + + o.selected = self.anchors.index( this ); + + self.abort(); + + // if tab may be closed + if ( o.collapsible ) { + if ( $li.hasClass( "ui-tabs-selected" ) ) { + o.selected = -1; + + if ( o.cookie ) { + self._cookie( o.selected, o.cookie ); + } + + self.element.queue( "tabs", function() { + hideTab( el, $hide ); + }).dequeue( "tabs" ); + + this.blur(); + return false; + } else if ( !$hide.length ) { + if ( o.cookie ) { + self._cookie( o.selected, o.cookie ); + } + + self.element.queue( "tabs", function() { + showTab( el, $show ); + }); + + // TODO make passing in node possible, see also http://dev.jqueryui.com/ticket/3171 + self.load( self.anchors.index( this ) ); + + this.blur(); + return false; + } + } + + if ( o.cookie ) { + self._cookie( o.selected, o.cookie ); + } + + // show new tab + if ( $show.length ) { + if ( $hide.length ) { + self.element.queue( "tabs", function() { + hideTab( el, $hide ); + }); + } + self.element.queue( "tabs", function() { + showTab( el, $show ); + }); + + self.load( self.anchors.index( this ) ); + } else { + throw "jQuery UI Tabs: Mismatching fragment identifier."; + } + + // Prevent IE from keeping other link focussed when using the back button + // and remove dotted border from clicked link. This is controlled via CSS + // in modern browsers; blur() removes focus from address bar in Firefox + // which can become a usability and annoying problem with tabs('rotate'). + if ( $.browser.msie ) { + this.blur(); + } + }); + + // disable click in any case + this.anchors.bind( "click.tabs", function(){ + return false; + }); + }, + + _getIndex: function( index ) { + // meta-function to give users option to provide a href string instead of a numerical index. + // also sanitizes numerical indexes to valid values. + if ( typeof index == "string" ) { + index = this.anchors.index( this.anchors.filter( "[href$=" + index + "]" ) ); + } + + return index; + }, + + destroy: function() { + var o = this.options; + + this.abort(); + + this.element + .unbind( ".tabs" ) + .removeClass( "ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible" ) + .removeData( "tabs" ); + + this.list.removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" ); + + this.anchors.each(function() { + var href = $.data( this, "href.tabs" ); + if ( href ) { + this.href = href; + } + var $this = $( this ).unbind( ".tabs" ); + $.each( [ "href", "load", "cache" ], function( i, prefix ) { + $this.removeData( prefix + ".tabs" ); + }); + }); + + this.lis.unbind( ".tabs" ).add( this.panels ).each(function() { + if ( $.data( this, "destroy.tabs" ) ) { + $( this ).remove(); + } else { + $( this ).removeClass([ + "ui-state-default", + "ui-corner-top", + "ui-tabs-selected", + "ui-state-active", + "ui-state-hover", + "ui-state-focus", + "ui-state-disabled", + "ui-tabs-panel", + "ui-widget-content", + "ui-corner-bottom", + "ui-tabs-hide" + ].join( " " ) ); + } + }); + + if ( o.cookie ) { + this._cookie( null, o.cookie ); + } + + return this; + }, + + add: function( url, label, index ) { + if ( index === undefined ) { + index = this.anchors.length; + } + + var self = this, + o = this.options, + $li = $( o.tabTemplate.replace( /#\{href\}/g, url ).replace( /#\{label\}/g, label ) ), + id = !url.indexOf( "#" ) ? url.replace( "#", "" ) : this._tabId( $( "a", $li )[ 0 ] ); + + $li.addClass( "ui-state-default ui-corner-top" ).data( "destroy.tabs", true ); + + // try to find an existing element before creating a new one + var $panel = self.element.find( "#" + id ); + if ( !$panel.length ) { + $panel = $( o.panelTemplate ) + .attr( "id", id ) + .data( "destroy.tabs", true ); + } + $panel.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide" ); + + if ( index >= this.lis.length ) { + $li.appendTo( this.list ); + $panel.appendTo( this.list[ 0 ].parentNode ); + } else { + $li.insertBefore( this.lis[ index ] ); + $panel.insertBefore( this.panels[ index ] ); + } + + o.disabled = $.map( o.disabled, function( n, i ) { + return n >= index ? ++n : n; + }); + + this._tabify(); + + if ( this.anchors.length == 1 ) { + o.selected = 0; + $li.addClass( "ui-tabs-selected ui-state-active" ); + $panel.removeClass( "ui-tabs-hide" ); + this.element.queue( "tabs", function() { + self._trigger( "show", null, self._ui( self.anchors[ 0 ], self.panels[ 0 ] ) ); + }); + + this.load( 0 ); + } + + this._trigger( "add", null, this._ui( this.anchors[ index ], this.panels[ index ] ) ); + return this; + }, + + remove: function( index ) { + index = this._getIndex( index ); + var o = this.options, + $li = this.lis.eq( index ).remove(), + $panel = this.panels.eq( index ).remove(); + + // If selected tab was removed focus tab to the right or + // in case the last tab was removed the tab to the left. + if ( $li.hasClass( "ui-tabs-selected" ) && this.anchors.length > 1) { + this.select( index + ( index + 1 < this.anchors.length ? 1 : -1 ) ); + } + + o.disabled = $.map( + $.grep( o.disabled, function(n, i) { + return n != index; + }), + function( n, i ) { + return n >= index ? --n : n; + }); + + this._tabify(); + + this._trigger( "remove", null, this._ui( $li.find( "a" )[ 0 ], $panel[ 0 ] ) ); + return this; + }, + + enable: function( index ) { + index = this._getIndex( index ); + var o = this.options; + if ( $.inArray( index, o.disabled ) == -1 ) { + return; + } + + this.lis.eq( index ).removeClass( "ui-state-disabled" ); + o.disabled = $.grep( o.disabled, function( n, i ) { + return n != index; + }); + + this._trigger( "enable", null, this._ui( this.anchors[ index ], this.panels[ index ] ) ); + return this; + }, + + disable: function( index ) { + index = this._getIndex( index ); + var self = this, o = this.options; + // cannot disable already selected tab + if ( index != o.selected ) { + this.lis.eq( index ).addClass( "ui-state-disabled" ); + + o.disabled.push( index ); + o.disabled.sort(); + + this._trigger( "disable", null, this._ui( this.anchors[ index ], this.panels[ index ] ) ); + } + + return this; + }, + + select: function( index ) { + index = this._getIndex( index ); + if ( index == -1 ) { + if ( this.options.collapsible && this.options.selected != -1 ) { + index = this.options.selected; + } else { + return this; + } + } + this.anchors.eq( index ).trigger( this.options.event + ".tabs" ); + return this; + }, + + load: function( index ) { + index = this._getIndex( index ); + var self = this, + o = this.options, + a = this.anchors.eq( index )[ 0 ], + url = $.data( a, "load.tabs" ); + + this.abort(); + + // not remote or from cache + if ( !url || this.element.queue( "tabs" ).length !== 0 && $.data( a, "cache.tabs" ) ) { + this.element.dequeue( "tabs" ); + return; + } + + // load remote from here on + this.lis.eq( index ).addClass( "ui-state-processing" ); + + if ( o.spinner ) { + var span = $( "span", a ); + span.data( "label.tabs", span.html() ).html( o.spinner ); + } + + this.xhr = $.ajax( $.extend( {}, o.ajaxOptions, { + url: url, + success: function( r, s ) { + self.element.find( self._sanitizeSelector( a.hash ) ).html( r ); + + // take care of tab labels + self._cleanup(); + + if ( o.cache ) { + $.data( a, "cache.tabs", true ); + } + + self._trigger( "load", null, self._ui( self.anchors[ index ], self.panels[ index ] ) ); + try { + o.ajaxOptions.success( r, s ); + } + catch ( e ) {} + }, + error: function( xhr, s, e ) { + // take care of tab labels + self._cleanup(); + + self._trigger( "load", null, self._ui( self.anchors[ index ], self.panels[ index ] ) ); + try { + // Passing index avoid a race condition when this method is + // called after the user has selected another tab. + // Pass the anchor that initiated this request allows + // loadError to manipulate the tab content panel via $(a.hash) + o.ajaxOptions.error( xhr, s, index, a ); + } + catch ( e ) {} + } + } ) ); + + // last, so that load event is fired before show... + self.element.dequeue( "tabs" ); + + return this; + }, + + abort: function() { + // stop possibly running animations + this.element.queue( [] ); + this.panels.stop( false, true ); + + // "tabs" queue must not contain more than two elements, + // which are the callbacks for the latest clicked tab... + this.element.queue( "tabs", this.element.queue( "tabs" ).splice( -2, 2 ) ); + + // terminate pending requests from other tabs + if ( this.xhr ) { + this.xhr.abort(); + delete this.xhr; + } + + // take care of tab labels + this._cleanup(); + return this; + }, + + url: function( index, url ) { + this.anchors.eq( index ).removeData( "cache.tabs" ).data( "load.tabs", url ); + return this; + }, + + length: function() { + return this.anchors.length; + } +}); + +$.extend( $.ui.tabs, { + version: "1.8.9" +}); + +/* + * Tabs Extensions + */ + +/* + * Rotate + */ +$.extend( $.ui.tabs.prototype, { + rotation: null, + rotate: function( ms, continuing ) { + var self = this, + o = this.options; + + var rotate = self._rotate || ( self._rotate = function( e ) { + clearTimeout( self.rotation ); + self.rotation = setTimeout(function() { + var t = o.selected; + self.select( ++t < self.anchors.length ? t : 0 ); + }, ms ); + + if ( e ) { + e.stopPropagation(); + } + }); + + var stop = self._unrotate || ( self._unrotate = !continuing + ? function(e) { + if (e.clientX) { // in case of a true click + self.rotate(null); + } + } + : function( e ) { + t = o.selected; + rotate(); + }); + + // start rotation + if ( ms ) { + this.element.bind( "tabsshow", rotate ); + this.anchors.bind( o.event + ".tabs", stop ); + rotate(); + // stop rotation + } else { + clearTimeout( self.rotation ); + this.element.unbind( "tabsshow", rotate ); + this.anchors.unbind( o.event + ".tabs", stop ); + delete this._rotate; + delete this._unrotate; + } + + return this; + } +}); + +})( jQuery ); diff --git a/src/Orchard.Web/Modules/Orchard.jQuery/Scripts/jquery.ui.tabs.min.js b/src/Orchard.Web/Modules/Orchard.jQuery/Scripts/jquery.ui.tabs.min.js new file mode 100644 index 000000000..d29d2cf57 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.jQuery/Scripts/jquery.ui.tabs.min.js @@ -0,0 +1,35 @@ +/* + * jQuery UI Tabs 1.8.9 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Tabs + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + */ +(function(d,p){function u(){return++v}function w(){return++x}var v=0,x=0;d.widget("ui.tabs",{options:{add:null,ajaxOptions:null,cache:false,cookie:null,collapsible:false,disable:null,disabled:[],enable:null,event:"click",fx:null,idPrefix:"ui-tabs-",load:null,panelTemplate:"",remove:null,select:null,show:null,spinner:"Loading…",tabTemplate:"#{label}"},_create:function(){this._tabify(true)},_setOption:function(b,e){if(b=="selected")this.options.collapsible&& +e==this.options.selected||this.select(e);else{this.options[b]=e;this._tabify()}},_tabId:function(b){return b.title&&b.title.replace(/\s/g,"_").replace(/[^\w\u00c0-\uFFFF-]/g,"")||this.options.idPrefix+u()},_sanitizeSelector:function(b){return b.replace(/:/g,"\\:")},_cookie:function(){var b=this.cookie||(this.cookie=this.options.cookie.name||"ui-tabs-"+w());return d.cookie.apply(null,[b].concat(d.makeArray(arguments)))},_ui:function(b,e){return{tab:b,panel:e,index:this.anchors.index(b)}},_cleanup:function(){this.lis.filter(".ui-state-processing").removeClass("ui-state-processing").find("span:data(label.tabs)").each(function(){var b= +d(this);b.html(b.data("label.tabs")).removeData("label.tabs")})},_tabify:function(b){function e(g,f){g.css("display","");!d.support.opacity&&f.opacity&&g[0].style.removeAttribute("filter")}var a=this,c=this.options,h=/^#.+/;this.list=this.element.find("ol,ul").eq(0);this.lis=d(" > li:has(a[href])",this.list);this.anchors=this.lis.map(function(){return d("a",this)[0]});this.panels=d([]);this.anchors.each(function(g,f){var i=d(f).attr("href"),l=i.split("#")[0],q;if(l&&(l===location.toString().split("#")[0]|| +(q=d("base")[0])&&l===q.href)){i=f.hash;f.href=i}if(h.test(i))a.panels=a.panels.add(a.element.find(a._sanitizeSelector(i)));else if(i&&i!=="#"){d.data(f,"href.tabs",i);d.data(f,"load.tabs",i.replace(/#.*$/,""));i=a._tabId(f);f.href="#"+i;f=a.element.find("#"+i);if(!f.length){f=d(c.panelTemplate).attr("id",i).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").insertAfter(a.panels[g-1]||a.list);f.data("destroy.tabs",true)}a.panels=a.panels.add(f)}else c.disabled.push(g)});if(b){this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all"); +this.list.addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.lis.addClass("ui-state-default ui-corner-top");this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom");if(c.selected===p){location.hash&&this.anchors.each(function(g,f){if(f.hash==location.hash){c.selected=g;return false}});if(typeof c.selected!=="number"&&c.cookie)c.selected=parseInt(a._cookie(),10);if(typeof c.selected!=="number"&&this.lis.filter(".ui-tabs-selected").length)c.selected= +this.lis.index(this.lis.filter(".ui-tabs-selected"));c.selected=c.selected||(this.lis.length?0:-1)}else if(c.selected===null)c.selected=-1;c.selected=c.selected>=0&&this.anchors[c.selected]||c.selected<0?c.selected:0;c.disabled=d.unique(c.disabled.concat(d.map(this.lis.filter(".ui-state-disabled"),function(g){return a.lis.index(g)}))).sort();d.inArray(c.selected,c.disabled)!=-1&&c.disabled.splice(d.inArray(c.selected,c.disabled),1);this.panels.addClass("ui-tabs-hide");this.lis.removeClass("ui-tabs-selected ui-state-active"); +if(c.selected>=0&&this.anchors.length){a.element.find(a._sanitizeSelector(a.anchors[c.selected].hash)).removeClass("ui-tabs-hide");this.lis.eq(c.selected).addClass("ui-tabs-selected ui-state-active");a.element.queue("tabs",function(){a._trigger("show",null,a._ui(a.anchors[c.selected],a.element.find(a._sanitizeSelector(a.anchors[c.selected].hash))[0]))});this.load(c.selected)}d(window).bind("unload",function(){a.lis.add(a.anchors).unbind(".tabs");a.lis=a.anchors=a.panels=null})}else c.selected=this.lis.index(this.lis.filter(".ui-tabs-selected")); +this.element[c.collapsible?"addClass":"removeClass"]("ui-tabs-collapsible");c.cookie&&this._cookie(c.selected,c.cookie);b=0;for(var j;j=this.lis[b];b++)d(j)[d.inArray(b,c.disabled)!=-1&&!d(j).hasClass("ui-tabs-selected")?"addClass":"removeClass"]("ui-state-disabled");c.cache===false&&this.anchors.removeData("cache.tabs");this.lis.add(this.anchors).unbind(".tabs");if(c.event!=="mouseover"){var k=function(g,f){f.is(":not(.ui-state-disabled)")&&f.addClass("ui-state-"+g)},n=function(g,f){f.removeClass("ui-state-"+ +g)};this.lis.bind("mouseover.tabs",function(){k("hover",d(this))});this.lis.bind("mouseout.tabs",function(){n("hover",d(this))});this.anchors.bind("focus.tabs",function(){k("focus",d(this).closest("li"))});this.anchors.bind("blur.tabs",function(){n("focus",d(this).closest("li"))})}var m,o;if(c.fx)if(d.isArray(c.fx)){m=c.fx[0];o=c.fx[1]}else m=o=c.fx;var r=o?function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.hide().removeClass("ui-tabs-hide").animate(o,o.duration||"normal", +function(){e(f,o);a._trigger("show",null,a._ui(g,f[0]))})}:function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.removeClass("ui-tabs-hide");a._trigger("show",null,a._ui(g,f[0]))},s=m?function(g,f){f.animate(m,m.duration||"normal",function(){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");e(f,m);a.element.dequeue("tabs")})}:function(g,f){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");a.element.dequeue("tabs")}; +this.anchors.bind(c.event+".tabs",function(){var g=this,f=d(g).closest("li"),i=a.panels.filter(":not(.ui-tabs-hide)"),l=a.element.find(a._sanitizeSelector(g.hash));if(f.hasClass("ui-tabs-selected")&&!c.collapsible||f.hasClass("ui-state-disabled")||f.hasClass("ui-state-processing")||a.panels.filter(":animated").length||a._trigger("select",null,a._ui(this,l[0]))===false){this.blur();return false}c.selected=a.anchors.index(this);a.abort();if(c.collapsible)if(f.hasClass("ui-tabs-selected")){c.selected= +-1;c.cookie&&a._cookie(c.selected,c.cookie);a.element.queue("tabs",function(){s(g,i)}).dequeue("tabs");this.blur();return false}else if(!i.length){c.cookie&&a._cookie(c.selected,c.cookie);a.element.queue("tabs",function(){r(g,l)});a.load(a.anchors.index(this));this.blur();return false}c.cookie&&a._cookie(c.selected,c.cookie);if(l.length){i.length&&a.element.queue("tabs",function(){s(g,i)});a.element.queue("tabs",function(){r(g,l)});a.load(a.anchors.index(this))}else throw"jQuery UI Tabs: Mismatching fragment identifier."; +d.browser.msie&&this.blur()});this.anchors.bind("click.tabs",function(){return false})},_getIndex:function(b){if(typeof b=="string")b=this.anchors.index(this.anchors.filter("[href$="+b+"]"));return b},destroy:function(){var b=this.options;this.abort();this.element.unbind(".tabs").removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible").removeData("tabs");this.list.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.anchors.each(function(){var e= +d.data(this,"href.tabs");if(e)this.href=e;var a=d(this).unbind(".tabs");d.each(["href","load","cache"],function(c,h){a.removeData(h+".tabs")})});this.lis.unbind(".tabs").add(this.panels).each(function(){d.data(this,"destroy.tabs")?d(this).remove():d(this).removeClass("ui-state-default ui-corner-top ui-tabs-selected ui-state-active ui-state-hover ui-state-focus ui-state-disabled ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide")});b.cookie&&this._cookie(null,b.cookie);return this},add:function(b, +e,a){if(a===p)a=this.anchors.length;var c=this,h=this.options;e=d(h.tabTemplate.replace(/#\{href\}/g,b).replace(/#\{label\}/g,e));b=!b.indexOf("#")?b.replace("#",""):this._tabId(d("a",e)[0]);e.addClass("ui-state-default ui-corner-top").data("destroy.tabs",true);var j=c.element.find("#"+b);j.length||(j=d(h.panelTemplate).attr("id",b).data("destroy.tabs",true));j.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide");if(a>=this.lis.length){e.appendTo(this.list);j.appendTo(this.list[0].parentNode)}else{e.insertBefore(this.lis[a]); +j.insertBefore(this.panels[a])}h.disabled=d.map(h.disabled,function(k){return k>=a?++k:k});this._tabify();if(this.anchors.length==1){h.selected=0;e.addClass("ui-tabs-selected ui-state-active");j.removeClass("ui-tabs-hide");this.element.queue("tabs",function(){c._trigger("show",null,c._ui(c.anchors[0],c.panels[0]))});this.load(0)}this._trigger("add",null,this._ui(this.anchors[a],this.panels[a]));return this},remove:function(b){b=this._getIndex(b);var e=this.options,a=this.lis.eq(b).remove(),c=this.panels.eq(b).remove(); +if(a.hasClass("ui-tabs-selected")&&this.anchors.length>1)this.select(b+(b+1=b?--h:h});this._tabify();this._trigger("remove",null,this._ui(a.find("a")[0],c[0]));return this},enable:function(b){b=this._getIndex(b);var e=this.options;if(d.inArray(b,e.disabled)!=-1){this.lis.eq(b).removeClass("ui-state-disabled");e.disabled=d.grep(e.disabled,function(a){return a!=b});this._trigger("enable",null, +this._ui(this.anchors[b],this.panels[b]));return this}},disable:function(b){b=this._getIndex(b);var e=this.options;if(b!=e.selected){this.lis.eq(b).addClass("ui-state-disabled");e.disabled.push(b);e.disabled.sort();this._trigger("disable",null,this._ui(this.anchors[b],this.panels[b]))}return this},select:function(b){b=this._getIndex(b);if(b==-1)if(this.options.collapsible&&this.options.selected!=-1)b=this.options.selected;else return this;this.anchors.eq(b).trigger(this.options.event+".tabs");return this}, +load:function(b){b=this._getIndex(b);var e=this,a=this.options,c=this.anchors.eq(b)[0],h=d.data(c,"load.tabs");this.abort();if(!h||this.element.queue("tabs").length!==0&&d.data(c,"cache.tabs"))this.element.dequeue("tabs");else{this.lis.eq(b).addClass("ui-state-processing");if(a.spinner){var j=d("span",c);j.data("label.tabs",j.html()).html(a.spinner)}this.xhr=d.ajax(d.extend({},a.ajaxOptions,{url:h,success:function(k,n){e.element.find(e._sanitizeSelector(c.hash)).html(k);e._cleanup();a.cache&&d.data(c, +"cache.tabs",true);e._trigger("load",null,e._ui(e.anchors[b],e.panels[b]));try{a.ajaxOptions.success(k,n)}catch(m){}},error:function(k,n){e._cleanup();e._trigger("load",null,e._ui(e.anchors[b],e.panels[b]));try{a.ajaxOptions.error(k,n,b,c)}catch(m){}}}));e.element.dequeue("tabs");return this}},abort:function(){this.element.queue([]);this.panels.stop(false,true);this.element.queue("tabs",this.element.queue("tabs").splice(-2,2));if(this.xhr){this.xhr.abort();delete this.xhr}this._cleanup();return this}, +url:function(b,e){this.anchors.eq(b).removeData("cache.tabs").data("load.tabs",e);return this},length:function(){return this.anchors.length}});d.extend(d.ui.tabs,{version:"1.8.9"});d.extend(d.ui.tabs.prototype,{rotation:null,rotate:function(b,e){var a=this,c=this.options,h=a._rotate||(a._rotate=function(j){clearTimeout(a.rotation);a.rotation=setTimeout(function(){var k=c.selected;a.select(++k - - - {#addmedia_dlg.title} - - - - - - - - {#addmedia_dlg.title} - - - - - - {#addmedia_dlg.path_label} - - - - - - - - - - - - - - - - - diff --git a/src/Orchard.Web/Modules/TinyMce/Scripts/plugins/addmedia/editor_plugin.js b/src/Orchard.Web/Modules/TinyMce/Scripts/plugins/addmedia/editor_plugin.js deleted file mode 100644 index 1359e75d5..000000000 --- a/src/Orchard.Web/Modules/TinyMce/Scripts/plugins/addmedia/editor_plugin.js +++ /dev/null @@ -1 +0,0 @@ -(function() { tinymce.PluginManager.requireLangPack("addmedia"); tinymce.create('tinymce.plugins.Orchard.AddMedia', { init: function(ed, url) { ed.addCommand('mceAddMedia', function() { ed.windowManager.open({ file: url + '/addmedia.htm', width: 550 + parseInt(ed.getLang('addmedia.delta_width', 0)), height: 110 + parseInt(ed.getLang('addmedia.delta_height', 0)), inline: 1 }, { plugin_url: url }) }); ed.addButton('addmedia', { title: ed.getLang('addmedia.title'), cmd: 'mceAddMedia', image: url + '/img/picture_add.png' }) }, createControl: function(n, cm) { return null }, getInfo: function() { return { longname: 'Orchard AddMedia Plugin', author: 'Nathan Heskew', authorurl: 'http://orchardproject.net', infourl: 'http://orchardproject.net', version: '0.1'} } }); tinymce.PluginManager.add('addmedia', tinymce.plugins.Orchard.AddMedia) })(); \ No newline at end of file diff --git a/src/Orchard.Web/Modules/TinyMce/Scripts/plugins/addmedia/editor_plugin_src.js b/src/Orchard.Web/Modules/TinyMce/Scripts/plugins/addmedia/editor_plugin_src.js deleted file mode 100644 index ddba1b6f6..000000000 --- a/src/Orchard.Web/Modules/TinyMce/Scripts/plugins/addmedia/editor_plugin_src.js +++ /dev/null @@ -1,68 +0,0 @@ -(function() { - // Load plugin specific language pack - tinymce.PluginManager.requireLangPack("addmedia"); - - tinymce.create('tinymce.plugins.Orchard.AddMedia', { - /** - * Initializes the plugin, this will be executed after the plugin has been created. - * This call is done before the editor instance has finished it's initialization so use the onInit event - * of the editor instance to intercept that event. - * - * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in. - * @param {string} url Absolute URL to where the plugin is located. - */ - init: function(ed, url) { - // Register the command so that it can be invoked by using tinyMCE.activeEditor.execCommand('mceAddMedia'); - ed.addCommand('mceAddMedia', function() { - ed.windowManager.open({ - file: url + '/addmedia.htm', - width: 550 + parseInt(ed.getLang('addmedia.delta_width', 0)), - height: 110 + parseInt(ed.getLang('addmedia.delta_height', 0)), - inline: 1 - }, { - plugin_url: url - }); - }); - - // Register addmedia button - ed.addButton('addmedia', { - title: ed.getLang('addmedia.title'), - cmd: 'mceAddMedia', - image: url + '/img/picture_add.png' - }); - }, - - /** - * Creates control instances based in the incomming name. This method is normally not - * needed since the addButton method of the tinymce.Editor class is a more easy way of adding buttons - * but you sometimes need to create more complex controls like listboxes, split buttons etc then this - * method can be used to create those. - * - * @param {String} n Name of the control to create. - * @param {tinymce.ControlManager} cm Control manager to use inorder to create new control. - * @return {tinymce.ui.Control} New control instance or null if no control was created. - */ - createControl: function(n, cm) { - return null; - }, - - /** - * Returns information about the plugin as a name/value array. - * The current keys are longname, author, authorurl, infourl and version. - * - * @return {Object} Name/value array containing information about the plugin. - */ - getInfo: function() { - return { - longname: 'Orchard AddMedia Plugin', - author: 'Nathan Heskew', - authorurl: 'http://orchardproject.net', - infourl: 'http://orchardproject.net', - version: '0.1' - }; - } - }); - - // Register plugin - tinymce.PluginManager.add('addmedia', tinymce.plugins.Orchard.AddMedia); -})(); \ No newline at end of file diff --git a/src/Orchard.Web/Modules/TinyMce/Scripts/plugins/addmedia/js/addmedia.js b/src/Orchard.Web/Modules/TinyMce/Scripts/plugins/addmedia/js/addmedia.js deleted file mode 100644 index f9c382f95..000000000 --- a/src/Orchard.Web/Modules/TinyMce/Scripts/plugins/addmedia/js/addmedia.js +++ /dev/null @@ -1,92 +0,0 @@ -tinyMCEPopup.requireLangPack("addmedia"); - -// async media file uploads brought to you with a little insight from reading: http://www.bennadel.com/blog/1244-ColdFusion-jQuery-And-AJAX-File-Upload-Demo.htm -var AddMediaDialog = { - init: function () { - var form = document.forms[0]; - form.action = tinyMCE.activeEditor.getParam('addmedia_action'); - form.MediaPath.value = tinyMCE.activeEditor.getParam('addmedia_path'); - form.__RequestVerificationToken.value = tinyMCE.activeEditor.getParam('request_verification_token'); - }, - - addMedia: function (form) { - var iframeName = "addmedia__" + (new Date()).getTime(); - - var iframe; - try { - iframe = document.createElement(""); // <- for some vowel browser - } catch (ex) { - iframe = document.createElement("iframe"); - iframe.name = iframeName; - } - iframe.src = "about:blank"; - tinymce.DOM.setStyles(iframe, { display: "none" }); - - var iframeLoadHandler = tinymce.dom.Event.add(iframe, 'load', function (ev) { - try { - var frameWindow = window.frames[iframeName]; - - if (frameWindow.document.URL == "about:blank") { - return true; - } - - var result = frameWindow.result, close = 0; - - if (result && result.url) { - if (window.parent && window.parent.AddMediaDialog) { - window.parent.AddMediaDialog.insertMedia(result.url); - } else { - AddMediaDialog.insertMedia(result.url); - } - - close = 1; - } else if (result && result.error) { - alert(tinyMCEPopup.getLang("addmedia_dlg.msg_error") + "\n\r\n\r" + result.error); - } else { - var somethingPotentiallyHorrible = ""; - try { - somethingPotentiallyHorrible = window.frames[iframeName].document.getElementsByTagName("body")[0].innerHTML; - } catch (ex) { // some browsers flip out trying to access anything in the iframe when there's an error. best we can do is guess at this point - somethingPotentiallyHorrible = tinyMCEPopup.getLang("addmedia_dlg.msg_error_unknown"); - } - if (somethingPotentiallyHorrible) { - alert(tinyMCEPopup.getLang("addmedia_dlg.msg_error_unexpected") + "\n\r\n\r" + somethingPotentiallyHorrible); - } - } - - //cleanup - setTimeout(function () { - tinymce.dom.Event.remove(iframe, 'load', iframeLoadHandler); - tinymce.DOM.remove(iframe); - iframe = null; - - if (close) { - if (window.parent && window.parent.tinyMCEPopup) { - window.parent.tinyMCEPopup.close(); - } else { - tinyMCEPopup.close(); - } - } - }, - 123); - } catch (ex) { - alert(ex.message); - } - }); - - tinymce.DOM.add(document.body, iframe); - form.target = iframe.name; - }, - - insertMedia: function (url) { - if (!url) return; - - var markup = /\.(jpe?g|png|gif)$/i.test(url) - ? "" - : "" + url + ""; - - tinyMCE.activeEditor.execCommand("mceInsertContent", false, markup); - } -}; - -tinyMCEPopup.onInit.add(AddMediaDialog.init, AddMediaDialog); \ No newline at end of file diff --git a/src/Orchard.Web/Modules/TinyMce/Scripts/plugins/addmedia/langs/en.js b/src/Orchard.Web/Modules/TinyMce/Scripts/plugins/addmedia/langs/en.js deleted file mode 100644 index a43644654..000000000 --- a/src/Orchard.Web/Modules/TinyMce/Scripts/plugins/addmedia/langs/en.js +++ /dev/null @@ -1,6 +0,0 @@ -tinyMCE.addI18n({ en: { - addmedia: { - title: "Add Media" - } -} -}); \ No newline at end of file diff --git a/src/Orchard.Web/Modules/TinyMce/Scripts/plugins/addmedia/langs/en_dlg.js b/src/Orchard.Web/Modules/TinyMce/Scripts/plugins/addmedia/langs/en_dlg.js deleted file mode 100644 index 0b47b86e7..000000000 --- a/src/Orchard.Web/Modules/TinyMce/Scripts/plugins/addmedia/langs/en_dlg.js +++ /dev/null @@ -1,12 +0,0 @@ -tinyMCE.addI18n({ en: { - addmedia_dlg: { - button_title: "Add Media", - title: "Add Media", - path_label: "Media File Path", - browse_button_text: "Browse", - msg_error: "Wait, there was an error:", - msg_error_unexpected: "Something unexpected happened:", - msg_error_unknown: "Can't get the error from the response. This can happen when the server throws an error. Is the file size larger than what the server is configured to accept?" - } -} -}); \ No newline at end of file diff --git a/src/Orchard.Web/Modules/TinyMce/Scripts/plugins/mediapicker/editor_plugin.js b/src/Orchard.Web/Modules/TinyMce/Scripts/plugins/mediapicker/editor_plugin.js new file mode 100644 index 000000000..59d6377c9 --- /dev/null +++ b/src/Orchard.Web/Modules/TinyMce/Scripts/plugins/mediapicker/editor_plugin.js @@ -0,0 +1,2 @@ +(function(){tinymce.create("tinymce.plugins.Orchard.MediaPicker",{init:function(b,d){b.addCommand("mceMediaPicker",function(){b.focus();var c,a=b.selection.getContent();if(a){a=a.replace(/\ with , so we can easily use jquery to get the 'src' without it + // being resolved by the browser (e.g. prevent '/foo.png' becoming 'http://localhost:12345/orchardlocal/foo.png'). + content = content.replace(/\ + + + @@ -124,13 +127,6 @@ - - - - - - - @@ -146,6 +142,7 @@ +
@FolderLink(T("Media Folders").ToString(), "") + @foreach (var navigation in MediaHelpers.GetFolderNavigationHierarchy(Model.MediaPath)) { + > @FolderLink(navigation.FolderName, navigation.FolderPath) + } +