Compare commits

...

105 Commits

Author SHA1 Message Date
Sebastiaan Janssen fd2fe1939a Adds new installer image 2014-06-11 10:22:13 +02:00
Morten Christensen 81e56bed9e Implementing Document Type Compositions in the Document Type Editor.
Adds changes to the CMSNode and doc type related class which improves the internal usage of the new model based on IUmbracoEntity.
2014-06-11 07:08:38 +02:00
Sebastiaan Janssen 52457fea21 Bump version number 2014-06-10 18:47:02 +02:00
Sebastiaan Janssen 07ec9aa813 Undo formatting of query for now 2014-06-10 18:11:14 +02:00
Sebastiaan Janssen ba89aec34f Merge pull request #415 from rustyswayne/7.1.5-templatequery
Query Builder - Fix Date sort fields and format returned query
2014-06-10 17:48:59 +02:00
Jeavon Leopold 29533820ab Query Builder - Fix Date sort fields and format returned query to be on new lines and tabbed 2014-06-10 16:43:28 +01:00
Sebastiaan Janssen 4366500fad Merge branch '7.1.2-Usky' of https://github.com/umbraco/UmbracoCms-Internal into 7.2.0
Conflicts:
	src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js
2014-06-10 17:15:07 +02:00
Sebastiaan Janssen ee6e1a9d8b Disable extra templates tree 2014-06-10 17:13:58 +02:00
antoine 3b95184f04 empty video or media issue 2014-06-10 17:06:13 +02:00
antoine 9da7e108da double )) in embed open windows 2014-06-10 16:52:29 +02:00
antoine cb491d45ab open preview issue fixed 2014-06-10 16:50:03 +02:00
Sebastiaan Janssen 344b880ba7 Merge branch '7.1.2-Usky' of https://github.com/umbraco/UmbracoCms-Internal into 7.2.0
Conflicts:
	src/Umbraco.Web.UI.Client/src/views/common/dialogs/content/edit.controller.js
	src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js
	src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js
	src/Umbraco.Web/Umbraco.Web.csproj
2014-06-10 15:22:31 +02:00
Sebastiaan Janssen c958f12bc1 Merge pull request #413 from rustyswayne/7.1.5-templatequery
Template Query Editor Prototype
2014-06-10 15:11:57 +02:00
Shannon 9a973d0ccc fixes csproj 2014-06-10 15:06:41 +02:00
Jeavon Leopold b170053e87 Support for multiple "and" conditions in the query builder 2014-06-10 14:05:57 +01:00
antoine db4509de2b link picker removed 2014-06-10 14:35:57 +02:00
antoine 5c91b822e3 legacy grid's configs removed 2014-06-10 13:45:52 +02:00
Shannon e6ddf63e56 re-adds old providers 2014-06-10 13:19:52 +02:00
Shannon 84ac029372 re-add segment provider 2014-06-10 13:18:54 +02:00
antoine 5d59741612 iframe resize with the tuning panel 2014-06-10 12:52:33 +02:00
Jeavon Leopold 2ffd0413e8 Adding query builder button to View editor 2014-06-10 11:27:26 +01:00
Jeavon Leopold dc4e6ee01c Adding Visible to template query builder 2014-06-10 08:40:33 +01:00
Jeavon Leopold 1d8e217c6f Template Query builder prototype working! 2014-06-09 23:23:31 +01:00
Rusty Swayne 0b3136cf20 Patched Contains query & implemented SortExpression 2014-06-09 09:14:26 -07:00
Rusty Swayne b0e96b256a Merge branch '7.1.5' into 7.1.5-templatequery 2014-06-09 08:44:19 -07:00
Jeavon Leopold 72a966b0fa Template Query Editor - Angular and hooking up to the API controller 2014-06-09 16:42:57 +01:00
Rusty Swayne bf222f26cd Merge remote-tracking branch 'umbraco/7.1.5' into 7.1.5 2014-06-09 08:41:33 -07:00
Rusty Swayne be9f4b70e2 Added JasonCamelCaseFormatter to the .Web project 2014-06-09 08:40:47 -07:00
Rusty Swayne 0060f0019e Merge branch '7.1.5' into 7.1.5-templatequery 2014-06-09 05:41:16 -07:00
Rusty Swayne 632cd221d9 Attribute for WebApi controllers that camel cases object property names when serialized to Json 2014-06-09 05:39:14 -07:00
Jeavon Leopold 46d5215d9e Merge remote-tracking branch 'upstream/7.1.5' into 7.1.5-templatequery 2014-06-09 13:21:07 +01:00
Jeavon Leopold aa43656cb9 Revisions to QueryModel. Added Sorting and Take functionality. 2014-06-09 13:18:35 +01:00
Jeavon Leopold e4837d1962 Changing DescendantOrSelf to DescendantOrSelf.First as it doesn't work with dynamics 2014-06-09 08:41:29 +01:00
Jeavon Leopold 40868d7cd2 Fixing typo in Descendants for template query controller 2014-06-09 07:08:06 +01:00
Jeavon Leopold 0fde3f7b5d Fixing up the test method for the query builder 2014-06-08 23:26:46 +01:00
Jeavon Leopold 76f0eac5f8 Template Query WIP 2014-06-08 22:39:01 +01:00
Jeavon Leopold 2352d258b9 Fix mock API method and some Stylecop 2014-06-08 19:06:37 +01:00
Rusty Swayne 2dc38a29cf Added QueryModel 2014-06-08 10:20:34 -07:00
Jeavon Leopold 1aa162e255 Add Site() method to DynamicPublishedContent for Template Query Builder 2014-06-08 18:09:29 +01:00
Rusty Swayne 342999a678 TemplateQueryController 2014-06-08 09:51:06 -07:00
antoine 5cb210c7b6 slider input updated real time 2014-06-08 11:08:56 +02:00
antoine b80ab20cb4 device preview background color 2014-06-08 09:42:39 +02:00
antoine bac6e71e7f color picker in real time, general clear code 2014-06-05 14:02:38 +02:00
antoine 9a36d74418 tuning issue 2014-06-04 16:24:02 +02:00
antoine 39eaa4a15e Element select improved, default style more clean 2014-06-03 19:33:31 +02:00
antoine 2fdf040f73 Palette improvement 2014-06-02 10:55:53 +02:00
antoine aec956dd2e grunt js task issue 2014-06-02 10:55:17 +02:00
antoine 65bb1e1e20 gridRow default less 2014-05-30 20:03:34 +02:00
antoine 3dabd73ec2 Merge branch '7.1.2-Usky' into 7.1.2-Usky-Grid
Conflicts:
	src/Umbraco.Web.UI.Client/src/tuning/tuning.controller.js
	src/Umbraco.Web.UI.Client/src/tuning/tuning.global.js
	src/Umbraco.Web.UI/Umbraco/js/tuning.panel.js
	src/Umbraco.Web.UI/Views/Partials/Grid/bootstrap3.cshtml
2014-05-30 19:56:51 +02:00
antoine 90e5440518 bootstrap3 ; issue + hx style issue 2014-05-28 15:33:36 +02:00
antoine 7cfd2d6593 schema's selector improved 2014-05-28 15:06:16 +02:00
antoine 6382515258 Device preview always enable 2014-05-28 11:29:08 +02:00
antoine fd2b9ce67d /umbraco/dialogs/tuning.aspx removed 2014-05-27 12:37:43 +02:00
antoine 1284417a58 /umbraco/dialogs/tuning.aspx removed 2014-05-27 12:36:49 +02:00
antoine b57642003c Grid row unique id 2014-05-25 20:52:23 +02:00
antoine 9ee59e8e38 Merge branch '7.1.2-Usky' into 7.1.2-Usky-Grid
Conflicts:
	src/Umbraco.Web.UI/Views/Partials/Grid/bootstrap3.cshtml
2014-05-25 18:35:18 +02:00
antoine a5f81ae6c2 Merge branch '7.1.2-Usky' of https://github.com/umbraco/UmbracoCms-Internal into 7.1.2-Usky
Conflicts:
	src/Umbraco.Web.UI.Client/gruntFile.js
2014-05-23 16:39:39 +02:00
per ploug 08cc608dc8 One last change, enable setting max number of items in a cell 2014-05-23 14:57:16 +02:00
per ploug 71839d53a6 Last round of polish to the grid
adds image captions, textstring markup support in rendering, better
styling and icon previews
2014-05-23 14:37:00 +02:00
antoine 7508613d13 First approach 2014-05-22 22:20:13 +02:00
antoine 9626e91c83 dependency with bootstrap.3.0.1.min.js 2014-05-21 19:51:13 +02:00
per ploug 83b6375f04 Migrated the Editors out of the grid
Added configuration for individual editors, added allowed editors, new
styling refactored css
2014-05-21 15:40:17 +02:00
antoine 52cd44a700 Merge branch '7.1.2-Usky' of https://github.com/umbraco/UmbracoCms-Internal into 7.1.2-Usky 2014-05-21 15:37:58 +02:00
antoine 0c8ff187aa grunt update to copy default files 2014-05-21 15:37:20 +02:00
John 3af74bd4fd Load google fonts independently in the content iframe.
Enables live preview on the page's content for google fonts.
2014-05-21 14:57:20 +02:00
John c9e2e1e116 Apply preview of font to page's content. 2014-05-21 14:57:18 +02:00
John c27f73e54a Fix glitch when selecting new font in dropdown.
In the font family picker, selecting a not-yet-loaded font caused the font sample to render as Arial until the font had been downloaded.
2014-05-21 14:57:17 +02:00
John cede13d180 Merge name and description changes from uSky. 2014-05-21 14:57:15 +02:00
antoine 3ffb2772b6 Merge branch '7.1.2-Usky' of https://github.com/umbraco/UmbracoCms-Internal into 7.1.2-Usky 2014-05-21 13:54:25 +02:00
antoine e6f0d4e056 @Umbraco.EnableTuning() and Recursive less file 2014-05-21 13:53:00 +02:00
per ploug 23c7eafaf5 merge from upstream 7.1.4 2014-05-20 12:32:47 +02:00
per ploug 90892ce818 adds rootscope access to umb.clientmgr 2014-05-20 12:14:10 +02:00
John e62d1559b1 Fix grunt not copying tuning's index.html to Umbraco/preview.
Also fixes same issue when using grunt --dev.
2014-05-20 10:36:48 +02:00
antoine 81934c47d2 laptop width 2014-05-19 16:06:45 +02:00
antoine 80379e116d umbraco media support 2014-05-19 14:34:09 +02:00
antoine 89081b86af preview grunt issue 2014-05-16 14:42:47 +02:00
antoine 216e2255f2 Save message more friendly 2014-05-16 13:33:41 +02:00
antoine 9db3dbd730 Style improvement
focus schema in front when roll-over in panel
2014-05-16 11:40:39 +02:00
antoine 8a1dd96294 some styles fixed 2014-05-14 19:15:43 +02:00
antoine 67bebd0d2d index.html missing 2014-05-14 18:39:25 +02:00
antoine 4f34fbd2c8 devices preview better look
tested in chrome/firefox/IE
2014-05-14 18:37:44 +02:00
antoine 8cda6e4062 Tuning panel include in preview mode 2014-05-14 14:51:58 +02:00
antoine d8209efefa Merge branch '7.1.2-Usky' of https://github.com/umbraco/UmbracoCms-Internal into 7.1.2-Usky 2014-05-13 16:20:20 +02:00
antoine 00180d2120 Web.config with less mime 2014-05-13 16:19:45 +02:00
per ploug 66e768afee Missing bootstrap3 grid view
Missing as gitignore wont allow it, had to manually add it
2014-05-13 13:31:51 +02:00
antoine bba109cb28 tuning.aspx deleted 2014-05-13 13:10:28 +02:00
antoine 39ede8c47a Merge branch '7.1.2-Usky' of https://github.com/umbraco/UmbracoCms-Internal into 7.1.2-Usky
Conflicts:
	src/Umbraco.Web/Umbraco.Web.csproj
2014-05-13 13:07:38 +02:00
antoine fec84b8ce3 iframe 100% in firefox 2014-05-13 13:04:39 +02:00
antoine a00e5d54aa rename tuning project without usky 2014-05-13 12:50:48 +02:00
per ploug 331813e370 Adds serverside grid api
Replaces the partial view macro, also enables the grid to be rendered
with different frameworks, should be fairly easy to support a couple
2014-05-13 12:36:38 +02:00
per ploug 6c36f43276 Adds support for loading custom RTE settings into the grid 2014-05-13 12:35:08 +02:00
per ploug e8f4e7663f More visual tweaks to the grid to handle deletes
also rolls back a couple of bower "tweaks" that ended up being horrible
2014-05-13 09:45:09 +02:00
antoine 9f116c00d5 Merge branch '7.1.2-Usky' of https://github.com/umbraco/UmbracoCms-Internal into 7.1.2-Usky 2014-05-12 20:00:57 +02:00
antoine 7e61b25746 uskytuning with style suggest and preview device 2014-05-12 19:57:06 +02:00
per ploug 482750ab6a Missing file 2014-05-12 13:22:58 +02:00
per ploug 40d1099fbd grid modifications 2014-05-12 13:22:57 +02:00
antoine 2d050a0c88 original sln 2014-05-10 15:57:59 +02:00
antoine 103f4f33bd Only one less style file 2014-05-10 15:56:36 +02:00
antoine 727f9355a0 dotless dependency 2014-05-10 14:53:59 +02:00
antoine e269a0b9bb Merge branch '7.1.2-Usky' of https://github.com/umbraco/UmbracoCms-Internal into 7.1.2-Usky 2014-05-09 18:22:37 +02:00
antoine 861b1cefe5 uSkyTuning.loader.js deleted 2014-05-09 18:22:02 +02:00
antoine 23301817bf uSkyTuning into Umbraco core, first approach 2014-05-09 18:18:08 +02:00
antoine f5842c82a6 dotless dependency 2014-05-09 18:15:31 +02:00
per ploug 4baf3ed99a Initial commit of the grid 2014-05-09 12:23:59 +02:00
per ploug 3c09cd7674 Initial commit to set upstream
Includes a minor change to force it to commit...
2014-05-07 15:10:14 +02:00
123 changed files with 16192 additions and 456 deletions
+6
View File
@@ -78,6 +78,12 @@ src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/loader.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/main.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/app.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/tuning.panel.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/tuning.palettes.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/tuning.loader.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/tuning.front.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/tuning.config.js
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.js
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.css
+1 -1
View File
@@ -4,7 +4,7 @@ IF NOT EXIST UmbracoVersion.txt (
GOTO :showerror
)
SET /p release=<UmbracoVersion.txt
SET comment=
SET comment=alpha
SET version=%release%
IF [%comment%] EQU [] (SET version=%release%) ELSE (SET version=%release%-%comment%)
+1 -1
View File
@@ -1 +1 @@
7.1.5
7.2.0
@@ -5,7 +5,7 @@ namespace Umbraco.Core.Configuration
{
public class UmbracoVersion
{
private static readonly Version Version = new Version("7.1.5");
private static readonly Version Version = new Version("7.2.0");
/// <summary>
/// Gets the current version of Umbraco.
@@ -23,7 +23,7 @@ namespace Umbraco.Core.Configuration
/// Gets the version comment (like beta or RC).
/// </summary>
/// <value>The version comment.</value>
public static string CurrentComment { get { return ""; } }
public static string CurrentComment { get { return "alpha"; } }
// Get the version of the umbraco.dll by looking at a class in that dll
// Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx
@@ -70,6 +70,11 @@
//TODO: Fill in the rest!
/// <summary>
/// alias for the media tree.
/// </summary>
public const string Templates = "template";
}
}
@@ -130,6 +130,12 @@ namespace Umbraco.Core
/// </summary>
public const string FolderBrowserAlias = "Umbraco.FolderBrowser";
/// <summary>
/// Alias for the grid datatype.
/// </summary>
public const string GridAlias = "Umbraco.Grid";
/// <summary>
/// Guid for the Image Cropper datatype.
/// </summary>
@@ -93,8 +93,17 @@ namespace Umbraco.Core.Models
{
if (ContentTypeCompositionExists(alias))
{
var contentTypeComposition = ContentTypeComposition.First(x => x.Alias == alias);
var contentTypeComposition = ContentTypeComposition.FirstOrDefault(x => x.Alias == alias);
if (contentTypeComposition == null)//You can't remove a composition from another composition
return false;
RemovedContentTypeKeyTracker.Add(contentTypeComposition.Id);
//If the ContentType we are removing has Compositions of its own these needs to be removed as well
var compositionIdsToRemove = contentTypeComposition.CompositionIds().ToList();
if(compositionIdsToRemove.Any())
RemovedContentTypeKeyTracker.AddRange(compositionIdsToRemove);
OnPropertyChanged(ContentTypeCompositionSelector);
return _contentTypeComposition.Remove(contentTypeComposition);
}
@@ -240,7 +240,7 @@ AND umbracoNode.id <> @id",
{
var nodeId = contentDto.NodeId;
var propertyTypeId = propertyType.Id;
var propertySql = new Sql().Select("*")
var propertySql = new Sql().Select("cmsPropertyData.id")
.From<PropertyDataDto>()
.InnerJoin<PropertyTypeDto>()
.On<PropertyDataDto, PropertyTypeDto>(
@@ -249,7 +249,7 @@ AND umbracoNode.id <> @id",
.Where<PropertyTypeDto>(x => x.Id == propertyTypeId);
//Finally delete the properties that match our criteria for removing a ContentType from the composition
Database.Delete<PropertyDataDto>(propertySql);
Database.Delete<PropertyDataDto>(new Sql("WHERE id IN (" + propertySql.SQL + ")", propertySql.Arguments));
}
}
}
@@ -285,7 +285,8 @@ AND umbracoNode.id <> @id",
}
}
if (((ICanBeDirty) entity).IsPropertyDirty("PropertyGroups") || entity.PropertyGroups.Any(x => x.IsDirty()))
if (((ICanBeDirty)entity).IsPropertyDirty("PropertyGroups") ||
entity.PropertyGroups.Any(x => x.IsDirty()))
{
//Delete Tabs/Groups by excepting entries from db with entries from collections
var dbPropertyGroups =
@@ -346,6 +347,25 @@ AND umbracoNode.id <> @id",
if (propertyType.HasIdentity == false)
propertyType.Id = typePrimaryKey; //Set Id on new PropertyType
}
//If a Composition is removed we need to update/reset references to the PropertyGroups on that ContentType
if (((ICanBeDirty)entity).IsPropertyDirty("ContentTypeComposition") &&
compositionBase != null &&
compositionBase.RemovedContentTypeKeyTracker != null &&
compositionBase.RemovedContentTypeKeyTracker.Any())
{
foreach (var compositionId in compositionBase.RemovedContentTypeKeyTracker)
{
var dbPropertyGroups =
Database.Fetch<PropertyTypeGroupDto>("WHERE contenttypeNodeId = @Id", new { Id = compositionId })
.Select(x => x.Id);
foreach (var propertyGroup in dbPropertyGroups)
{
Database.Update<PropertyTypeGroupDto>("SET parentGroupId = NULL WHERE parentGroupId = @TabId AND contenttypeNodeId = @ContentTypeNodeId",
new { TabId = propertyGroup, ContentTypeNodeId = entity.Id });
}
}
}
}
protected IEnumerable<ContentTypeSort> GetAllowedContentTypeIds(int id)
@@ -371,7 +371,6 @@ namespace Umbraco.Core.Persistence.Repositories
//template.CreateDate = _viewsFileSystem.GetCreated(path).UtcDateTime;
//template.Key = new FileInfo(path).Name.EncodeAsGuid();
template.Path = path;
template.Content = content;
}
+3 -1
View File
@@ -16,6 +16,8 @@
"tests"
],
"dependencies": {
"typeahead.js": "~0.10.2"
"typeahead.js": "~0.10.2",
"ace-builds": "~1.1.3",
"rgrove-lazyload": "*"
}
}
+58 -14
View File
@@ -6,15 +6,16 @@ module.exports = function (grunt) {
grunt.registerTask('dev', ['jshint:dev', 'build', 'webserver', 'open:dev', 'watch']);
//run by the watch task
grunt.registerTask('watch-js', ['jshint:dev','concat','copy:app','copy:mocks','copy:packages','copy:vs','karma:unit']);
grunt.registerTask('watch-less', ['recess:build','recess:installer','copy:assets','copy:vs']);
grunt.registerTask('watch-js', ['jshint:dev','concat','copy:app','copy:mocks','copy:packages','copy:tuning','copy:vs','karma:unit']);
grunt.registerTask('watch-less', ['recess:build', 'recess:installer', 'recess:tuning','copy:tuning', 'copy:assets', 'copy:vs']);
grunt.registerTask('watch-html', ['copy:views', 'copy:vs']);
grunt.registerTask('watch-packages', ['copy:packages']);
grunt.registerTask('watch-installer', ['concat:install','concat:installJs','copy:installer', 'copy:vs']);
grunt.registerTask('watch-installer', ['concat:install', 'concat:installJs', 'copy:installer', 'copy:vs']);
grunt.registerTask('watch-tuning', ['copy:tuning', 'concat:tuningJs', 'copy:vs']);
grunt.registerTask('watch-test', ['jshint:dev', 'karma:unit']);
//triggered from grunt dev or grunt
grunt.registerTask('build', ['clean','concat','recess:min','recess:installer','copy']);
grunt.registerTask('build', ['clean', 'concat', 'recess:min', 'recess:installer', 'recess:tuning', 'bower', 'copy']);
//utillity tasks
grunt.registerTask('docs', ['ngdocs']);
@@ -109,23 +110,48 @@ module.exports = function (grunt) {
clean: ['<%= distdir %>/*'],
copy: {
/* Copies over the files downloaded by bower
bower: {
files: [
{ dest: '<%= distdir %>/lib/typeahead/typeahead.bundle.min.js', src: '<%= bowerfiles %>/typeahead.js/dist/typeahead.bundle.min.js' }
]
},
*/
assets: {
files: [{ dest: '<%= distdir %>/assets', src : '**', expand: true, cwd: 'src/assets/' }]
},
// Copies over the files downloaded by bower
bower: {
files: [
{
dest: '<%= distdir %>/lib/typeahead/typeahead.bundle.min.js',
src: 'bower_components/typeahead.js/dist/typeahead.bundle.min.js'
},
{
dest: '<%= distdir %>/lib/lazyload/lazyload.min.js',
src: 'bower_components/rgrove-lazyload/lazyload.js'
},
{
dest: '<%= distdir %>/lib/ace/',
src: '**',
expand: true,
cwd: 'bower_components/ace-builds/src-min-noconflict/'
}
]
},
installer: {
files: [{ dest: '<%= distdir %>/views/install', src : '**/*.html', expand: true, cwd: 'src/installer/steps' }]
},
tuning: {
files: [
{ dest: '<%= distdir %>/preview', src: '**/*.html', expand: true, cwd: 'src/tuning' },
{ dest: '<%= distdir %>/assets/css', src: 'tuning.defaultStyle.css', expand: true, cwd: 'src/tuning' },
{ dest: '<%= distdir %>/assets/less', src: 'tuning.defaultStyle.less', expand: true, cwd: 'src/tuning' },
{ dest: '<%= distdir %>/assets/less', src: 'tuning.gridRowStyle.less', expand: true, cwd: 'src/tuning' },
{ dest: '<%= distdir %>/js', src: 'tuning.config.js', expand: true, cwd: 'src/tuning/config' },
{ dest: '<%= distdir %>/js', src: 'tuning.palettes.js', expand: true, cwd: 'src/tuning/config' },
{ dest: '<%= distdir %>/js', src: 'tuning.front.js', expand: true, cwd: 'src/tuning' }
]
},
vendor: {
files: [{ dest: '<%= distdir %>/lib', src : '**', expand: true, cwd: 'lib/' }]
},
@@ -147,7 +173,8 @@ module.exports = function (grunt) {
{ dest: '<%= vsdir %>/assets', src: '**', expand: true, cwd: '<%= distdir %>/assets' },
{ dest: '<%= vsdir %>/js', src: '**', expand: true, cwd: '<%= distdir %>/js' },
{ dest: '<%= vsdir %>/lib', src: '**', expand: true, cwd: '<%= distdir %>/lib' },
{ dest: '<%= vsdir %>/views', src: '**', expand: true, cwd: '<%= distdir %>/views' }
{ dest: '<%= vsdir %>/views', src: '**', expand: true, cwd: '<%= distdir %>/views' },
{ dest: '<%= vsdir %>/preview', src: '**', expand: true, cwd: '<%= distdir %>/preview' }
]
},
@@ -186,6 +213,10 @@ module.exports = function (grunt) {
footer: "\n\n})();"
}
},
tuningJs: {
src: ['src/tuning/tuning.global.js', 'src/tuning/tuning.controller.js', 'src/tuning/lib/slider.directive.js', 'src/tuning/lib/spectrum.directive.js'],
dest: '<%= distdir %>/js/tuning.panel.js'
},
controllers: {
src:['src/controllers/**/*.controller.js','src/views/**/*.controller.js'],
dest: '<%= distdir %>/js/umbraco.controllers.js',
@@ -272,6 +303,15 @@ module.exports = function (grunt) {
compile: true
}
},
tuning: {
files: {
'<%= distdir %>/assets/css/tuning.panelStyles.css':
['src/less/tuning.panelStyles.less', 'src/less/helveticons.less']
},
options: {
compile: true
}
},
min: {
files: {
'<%= distdir %>/assets/css/<%= pkg.name %>.css': ['<%= src.less %>']
@@ -304,6 +344,10 @@ module.exports = function (grunt) {
files: ['src/installer/**/*.*'],
tasks: ['watch-installer', 'timestamp'],
},
tuning: {
files: ['src/tuning/**/*.*'],
tasks: ['watch-tuning', 'timestamp'],
},
html: {
files: ['src/views/**/*.html', 'src/*.html'],
tasks:['watch-html','timestamp']
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -5,7 +5,7 @@
// Inline and block code styles
code,
pre {
pre.code {
padding: 0 3px 2px;
#font > #family > .monospace;
font-size: @baseFontSize - 2;
@@ -23,7 +23,7 @@ code {
}
// Blocks of code
pre {
pre.code {
display: block;
padding: (@baseLineHeight - 1) / 2;
margin: 0 0 @baseLineHeight / 2;
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,4 +0,0 @@
LazyLoad=function(x,h){function r(b,a){var c=h.createElement(b),d;for(d in a)a.hasOwnProperty(d)&&c.setAttribute(d,a[d]);return c}function k(b){var a=i[b],c,d;if(a){c=a.callback;d=a.urls;d.shift();l=0;if(!d.length){c&&c.call(a.context,a.obj);i[b]=null;j[b].length&&m(b)}}}function w(){if(!e){var b=navigator.userAgent;e={async:h.createElement("script").async===true};(e.webkit=/AppleWebKit\//.test(b))||(e.ie=/MSIE/.test(b))||(e.opera=/Opera/.test(b))||(e.gecko=/Gecko\//.test(b))||(e.unknown=true)}}function m(b,
a,c,d,s){var n=function(){k(b)},o=b==="css",f,g,p;w();if(a){a=typeof a==="string"?[a]:a.concat();if(o||e.async||e.gecko||e.opera)j[b].push({urls:a,callback:c,obj:d,context:s});else{f=0;for(g=a.length;f<g;++f)j[b].push({urls:[a[f]],callback:f===g-1?c:null,obj:d,context:s})}}if(!(i[b]||!(p=i[b]=j[b].shift()))){q||(q=h.head||h.getElementsByTagName("head")[0]);a=p.urls;f=0;for(g=a.length;f<g;++f){c=a[f];if(o)c=r("link",{charset:"utf-8","class":"lazyload",href:c,rel:"stylesheet",type:"text/css"});else{c=
r("script",{charset:"utf-8","class":"lazyload",src:c});c.async=false}if(e.ie)c.onreadystatechange=function(){var t=this.readyState;if(t==="loaded"||t==="complete"){this.onreadystatechange=null;n()}};else if(o&&(e.gecko||e.webkit))if(e.webkit){p.urls[f]=c.href;u()}else setTimeout(n,50*g);else c.onload=c.onerror=n;q.appendChild(c)}}}function u(){var b=i.css,a;if(b){for(a=v.length;a&&--a;)if(v[a].href===b.urls[0]){k("css");break}l+=1;if(b)l<200?setTimeout(u,50):k("css")}}var e,q,i={},l=0,j={css:[],js:[]},
v=h.styleSheets;return{css:function(b,a,c,d){m("css",b,a,c,d)},js:function(b,a,c,d){m("js",b,a,c,d)}}}(this,this.document);
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -335,6 +335,10 @@ Umbraco.Sys.registerNamespace("Umbraco.Application");
this._modal.push(dialog);
return dialog;
},
rootScope : function(){
return getRootScope();
},
closeModalWindow: function(rVal) {
//get our angular navigation service
Binary file not shown.

Before

Width:  |  Height:  |  Size: 763 KiB

After

Width:  |  Height:  |  Size: 116 KiB

@@ -0,0 +1,269 @@
angular.module('umbraco')
.constant('uiAceConfig', {})
.directive('uiAce', ['uiAceConfig', function (uiAceConfig) {
if (angular.isUndefined(window.ace)) {
throw new Error('ui-ace need ace to work... (o rly?)');
}
/**
* Sets editor options such as the wrapping mode or the syntax checker.
*
* The supported options are:
*
* <ul>
* <li>showGutter</li>
* <li>useWrapMode</li>
* <li>onLoad</li>
* <li>theme</li>
* <li>mode</li>
* </ul>
*
* @param acee
* @param session ACE editor session
* @param {object} opts Options to be set
*/
var setOptions = function(acee, session, opts) {
// Boolean options
if (angular.isDefined(opts.showGutter)) {
acee.renderer.setShowGutter(opts.showGutter);
}
if (angular.isDefined(opts.useWrapMode)) {
session.setUseWrapMode(opts.useWrapMode);
}
if (angular.isDefined(opts.showInvisibles)) {
acee.renderer.setShowInvisibles(opts.showInvisibles);
}
if (angular.isDefined(opts.showIndentGuides)) {
acee.renderer.setDisplayIndentGuides(opts.showIndentGuides);
}
if (angular.isDefined(opts.useSoftTabs)) {
session.setUseSoftTabs(opts.useSoftTabs);
}
// commands
if (angular.isDefined(opts.disableSearch) && opts.disableSearch) {
acee.commands.addCommands([
{
name: 'unfind',
bindKey: {
win: 'Ctrl-F',
mac: 'Command-F'
},
exec: function () {
return false;
},
readOnly: true
}
]);
}
// onLoad callback
if (angular.isFunction(opts.onLoad)) {
opts.onLoad(acee);
}
// Basic options
if (angular.isString(opts.theme)) {
acee.setTheme('ace/theme/' + opts.theme);
}
if (angular.isString(opts.mode)) {
session.setMode('ace/mode/' + opts.mode);
}
};
return {
restrict: 'EA',
require: '?ngModel',
link: function (scope, elm, attrs, ngModel) {
/**
* Corresponds the uiAceConfig ACE configuration.
* @type object
*/
var options = uiAceConfig.ace || {};
/**
* uiAceConfig merged with user options via json in attribute or data binding
* @type object
*/
var opts = angular.extend({}, options, scope.$eval(attrs.uiAce));
/**
* ACE editor
* @type object
*/
var acee = window.ace.edit(elm[0]);
/**
* ACE editor session.
* @type object
* @see [EditSession]{@link http://ace.c9.io/#nav=api&api=edit_session}
*/
var session = acee.getSession();
/**
* Reference to a change listener created by the listener factory.
* @function
* @see listenerFactory.onChange
*/
var onChangeListener;
/**
* Reference to a blur listener created by the listener factory.
* @function
* @see listenerFactory.onBlur
*/
var onBlurListener;
/**
* Calls a callback by checking its existing. The argument list
* is variable and thus this function is relying on the arguments
* object.
* @throws {Error} If the callback isn't a function
*/
var executeUserCallback = function () {
/**
* The callback function grabbed from the array-like arguments
* object. The first argument should always be the callback.
*
* @see [arguments]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments}
* @type {*}
*/
var callback = arguments[0];
/**
* Arguments to be passed to the callback. These are taken
* from the array-like arguments object. The first argument
* is stripped because that should be the callback function.
*
* @see [arguments]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments}
* @type {Array}
*/
var args = Array.prototype.slice.call(arguments, 1);
if (angular.isDefined(callback)) {
scope.$apply(function () {
if (angular.isFunction(callback)) {
callback(args);
} else {
throw new Error('ui-ace use a function as callback.');
}
});
}
};
/**
* Listener factory. Until now only change listeners can be created.
* @type object
*/
var listenerFactory = {
/**
* Creates a change listener which propagates the change event
* and the editor session to the callback from the user option
* onChange. It might be exchanged during runtime, if this
* happens the old listener will be unbound.
*
* @param callback callback function defined in the user options
* @see onChangeListener
*/
onChange: function (callback) {
return function (e) {
var newValue = session.getValue();
if (newValue !== scope.$eval(attrs.value) && !scope.$$phase && !scope.$root.$$phase) {
if (angular.isDefined(ngModel)) {
scope.$apply(function () {
ngModel.$setViewValue(newValue);
});
}
executeUserCallback(callback, e, acee);
}
};
},
/**
* Creates a blur listener which propagates the editor session
* to the callback from the user option onBlur. It might be
* exchanged during runtime, if this happens the old listener
* will be unbound.
*
* @param callback callback function defined in the user options
* @see onBlurListener
*/
onBlur: function (callback) {
return function () {
executeUserCallback(callback, acee);
};
}
};
attrs.$observe('readonly', function (value) {
acee.setReadOnly(value === 'true');
});
// Value Blind
if (angular.isDefined(ngModel)) {
ngModel.$formatters.push(function (value) {
if (angular.isUndefined(value) || value === null) {
return '';
}
else if (angular.isObject(value) || angular.isArray(value)) {
throw new Error('ui-ace cannot use an object or an array as a model');
}
return value;
});
ngModel.$render = function () {
session.setValue(ngModel.$viewValue);
};
}
// set the options here, even if we try to watch later, if this
// line is missing things go wrong (and the tests will also fail)
setOptions(acee, session, opts);
// Listen for option updates
scope.$watch( attrs.uiAce, function() {
opts = angular.extend({}, options, scope.$eval(attrs.uiAce));
// unbind old change listener
session.removeListener('change', onChangeListener);
// bind new change listener
onChangeListener = listenerFactory.onChange(opts.onChange);
session.on('change', onChangeListener);
// unbind old blur listener
//session.removeListener('blur', onBlurListener);
acee.removeListener('blur', onBlurListener);
// bind new blur listener
onBlurListener = listenerFactory.onBlur(opts.onBlur);
acee.on('blur', onBlurListener);
setOptions(acee, session, opts);
}, /* deep watch */ true );
// EVENTS
onChangeListener = listenerFactory.onChange(opts.onChange);
session.on('change', onChangeListener);
onBlurListener = listenerFactory.onBlur(opts.onBlur);
acee.on('blur', onBlurListener);
elm.on('$destroy', function () {
acee.session.$stopWorker();
acee.destroy();
});
scope.$watch(function() {
return [elm[0].offsetWidth, elm[0].offsetHeight];
}, function() {
acee.resize();
acee.renderer.updateFull();
}, true);
}
};
}]);
@@ -0,0 +1,283 @@
angular.module("umbraco")
.directive('gridRte', function (tinyMceService, stylesheetResource, angularHelper, assetsService, $q, $timeout) {
return {
scope: {
uniqueId: '=',
value: '=',
onClick: '&',
onFocus: '&',
onBlur: '&',
configuration:"="
},
template: "<textarea ng-model=\"value\" rows=\"10\" class=\"mceNoEditor\" id=\"{{uniqueId}}\"></textarea>",
replace: true,
link: function (scope, element, attrs) {
var initTiny = function () {
//we always fetch the default one, and then override parts with our own
tinyMceService.configuration().then(function (tinyMceConfig) {
//config value from general tinymce.config file
var validElements = tinyMceConfig.validElements;
var fallbackStyles = [{title: "Page header", block: "h2"}, {title: "Section header", block: "h3"}, {title: "Paragraph header", block: "h4"}, {title: "Normal", block: "p"}, {title: "Quote", block: "blockquote"}, {title: "Code", block: "code"}];
//These are absolutely required in order for the macros to render inline
//we put these as extended elements because they get merged on top of the normal allowed elements by tiny mce
var extendedValidElements = "@[id|class|style],-div[id|dir|class|align|style],ins[datetime|cite],-ul[class|style],-li[class|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align]";
var invalidElements = tinyMceConfig.inValidElements;
var plugins = _.map(tinyMceConfig.plugins, function (plugin) {
if (plugin.useOnFrontend) {
return plugin.name;
}
}).join(" ") + " autoresize";
//config value on the data type
var toolbar = ["code", "styleselect", "bold", "italic", "alignleft", "aligncenter", "alignright", "bullist", "numlist", "link", "umbmediapicker", "umbembeddialog"].join(" | ");
var stylesheets = [];
var styleFormats = [];
var await = [];
//queue file loading
if (typeof (tinymce) === "undefined") {
await.push(assetsService.loadJs("lib/tinymce/tinymce.min.js", scope));
}
if(scope.configuration && scope.configuration.toolbar){
toolbar = scope.configuration.toolbar.join(' | ');
}
if(scope.configuration && scope.configuration.stylesheets){
angular.forEach(scope.configuration.stylesheets, function(stylesheet, key){
stylesheets.push("/css/" + stylesheet + ".css");
await.push(stylesheetResource.getRulesByName(stylesheet).then(function (rules) {
angular.forEach(rules, function (rule) {
var r = {};
r.title = rule.name;
if (rule.selector[0] === ".") {
r.inline = "span";
r.classes = rule.selector.substring(1);
}else if (rule.selector[0] === "#") {
r.inline = "span";
r.attributes = { id: rule.selector.substring(1) };
}else {
r.block = rule.selector;
}
styleFormats.push(r);
});
}));
});
}else{
stylesheets.push("views/propertyeditors/grid/config/grid.default.rtestyles.css");
styleFormats = fallbackStyles;
}
//stores a reference to the editor
var tinyMceEditor = null;
$q.all(await).then(function () {
var uniqueId = scope.uniqueId;
//create a baseline Config to exten upon
var baseLineConfigObj = {
mode: "exact",
skin: "umbraco",
plugins: plugins,
valid_elements: validElements,
invalid_elements: invalidElements,
extended_valid_elements: extendedValidElements,
menubar: false,
statusbar: false,
relative_urls: false,
autoresize_min_height: 30,
toolbar: toolbar,
content_css: stylesheets.join(','),
style_formats: styleFormats
};
if (tinyMceConfig.customConfig) {
angular.extend(baseLineConfigObj, tinyMceConfig.customConfig);
}
//set all the things that user configs should not be able to override
baseLineConfigObj.elements = uniqueId;
baseLineConfigObj.setup = function (editor) {
//set the reference
tinyMceEditor = editor;
//enable browser based spell checking
editor.on('init', function (e) {
editor.getBody().setAttribute('spellcheck', true);
//hide toolbar by default
$(editor.editorContainer)
.find(".mce-toolbar")
.css("visibility", "hidden");
$timeout(function(){
if(scope.value === null){
editor.focus();
}
}, 500);
});
//when we leave the editor (maybe)
editor.on('blur', function (e) {
editor.save();
angularHelper.safeApply(scope, function () {
scope.value = editor.getContent();
var _toolbar = $(editor.editorContainer)
.find(".mce-toolbar");
if(scope.onBlur){
scope.onBlur();
}
_toolbar.css("visibility", "hidden");
});
});
// Focus on editor
editor.on('focus', function (e) {
angularHelper.safeApply(scope, function () {
var _toolbar = $(editor.editorContainer)
.find(".mce-toolbar");
if(scope.onFocus){
scope.onFocus();
}
var toolbarHeight = -_toolbar.height() - 2;
_toolbar
.css("visibility", "visible")
.css("margin-top", toolbarHeight + "px");
});
});
// Click on editor
editor.on('click', function (e) {
angularHelper.safeApply(scope, function () {
var _toolbar = $(editor.editorContainer)
.find(".mce-toolbar");
if(scope.onClick){
scope.onClick();
}
var toolbarHeight = -_toolbar.height() - 2;
_toolbar
.css("visibility", "visible")
.css("margin-top", toolbarHeight + "px");
});
});
//when buttons modify content
editor.on('ExecCommand', function (e) {
editor.save();
angularHelper.safeApply(scope, function () {
scope.value = editor.getContent();
});
});
// Update model on keypress
editor.on('KeyUp', function (e) {
editor.save();
angularHelper.safeApply(scope, function () {
scope.value = editor.getContent();
});
});
// Update model on change, i.e. copy/pasted text, plugins altering content
editor.on('SetContent', function (e) {
if (!e.initial) {
editor.save();
angularHelper.safeApply(scope, function () {
scope.value = editor.getContent();
});
}
});
editor.on('ObjectResized', function (e) {
var qs = "?width=" + e.width + "px&height=" + e.height + "px";
var srcAttr = $(e.target).attr("src");
var path = srcAttr.split("?")[0];
$(e.target).attr("data-mce-src", path + qs);
});
//Create the insert media plugin
tinyMceService.createMediaPicker(editor, scope);
//Create the embedded plugin
tinyMceService.createInsertEmbeddedMedia(editor, scope);
//Create the insert link plugin
//tinyMceService.createLinkPicker(editor, scope);
//Create the insert macro plugin
tinyMceService.createInsertMacro(editor, scope);
};
/** Loads in the editor */
function loadTinyMce() {
//we need to add a timeout here, to force a redraw so TinyMCE can find
//the elements needed
$timeout(function () {
tinymce.DOM.events.domLoaded = true;
tinymce.init(baseLineConfigObj);
}, 150, false);
}
loadTinyMce();
//here we declare a special method which will be called whenever the value has changed from the server
//this is instead of doing a watch on the model.value = faster
//scope.model.onValueChanged = function (newVal, oldVal) {
// //update the display val again if it has changed from the server;
// tinyMceEditor.setContent(newVal, { format: 'raw' });
// //we need to manually fire this event since it is only ever fired based on loading from the DOM, this
// // is required for our plugins listening to this event to execute
// tinyMceEditor.fire('LoadContent', null);
//};
//listen for formSubmitting event (the result is callback used to remove the event subscription)
var unsubscribe = scope.$on("formSubmitting", function () {
//TODO: Here we should parse out the macro rendered content so we can save on a lot of bytes in data xfer
// we do parse it out on the server side but would be nice to do that on the client side before as well.
scope.value = tinyMceEditor.getContent();
});
//when the element is disposed we need to unsubscribe!
// NOTE: this is very important otherwise if this is part of a modal, the listener still exists because the dom
// element might still be there even after the modal has been hidden.
scope.$on('$destroy', function () {
unsubscribe();
});
});
});
};
initTiny();
}
};
});
@@ -0,0 +1,94 @@
angular.module('umbraco.directives')
.directive('checklistModel', ['$parse', '$compile', function($parse, $compile) {
// contains
function contains(arr, item) {
if (angular.isArray(arr)) {
for (var i = 0; i < arr.length; i++) {
if (angular.equals(arr[i], item)) {
return true;
}
}
}
return false;
}
// add
function add(arr, item) {
arr = angular.isArray(arr) ? arr : [];
for (var i = 0; i < arr.length; i++) {
if (angular.equals(arr[i], item)) {
return arr;
}
}
arr.push(item);
return arr;
}
// remove
function remove(arr, item) {
if (angular.isArray(arr)) {
for (var i = 0; i < arr.length; i++) {
if (angular.equals(arr[i], item)) {
arr.splice(i, 1);
break;
}
}
}
return arr;
}
// http://stackoverflow.com/a/19228302/1458162
function postLinkFn(scope, elem, attrs) {
// compile with `ng-model` pointing to `checked`
$compile(elem)(scope);
// getter / setter for original model
var getter = $parse(attrs.checklistModel);
var setter = getter.assign;
// value added to list
var value = $parse(attrs.checklistValue)(scope.$parent);
// watch UI checked change
scope.$watch('checked', function(newValue, oldValue) {
if (newValue === oldValue) {
return;
}
var current = getter(scope.$parent);
if (newValue === true) {
setter(scope.$parent, add(current, value));
} else {
setter(scope.$parent, remove(current, value));
}
});
// watch original model change
scope.$parent.$watch(attrs.checklistModel, function(newArr, oldArr) {
scope.checked = contains(newArr, value);
}, true);
}
return {
restrict: 'A',
priority: 1000,
terminal: true,
scope: true,
compile: function(tElement, tAttrs) {
if (tElement[0].tagName !== 'INPUT' || !tElement.attr('type', 'checkbox')) {
throw 'checklist-model should be applied to `input[type="checkbox"]`.';
}
if (!tAttrs.checklistValue) {
throw 'You should provide `checklist-value`.';
}
// exclude recursion
tElement.removeAttr('checklist-model');
// local scope var storing individual checkbox model
tElement.attr('ng-model', 'checked');
return postLinkFn;
}
};
}]);
@@ -0,0 +1,82 @@
/**
* @ngdoc service
* @name umbraco.resources.stylesheetResource
* @description service to retrieve available stylesheets
*
*
**/
function templateResource($q, $http, umbRequestHelper) {
//the factory object returned
return {
/**
* @ngdoc method
* @name umbraco.resources.stylesheetResource#getAll
* @methodOf umbraco.resources.stylesheetResource
*
* @description
* Gets all registered stylesheets
*
* ##usage
* <pre>
* stylesheetResource.getAll()
* .then(function(stylesheets) {
* alert('its here!');
* });
* </pre>
*
* @returns {Promise} resourcePromise object containing the stylesheets.
*
*/
getAll: function () {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"templateApiBaseUrl",
"GetAll")),
'Failed to retrieve stylesheets ');
},
/**
* @ngdoc method
* @name umbraco.resources.stylesheetResource#getRulesByName
* @methodOf umbraco.resources.stylesheetResource
*
* @description
* Returns all defined child rules for a stylesheet with a given name
*
* ##usage
* <pre>
* stylesheetResource.getRulesByName("ie7stylesheet")
* .then(function(rules) {
* alert('its here!');
* });
* </pre>
*
* @returns {Promise} resourcePromise object containing the rules.
*
*/
getById: function (id) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"templateApiBaseUrl",
"GetById",
[{ id: id }])),
'Failed to retreive template ');
},
saveAndRender: function(html, templateId, pageId){
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"templateApiBaseUrl",
"PostSaveAndRender"),
{templateId: templateId, pageId: pageId, html: html }),
'Failed to retreive template ');
}
};
}
angular.module('umbraco.resources').factory('templateResource', templateResource);
@@ -0,0 +1,14 @@
angular.module('umbraco.services')
.factory('gridService', function ($http, $q){
var configPath = "views/propertyeditors/grid/config/grid.editors.config.js";
var service = {
getGridEditors: function () {
return $http.get(configPath);
}
};
return service;
});
+569 -40
View File
@@ -1,56 +1,585 @@
// Gridview
// -------------------------
.editor {
border: 1px solid #cdcdcd;
text-align: center;
line-height: 100px;
position: relative;
-webkit-transition: all 500ms ease-in-out;
-moz-transition: all 500ms ease-in-out;
-ms-transition: all 500ms ease-in-out;
-o-transition: all 500ms ease-in-out;
transition: all 500ms ease-in-out;
margin: 0 0 1.5% 0
// Sortabel
// -------------------------
.usky-grid .ui-sortable-helper {
border: dashed 1px #000;
background: #ccc;
opacity: 0.4;
height: 50px !important;
overflow: hidden;
padding: 5px;
}
.usky-grid .ui-sortable-helper *{
border: none !important;
background: none !important;
color: #999 !important;
padding: 0 !important;
margin: 0 !important;
}
.usky-grid .ui-sortable-helper .cell-tools{
display: none !important;
}
.usky-grid .ui-sortable-placeholder{
border: 2px dashed @grayLight;
padding: 20px;
font-family: icomoon;
text-align: center;
font-size: 85px;
line-height: 65px;
color: @grayLighter;
vertical-align: middle;
}
.usky-grid .ui-sortable-placeholder:hover{
border-color: @gray;
}
.usky-grid .ui-sortable-placeholder:before{
content: "\e1bd";
}
// utils
// -------------------------
.usky-grid-width {
margin: 0 auto;
width:100%;
}
.usky-grid .right {
float:right;
}
// general layout components
// -------------------------
.usky-grid .tb {
display:table;
width: 100%;
table-layout: fixed;
}
.usky-grid .td {
display:table-cell;
vertical-align:top;
border-right:1px dashed rgba(182, 182, 182, 0.0);
}
.usky-grid .tb:hover .td{
border-right:1px dashed rgba(182, 182, 182, 0.3);
}
.usky-grid .td.last {
border-right:1px dashed rgba(182, 182, 182, 0.0) !important;
}
.usky-grid .tr {
display:table-row;
}
.usky-grid .td2{
width: 50%;
}
.usky-grid .td3{
width: 33.33%;
}
.usky-grid .td4{
width: 25%;
}
.usky-grid .td5{
width: 20%;
}
.usky-grid .td6{
width: 16.6%;
}
.usky-grid .middle {
text-align: center;
}
.usky-grid .mainTb {
border-collapse: separate;
}
.usky-grid .mainTd {
position:relative;
}
// COLUMN
// -------------------------
.usky-grid .usky-column {
}
.usky-grid .usky-column.last {
}
// ROW
// -------------------------
.usky-grid .usky-row{
position: relative;
border: 1px dashed rgba(0,0,0,0);
}
.editor:hover {
border-color: #000
// CELL
// -------------------------
.usky-grid .usky-cell{
position: relative;
border: 1px dashed rgba(0,0,0,0);
}
.editor:hover i {
opacity: 1
.usky-grid .cell-tools {
transition: all .20s ease-in-out;
-moz-transition: all .20s ease-in-out;
-webkit-transition: all .20s ease-in-out;
position: absolute;
bottom: 0;
top: 0;
left: 0;
right: 0;
}
.editor i {
position: absolute;
top: 0;
font-size: 20px;
color: @white;
background: @black;
padding: 10px;
-webkit-transition: all 500ms ease-in-out;
-moz-transition: all 500ms ease-in-out;
-ms-transition: all 500ms ease-in-out;
-o-transition: all 500ms ease-in-out;
transition: all 500ms ease-in-out;
opacity: 0
.usky-grid .cell-tools-add {
position: absolute;
text-align: center;
bottom: -20px;
left: 0;
right: 0;
z-index: 500;
}
.editor i:hover {
.usky-grid .cell-tools-add-single i{
font-size: 85px;
color: @grayLight;
display: block;
line-height: 55px;
margin-bottom: 10px;
}
.editor i.icon-move {
left: 0;
cursor: move
}
.usky-grid .cell-tools-add-single a:hover i{color: @gray;}
.editor i.icon-edit {
right: 36px;
cursor: pointer
.usky-grid .cell-tools-add-single a{text-decoration: none;}
.usky-grid .cell-tools-add-single p{
display: none;
}
.editor i.icon-trash {
right: 0;
cursor: pointer
}
.usky-grid .single-editor-rte p{
display: block;
}
.usky-grid .single-editor-rte i, .usky-grid .single-editor-rte div{
display: none;
}
.usky-grid .cell-tools-remove {
display:inline-block;
position: absolute;
top: -20px;
right: -20px;
text-align: right;
z-index: 500;
}
.usky-grid .cell-tools-remove .iconBox:hover, .usky-grid .cell-tools-remove .iconBox:hover *{
background: @red !important;
border-color: @red !important;
}
.usky-grid .cell-tools-move {
display:inline-block;
position: absolute;
top: 20px;
right: -20px;
z-index: 500;
cursor: move
}
// CONTROL
// -------------------------
.usky-grid .usky-control {
transition: all .20s ease-in-out;
-moz-transition: all .20s ease-in-out;
-webkit-transition: all .20s ease-in-out;
position:relative;
display:block;
border: 1px dashed rgba(255, 0, 0, .0);
border-bottom-width: 2px;
-webkit-background-clip: padding-box; /* for Safari */
background-clip: padding-box; /* for IE9+, Firefox 4+, Opera, Chrome */
}
.usky-grid .usky-control:hover{
border-bottom:2px solid rgba(182, 182, 182, 0.3);
}
.usky-grid .usky-control-placeholder:hover{
border-bottom:2px solid rgba(182, 182, 182, 0.0) !important;
}
.usky-grid .removingControl{
border: 1px dashed @red !important;
}
.usky-grid .movingControl{
border: 1px dashed @blue !important;
}
// CONTROL PLACEHOLDER
// -------------------------
.usky-grid .usky-control-placeholder {
min-height: 100px;
position: relative;
text-align: center;
text-align: -moz-center;
cursor: text;
}
.usky-grid .usky-control-placeholder .placeholder{
font-size: 14px; opacity: 0.7; text-align: left;
padding: 5px;
border:1px solid rgba(182, 182, 182, 0.3);
height: 50px;
}
.usky-grid .usky-control-placeholder:hover .placeholder{
border:1px solid rgba(182, 182, 182, 0.8);
}
// EDITOR PLACEHOLDER
// -------------------------
.usky-grid .usky-editor-placeholder{
min-height: 65px;
padding: 20px;
padding-bottom: 30px;
position: relative;
background-color: white;
border: 4px dashed @grayLight;
text-align: center;
text-align: -moz-center;
}
.usky-grid .usky-editor-placeholder i{
color: @grayLight;
font-size: 85px;
line-height: 85px;
display: block;
margin-bottom: 10px;
}
// Form elements
// -------------------------
.usky-grid textarea.textstring{
display: block;
overflow: hidden;
border: none;
background: #fff;
outline: none;
width: 99%;
resize: none;
color: @gray;
}
.usky-grid .usky-cell-rte textarea{
display: none !important
}
.usky-grid .usky-cell-media .caption{
display: block;
overflow: hidden;
border: none;
background: #fff;
outline: none;
width: 99%;
resize: none;
font-style: italic;
}
.usky-grid .cellPanelRte {
min-height:60px;
}
// ICONS
// -------------------------
.usky-grid .iconBox {
padding: 4px 7px 4px 7px;
display: inline-block;
cursor: pointer;
border-radius: 200px;
background: rgba(255,255,255, 1);
border:1px solid rgb(182, 182, 182);
margin: 2px;
}
.usky-grid .iconBox:hover, .usky-grid .iconBox:hover *{
background: @blue !important;
color: white !important;
border-color: @blue !important;
}
.usky-grid .iconBox a:hover {
text-decoration:none;
color: white !important;
}
.usky-grid .iconBox.selected {
-webkit-appearance: none;
background-image: -moz-linear-gradient(top,#e6e6e6,#bfbfbf);
background-image: -webkit-gradient(linear,0 0,0 100%,from(#e6e6e6),to(#bfbfbf));
background-image: -webkit-linear-gradient(top,#e6e6e6,#bfbfbf);
background-image: -o-linear-gradient(top,#e6e6e6,#bfbfbf);
background-image: linear-gradient(to bottom,#e6e6e6,#bfbfbf);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe6e6e6',endColorstr='#ffbfbfbf',GradientType=0);
zoom: 1;
border-color: #bfbfbf #bfbfbf #999;
border-color: rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);
-webkit-box-shadow: inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);
-moz-box-shadow: inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);
box-shadow: inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
background: transparent;
}
.usky-grid .iconBox i {
font-size:16px !important;
color: #5F5F5F;
}
.usky-grid ul {
display:inline-block;
list-style:none;
padding:0;
margin: 10px 0 0 0;
text-align:center;
}
.usky-grid .help-text {
background: @grayLighter;
color: @gray;
font-size: 14px;
padding: 10px 20px 10px 20px;
border-radius: 15px;
display: inline-block;
clear: both;
}
.usky-grid ul li {
display: inline-block;
width: 120px;
margin: 8px 8px 0px 8px;
}
// TINYMCE EDITOR
// -------------------------
.usky-grid .mce-panel {
border:none !important;
clear:both;
}
.usky-grid .mce-btn button {
padding: 8px 6px;
line-height: inherit;
}
.usky-grid .mce-toolbar {
border: 1px solid rgba(207, 207, 207, 0.7);
background-color: rgba(250, 250, 250, 1);
z-index: 100;
display: inline-block;
float: left;
padding: -1px;
position: absolute;
margin: -1px -1px 0 -1px;
-webkit-box-shadow: 2px 2px 10px 0px rgba(50, 50, 50, 0.14);
-moz-box-shadow: 2px 2px 10px 0px rgba(50, 50, 50, 0.14);
box-shadow: 2px 2px 10px 0px rgba(50, 50, 50, 0.14);
z-index: 9999999;
}
.mce-flow-layout-item {
margin: 0;
}
.usky-grid .mce-panel {
background: transparent !important;
}
.usky-grid .mce-floatpanel {
background-color: #F7F7F7 !important;
}
// MEDIA EDITOR
// -------------------------
.usky-grid .fullSizeImage {
width:100%
}
/**************************************************************************************************/
/* Width */
/**************************************************************************************************/
.usky-grid .boxWidth {
text-align:right;
margin-bottom: 10px;
}
.usky-grid .boxWidth input {
text-align: center;
width: 40px;
}
.usky-grid .boxWidth label {
font-size: 10px;
padding: 0;
margin: 5px 5px 0 0;
color: #808080;
}
/**************************************************************************************************/
/* Margin control */
/**************************************************************************************************/
.usky-grid .usky-cell{
}
.usky-grid .usky-row{
}
.usky-grid .usky-control{
margin: 30px;
padding: 5px;
}
.usky-grid .usky-templates-columns{
margin-top: 30px;
}
/**************************************************************************************************/
/* template */
/**************************************************************************************************/
.usky-grid .uSky-templates {
text-align:center;
overflow:hidden;
width:100%;
}
.usky-grid .uSky-templates-template {
display:inline-block;
width:100px;
padding-right: 30px;
margin: 20px;
}
.usky-grid .uSky-templates-template .tb:hover {
border:5px solid @blue;
}
.usky-grid .uSky-templates-template .tb {
width:100%;
height:150px;
padding:10px;
background-color: @grayLighter;
border: 5px solid @grayLight;
cursor:pointer;
position: relative;
}
.usky-grid .uSky-templates-template .tr {
height: 100%;
position: relative;
}
.usky-grid .uSky-templates-template .tb .uSky-templates-column {
height:100%;
border: 1px dashed @grayLight;
border-right: none;
}
.usky-grid .uSky-templates-template .tb .uSky-templates-column.last {
border-right: 1px dashed @grayLight !important;
}
/**************************************************************************************************/
/* template column */
/**************************************************************************************************/
.usky-grid .usky-templates-columns .td{
border: none !important;
vertical-align: middle;
}
.usky-grid .usky-templates-columns .td i{
color: @gray;
opacity: 0.8
}
.usky-grid .mainTbpt:hover {
border-color:@blue;
}
.usky-grid .mainTbpt {
cursor:pointer;
border-spacing: 5px;
border-collapse: separate;
height: 35px;
border: 2px solid @grayLight;
margin:0px;
background-color: @grayLighter;
}
.usky-grid .mainTdpt {
height: 35px;
border: 1px dashed @grayLight;
margin:0px;
background-color: rgba(228, 228, 228, 0.41);
}
@@ -6,6 +6,8 @@
/*wft ms?*/
*{ -ms-touch-action: none;}
.ace_editor { height: 200px; }
.nounderline {text-decoration: none !important}
.nounderline:hover {text-decoration: underline !important}
.nounderline * {text-decoration: none !important; border: none}
@@ -0,0 +1,784 @@
html {
height: 100%;
}
body {
height: 100%;
padding: 0;
margin: 0;
text-align: center;
background-color: #EEEEEE;
-webkit-transition: all .3s linear;
-moz-transition: all .3s linear;
transition: all .3s linear;
}
body.open {
padding-left:275px;
}
h2 {
margin-left: 10px;
font-size: 14px;
color: #d9d9d9;
text-align: center;
}
.tuning #TuningBg {
position: fixed;
top: 0;
left: 0;
z-index: 999;
width: 275px;
height: 100%;
padding: 0;
margin-left: -275px;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 13px;
line-height: 16px;
background: #1D1D1D;
-webkit-box-shadow: 0 0 14px rgba(0, 0, 0, 0);
-moz-box-shadow: 0 0 14px rgba(0, 0, 0, 0);
box-shadow: 0 0 14px rgba(0, 0, 0, 0);
-webkit-transition: all 0.2s ease-in-out;
-moz-transition: all 0.2s ease-in-out;
transition: all 0.2s ease-in-out;
}
.show {
display: block !important;
}
.wait {
margin: 10px 0 0 0;
}
.tuning .tuning-wrapper {
height: 100%;
overflow-y: auto;
text-align: left;
}
.tuning .wrapper-pading {
padding: 10px;
}
.tuning #TuningBg.open {
margin-left: 0;
-webkit-box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
-moz-box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
}
.tuning .colpick {
z-index: 99999999;
}
.tuning .nav {
padding: 0 10px;
margin-top: 12px;
margin-bottom: 30px;
text-transform: uppercase;
border-bottom: none;
}
.tuning .nav-tabs > li > a {
color: #d9d9d9 !important;
background-color: #1D1D1D !important;
border: none;
border-bottom: 3px solid transparent;
}
.tuning .nav-tabs > li.active > a,
.tuning .nav-tabs > li.active > a:hover,
.tuning .nav-tabs > li.active > a:focus {
color: #d9d9d9;
cursor: default;
background-color: #1D1D1D !important;
border: none;
border-bottom: 3px solid #d9d9d9;
}
.tuning .nav-tabs > li > a:hover {
color: #d9d9d9 !important;
background-color: #1D1D1D !important;
border: none !important;
border-bottom: 3px solid #d9d9d9 !important;
}
.tuning .nav > li > a:hover,
.tuning .nav > li > a:focus {
color: #d9d9d9 !important;
text-decoration: none !important;
background-color: #1D1D1D !important;
border: none !important;
border-bottom: 3px solid #d9d9d9 !important;
}
.tuning .nav-tabs > li > a {
border-bottom: 3px solid transparent;
/*border-radius: 2px 2px 0 0;*/
}
.nav-tabs > li {
float: left;
margin-top: 1px;
margin-bottom: -1px;
}
.tuning .nav-tabs > li > a {
color: #ffffff;
cursor: pointer;
}
.tuning .nav > li > a {
padding: 5px 7px;
}
.tuning .panel-title > a span {
display: block;
}
.tuning .panel-title > a:hover {
text-decoration: none;
}
/* .panel-heading */
.tab-pane {
border-bottom: 1px solid #343434;
}
.tuning .panel-heading {
padding: 0;
}
.tuning .panel-default > .panel-heading {
color: #d9d9d9;
background-color: #1D1D1D;
border-left: 4px transparent solid;
border-color: none;
border-radius: 0;
}
.tuning .panel-default > .panel-heading:hover,
.tuning .panel-default.selected > .panel-heading {
color: #d9d9d9;
text-decoration: none;
background-color: rgba(255, 255, 255, 0.09);
border-left: 4px #f57020 solid;
-webkit-transition: all .3s linear;
-moz-transition: all .3s linear;
transition: all .3s linear;
}
.tuning .panel-title > a {
display: block;
padding: 5px 0 5px 17px;
font-size: 13px;
line-height: 22px;
color: #d9d9d9;
cursor: pointer;
}
.tuning .panel-default > .panel-heading h4 {
margin: 0 0 0 -4px;
border-top: 1px solid #343434;
}
.panel-group .panel + .panel {
margin-top: 0;
}
.tuning .panel-default.selected {
margin: 1px 0 0 0;
}
.tuning .panel-default.selected > .panel-heading h4 {
margin: 0 0 0 0;
color: #d9d9d9;
border-top: none;
border-bottom: 1px solid #6D6D6D;
}
.tuning .panel-default.selected > .panel-heading h4 a {
padding: 5px 0 5px 13px;
}
/* panel */
.tuning .panel-default {
background-color: #1D1D1D;
border: 0 solid rgba(217, 217, 217, 0.5);
border-radius: 0;
}
.tuning .panel-group .panel {
border-radius: 1px;
}
.panel-collapse {
background-color: rgba(255, 255, 255, 0.09);
border-left: 4px #f57020 solid;
}
.tuning .btn-default-save,
.tuning .btn-default-save:focus {
float: right;
margin-top: 20px;
margin-right: 20px;
color: #fff;
background-color: #53a93f;
border-color: #ccc;
border-radius: 2px;
}
.tuning .btn-default-save:hover {
color: #fff;
background-color: rgba(83, 169, 63, 0.81);
}
.tuning .btn-default-delete,
.tuning .btn-default-delete:focus {
float: right;
margin-top: 20px;
margin-right: 10px;
color: #343434;
background: #e4e4e4;
border-color: #ccc;
border-radius: 2px;
}
.tuning .btn-default-delete:hover {
color: #343434;
background-color: rgba(228, 228, 228, 0.65);
}
.tuning .field-title {
float: left;
margin-right: 10px;
font-size: 11px;
color: #d9d9d9;
}
.tuning .div-field {
margin-bottom: 10px;
overflow: hidden;
clear: both;
}
.tuning .sp-replacer {
float: left;
padding: 0;
margin-top: 0;
background: none;
border: solid 1px #d9d9d9;
}
.tuning .panel-body {
border-top: none !important;
}
.tuning select {
font-size: 11px;
}
.tuning .sp-dd {
display: none;
}
.tuning .sp-preview {
width: 17px;
height: 17px;
margin-right: 0;
border: none;
}
.tuning .box-slider {
padding: 7px 2px 12px 2px;
clear: both;
}
.tuning .ui-widget-content {
background: rgba(255, 255, 255, 0.09) !important;
border: 0px solid #fff !important;
border-radius: 2px !important;
}
.tuning .ui-slider-horizontal {
float: left;
width: 155px;
height: .3em !important;
}
.tuning .ui-slider .ui-slider-handle {
top: -0.3em;
width: 0.3em !important;
height: 0.9em !important;
margin-left: 0;
color: #d9d9d9 !important;
cursor: pointer;
border-top-right-radius: 1px;
border-bottom-right-radius: 1px;
border-bottom-left-radius: 1px;
border-top-left-radius: 1px;
}
.tuning .ui-slider .ui-slider-handle:hover,
.tuning .ui-slider .ui-slider-handle:focus {
color: #d9d9d9 !important;
}
.tuning .slider-input {
float: right;
width: 25px;
padding: 0;
margin-top: -9px;
margin-right: 1px;
font-size: 11px;
color: #d9d9d9;
text-align: right;
background-color: transparent;
border: none;
}
.tuning .slider-span {
float: right;
margin: -9px 0 0 0;
font-size: 11px;
color: #d9d9d9;
}
@-moz-document url-prefix() {
.tuning .slider-input {
margin-top: -6px;
}
}
.tuning .imagePickerPreviewContainer {
float: left;
}
.tuning .imagePickerPreview {
float: left;
width: 50px;
height: 30px;
clear: both;
color: rgba(217, 217, 217, 0.5);
text-align: center;
cursor: pointer;
-webkit-background-size: cover;
-moz-background-size: cover;
-o-background-size: cover;
background-size: cover;
border: 1px solid rgba(217, 217, 217, 0.5);
}
.tuning .imagePickerPreview:hover {
color: #d9d9d9;
border: 1px solid #d9d9d9;
}
.tuning .imagePickerPreview i {
font-size: 14px;
line-height: 30px;
cursor: pointer;
}
.tuning .colorPickerDelete {
float: left;
margin-top: 5px;
margin-left: 3px;
font-size: 10px;
color: rgba(217, 217, 217, 0.5);
cursor: pointer;
}
.tuning .colorPickerDelete:hover {
color: #d9d9d9;
}
.tuning .tuning-tools {
position: absolute;
right: -28px;
margin: 25px 0 0 0;
}
.tuning #TuningBg.open .tuning-tools {
right: -33px;
}
.tuning .tuning-tools div {
display: block;
padding: 8px 8px 8px 9px;
margin: 5px 0 0 0;
font-size: 16px;
color: rgba(255, 255, 255, 0.8);
cursor: pointer;
background: #353535;
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
-webkit-box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
-moz-box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
}
.tuning .tuning-tools div:hover,
.tuning .tuning-tools div.selected {
color: rgba(255, 255, 255, 0.8);
background: #1D1D1D;
}
/* image picker */
.tuningImagePicker {
height: 500px;
padding: 0;
margin-top: 10px;
overflow: auto;
text-align: left;
list-style: none;
}
.tuningImagePicker ul {
padding: 0;
margin: 0;
margin-left: 17px;
list-style: none;
}
.tuningImagePicker li {
display: inline-block;
margin-bottom: 10px;
}
.tuningImagePicker ul.media-items li {
margin-right: 17px;
}
.tuningImagePicker .media-preview {
position: relative;
display: inline-block;
width: 110px;
height: 60px;
cursor: pointer;
-webkit-background-size: cover;
-moz-background-size: cover;
-o-background-size: cover;
background-size: cover;
border: 2px solid #F3F2F2;
}
.tuningImagePicker .media-preview .folder {
position: absolute;
top: -5px;
left: 0;
width: 100%;
font-size: 40px;
color: gainsboro;
text-align: center;
}
.tuningImagePicker .media-preview .folder-name {
margin-top: -5px;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 13px;
line-height: 0;
color: grey;
}
.tuningImagePicker .media-preview:hover,
.media-preview.selected {
border: 2px solid #84B8F0;
}
.bodyTuningImagePicker {
padding: 10px 20px 0 15px;
}
.tuningImagePicker .modal-dialog {
width: 618px;
font-weight: normal;
}
.bodyTuningImagePicker .breadcrumb {
margin-top: 4px;
margin-bottom: 10px;
font-size: 16px;
text-align: left;
}
.bodyTuningImagePicker .breadcrumb > li {
display: inline;
}
.bodyTuningImagePicker .breadcrumb > li a {
cursor: pointer;
}
.bodyTuningImagePicker .breadcrumb input {
padding: 0;
margin: -4px -4px;
}
.bodyTuningImagePicker .fileinput-button {
position: absolute;
top: 10px;
right: 10px;
font-size: 19px;
}
.bodyTuningImagePicker input.input-media {
position: absolute;
top: 0;
right: 0;
margin: 0;
font-size: 23px;
cursor: pointer;
opacity: 0;
filter: alpha(opacity=0);
transform: translate(-300px, 0) scale(4);
direction: ltr;
}
.bodyTuningImagePicker .breadcrumb a.disabled,
.bodyTuningImagePicker .breadcrumb a.disabled:hover {
color: grey;
text-decoration: none;
cursor: default;
}
.tuningImagePicker h3 {
font-size: 18px;
color: #555555;
}
/* font picker */
.tuning-fontfamilypicker .show {
margin-top: 10px;
font-size: 32px;
line-height: 34px;
color: #242424;
}
.tuning .fontFamilyPickerPreview {
float: left;
width: 90%;
min-height: 50px;
padding: 8px;
margin-top: 4px;
clear: both;
font-size: 18px;
color: rgba(255, 255, 255, 0.8);
cursor: pointer;
background-color: rgba(0, 0, 0, 0.28);
border: 1px solid rgba(217, 217, 217, 0.5);
}
.tuning .fontFamilyPickerPreview span {
font-size: 32px;
line-height: 32px;
}
.tuning .fontFamilyPickerPreview:hover {
color: #d9d9d9;
border: 1px solid #d9d9d9;
}
.tuning-fontfamilypicker {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 14px;
}
.tuning .palette {
padding: 0;
margin: 20px 0 20px 20px;
list-style: none;
}
.tuning .palette li {
margin: 10px 0 0 0;
overflow: hidden;
clear: both;
cursor: pointer;
background-color: rgba(0, 0, 0, 0.12);
}
.tuning .palette div {
display: inline-block;
float: left;
border: 1px solid rgba(255, 255, 255, 0.61);
}
.tuning .palette li:hover {
background-color: rgba(0, 0, 0, 0.4);
}
.tuning .palette li:hover div {
border: 1px solid #ffffff;
}
.tuning .palette li:hover p {
color: #ffffff;
}
.tuning .palette div span {
display: table-cell;
width: 30px;
height: 30px;
padding: 0 0 0 0;
margin: 0;
border: 1px solid rgba(0, 0, 0, 0.55);
}
.tuning .palette p {
float: left;
margin: 7px 0 0 10px;
color: rgba(255, 255, 255, 0.57);
}
/******* IFRAME *******/
.desktop {
width: 100%;
height: 100%;
margin: 0 auto;
overflow: hidden;
}
.laptop {
width: 1366px;
height: 768px;
}
.iPad-portrait {
width: 769px;
height: 929px;
}
.iPad-landscape {
width: 1024px;
height: 675px;
}
.smartphone-portrait {
width: 320px;
height: 504px;
}
.smartphone-landscape {
width: 480px;
height: 256px;
}
.border {
margin: 75px auto;
background-color: #ffffff;
border-radius: 10px;
opacity: 1.0;
box-shadow: 0 0 0 29px #fff, 0 0 0 30px #dee0e3;
transition: all 0.5s ease-in-out;
}
iframe {
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
overflow-x: hidden;
overflow-y: hidden;
}
.flip:before {
-webkit-transform: rotate(90deg);
-moz-transform: rotate(90deg);
-o-transform: rotate(90deg);
}
/******* preview-device *******/
ul.preview-device {
padding: 0;
margin: 0;
list-style: none;
border-top: 1px solid #343434;
}
ul.preview-device li {
display: block;
overflow: hidden;
clear: both;
text-align: center;
cursor: pointer;
border-left: 4px solid transparent;
-webkit-transition: all .3s linear;
-moz-transition: all .3s linear;
transition: all .3s linear;
}
ul.preview-device li div {
padding: 10px;
border-bottom: 1px solid #343434;
}
ul.preview-device li:hover,
ul.preview-device li.selected {
background-color: rgba(255, 255, 255, 0.09);
border-left: 4px #f57020 solid;
}
ul.preview-device li i {
font-size: 80px;
line-height: 80px;
color: #d9d9d9;
}
ul.preview-device li span {
display: block;
color: #d9d9d9;
}
/******* font-face *******/
#speechbubble {
position: absolute;
right: 0;
bottom: 40px;
left: 0;
z-index: 1000;
display: none;
padding: 0;
margin: auto;
margin-left: 300px;
text-align: left;
background: none;
border: none;
border-bottom: none;
}
#speechbubble p {
position: relative;
padding: 8px 30px 8px 20px;
margin: auto;
margin-top: 5px;
font-size: 12px;
color: #ffffff;
text-shadow: none;
background-color: #46a546;
border: none;
border-color: transparent;
border-radius: 5px;
}
/******* font-face *******/
@font-face {
src: url('/Umbraco/assets/fonts/helveticons/helveticons.eot') !important;
src: url('/Umbraco/assets/fonts/helveticons/helveticons.eot?#iefix') format('embedded-opentype'), url('/Umbraco/assets/fonts/helveticons/helveticons.ttf') format('truetype'), url('/Umbraco/assets/fonts/helveticons/helveticons.svg#icomoon') format('svg') !important;
}
@@ -0,0 +1,22 @@
LazyLoad.js([
'/Umbraco/lib/jquery/jquery-2.0.3.min.js',
'/Umbraco/lib/jquery/jquery-ui-1.10.4.custom.min.js',
'/Umbraco/lib/angular/1.1.5/angular.min.js',
'/Umbraco/lib/underscore/underscore.js',
'/Umbraco/js/app.js',
'/Umbraco/js/umbraco.resources.js',
'/Umbraco/js/umbraco.services.js',
'/Umbraco/js/umbraco.security.js',
'/Umbraco/ServerVariables',
'/Umbraco/lib/angular-bootstrap/ui-bootstrap-tpls-0.10.0.min.js',
'/Umbraco/lib/spectrum/spectrum.js',
'http://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js',
'/Umbraco/js/tuning.config.js',
'/Umbraco/js/tuning.palettes.js',
'/umbraco/js/tuning.panel.js'
], function () {
jQuery(document).ready(function () {
angular.bootstrap(document, ['umbraco.tuning']);
});
}
);
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,238 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link href="/Umbraco/lib/bootstrap/css/bootstrap.min.3.0.1.css" type="text/css" rel="stylesheet" />
<link href="/Umbraco/assets/css/tuning.panelStyles.css" type="text/css" rel="stylesheet" />
<link href="/Umbraco/lib/spectrum/spectrum.css" type="text/css" rel="stylesheet" />
<link href="/Umbraco/lib/jquery/jquery-ui-1.10.4.custom.min.css" type="text/css" rel="stylesheet" />
</head>
<body id="tuningPanel" ng-class="{open:isOpen}" ng-controller="Umbraco.tuningController">
<div class="wait" ng-show="!frameLoaded">Loading ...</div>
<div id="demo-iframe-wrapper" ng-show="frameLoaded" class="{{previewDevice.css}}">
<iframe id="resultFrame" ng-src="{{pageId}}" frameborder="0"></iframe>
</div>
<div class="tuning">
<div id="TuningBg" ng-class="{open:isOpen}" style="display: block;">
<div class="tuning-tools">
<div ng-click="togglePanel()">
<i ng-if="isOpen" class="icon icon-left-double-arrow"></i>
<i ng-if="!isOpen" class="icon icon-right-double-arrow"></i>
</div>
<div ng-if="isOpen" ng-click="settingOpen('previewDevice')" ng-class="{selected:settingIsOpen=='previewDevice', show:frameLoaded}" style="display:none">
<i class="icon icon-display"></i>
</div>
<div ng-if="isOpen" ng-click="settingOpen('setting')" ng-class="{selected:settingIsOpen=='setting', show:enableTuning > 0}" style="display:none">
<i class="icon icon-paint-roller"></i>
</div>
<div ng-if="isOpen" ng-click="settingOpen('presetColors')" ng-class="{selected:settingIsOpen=='presetColors', show:enableTuning > 0}" style="display:none">
<i class="icon icon-palette"></i>
</div>
</div>
<div class="tuning-wrapper">
<div ng-show="settingIsOpen == 'previewDevice'">
<h2>DEVICES</h2>
<ul class="preview-device">
<li ng-repeat="device in devices" ng-class=" { selected:previewDevice==device }" ng-click="updatePreviewDevice(device)">
<div><i class="icon {{device.icon}}"></i><span>{{device.name}}</span></div>
</li>
</ul>
</div>
<div ng-show="settingIsOpen == 'setting'">
<tabset vertical="false">
<tab ng-repeat="category in tuningModel.categories" active="category.active">
<tab-heading>
<span>{{category.name}}</span>
</tab-heading>
<div ng-repeat="section in category.sections">
<accordion close-others="true">
<accordion-group ng-repeat="subSection in section.subSections"
ng-class="{true:'selected', false:''}[isOpen]"
is-open="(schemaFocus != '' && schemaFocus == subSection.name.toLowerCase())">
<accordion-heading>
<span ng-click="accordionOpened(subSection.name)" ng-mouseover="accordionWillBeOpened(subSection.schema)">
{{subSection.name}}
</span>
</accordion-heading>
<div ng-mouseover="accordionWillBeOpened(subSection.schema)">
<div class="div-field" ng-repeat="field in subSection.fields">
<span class="field-title">{{field.name}}:</span>
<div ng-if="field.type == 'colorPicker'">
<spectrum colorselected="field.value"></spectrum>
<i ng-if="field.value != ''" ng-click="removeField(field)" class="icon icon-delete colorPickerDelete"></i>
</div>
<div ng-if="field.type == 'bgImagePicker'" class="imagePickerPreviewContainer">
<div class="imagePickerPreview" ng-click="open(field)" style="background-image:{{ field.value }}">
<i class="icon icon-add"></i>
</div>
<i ng-if="field.value != ''" ng-click="removeField(field)" class="icon icon-delete colorPickerDelete"></i>
</div>
<div ng-if="field.type == 'bgPositionPicker'">
<select ng-options="BackgroundPosition for BackgroundPosition in BackgroundPositions" ng-model="field.value"></select>
</div>
<div ng-if="field.type == 'bgRepeatPicker'">
<select ng-options="BackgroundRepeat for BackgroundRepeat in BackgroundRepeats" ng-model="field.value"></select>
</div>
<div ng-if="field.type == 'bgAttachmentPicker'">
<select ng-options="BackgroundAttachment for BackgroundAttachment in BackgroundAttachments" ng-model="field.value"></select>
</div>
<div ng-if="field.type == 'layoutPicker'">
<select ng-options="Layout for Layout in Layouts" ng-model="field.value"></select>
</div>
<div class="box-slider" ng-if="field.type == 'slider'">
<div ui-slider min="{{field.min}}" max="{{field.max}}" step="1" ng-model="field.value"></div>
</div>
<div ng-if="field.type == 'checkbox'">
<input type="checkbox" ng-checked="field.value == 'true'" ng-model="field.value">
</div>
<div ng-if="field.type == 'displayPicker'">
<select ng-options="display for display in displays" ng-model="field.value"></select>
</div>
<div ng-if="field.type == 'optionHomePicker'">
<select ng-options="optionHome for optionHome in optionHomes" ng-model="field.value"></select>
</div>
<div ng-if="field.type == 'fontFamilyPicker'">
<div class="fontFamilyPickerPreview" ng-click="openFontFamilyPickerModal(field)" style="font-family:{{ field.value }}">
<span>Aa</span>
{{ field.value }}
</div>
<i ng-if="field.value != ''" ng-click="removeField(field)" class="fa fa-times colorPickerDelete"></i>
</div>
</div>
</div>
</accordion-group>
</accordion>
</div>
</tab>
</tabset>
<button class="btn btn-default btn-default-save" ng-click="saveLessParameters()">save</button>
<button class="btn btn-default btn-default-delete" ng-click="deleteTuning()">reset</button>
</div>
<div ng-show="settingIsOpen == 'presetColors'">
<h2>PALETTE COLORS</h2>
<ul class="palette">
<li ng-click="refreshtuningByPalette(palette)" ng-repeat="palette in tuningPalette">
<div>
<span style="background-color: {{ palette.mainColor }}"></span>
</div>
<p>{{ palette.name }}</p>
</li>
</ul>
<button class="btn btn-default btn-default-save" style="display:none" ng-click="saveLessParameters()">save</button>
<button class="btn btn-default btn-default-save" ng-click="makePreset()">make it !</button>
</div>
</div>
<script type="text/ng-template" id="myModalContent.html">
<div class="modal-header bodyTuningImagePicker ng-scope">
<ul class="breadcrumb">
<li ng-if="startNodeId == -1">
<a href="" ng-click="gotoFolder()">Media</a>
</li>
<!-- ngRepeat: item in path -->
<li ng-repeat="item in currentPath" class="ng-scope">
<a ng-if="currentFolder.id == item.id" href="" ng-class="{disabled:currentFolder.id == item.id}">{{ item.name }}</a>
<a ng-if="currentFolder.id != item.id" ng-click="gotoFolder(item)">{{ item.name }}</a>
</li>
</ul>
</div>
<div class="modal-body bodyTuningImagePicker">
<ul class="tuningImagePicker">
<li>
<ul class="media-items">
<li ng-repeat="child in currentFolder.children | orderBy:'isFolder':true">
<div ng-if="!child.isFolder" class=" media-preview" ng-class="{selected:selectedMedia.id == child.id}" ng-click="selectMedia(child)" style="background-image: url({{ child.thumbnail }})"></div>
<div ng-if="child.isFolder" class=" media-preview" ng-click="selectMedia(child)">
<i class="icon icon-folder folder"><p class="folder-name">{{child.name}}</p></i>
</div>
</li>
</ul>
</li>
</ul>
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="ok()">OK</button>
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
</div>
</script>
<script type="text/ng-template" id="fontFamilyPickerModel.html">
<div class="modal-header tuning-fontfamilypicker">
<select ng-model="selectedFont" ng-change="showFontPreview(selectedFont)" ng-options="font as font.fontFamily group by font.fontType for font in fonts"></select>
<select ng-model="selectedFont.variant" ng-change="showFontPreview(selectedFont)" ng-options="variant for variant in selectedFont.variants" />
</div>
<div class="modal-body tuning-fontfamilypicker">
<span class="show" ng-style="setStyleVariant()">Aa Bb Cc 1 2 3 4&hellip; <br />The quick brown fox jumps over the lazy dog&hellip;</span>
</div>
<div class="modal-footer tuning-fontfamilypicker">
<button class="btn btn-primary" ng-click="ok()">OK</button>
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
</div>
</script>
</div>
</div>
<div id="speechbubble">
<p>Styles saved and visible at the website</p>
</div>
<script src="/umbraco/lib/lazyload/lazyload.min.js"></script>
<script src="/umbraco/js/tuning.loader.js"></script>
</body>
</html>
@@ -0,0 +1,148 @@
/*********************************************************************************************************/
/* jQuery UI Slider plugin wrapper */
/*********************************************************************************************************/
angular.module('ui.slider', []).value('uiSliderConfig', {}).directive('uiSlider', ['uiSliderConfig', '$timeout', function (uiSliderConfig, $timeout) {
uiSliderConfig = uiSliderConfig || {};
return {
require: 'ngModel',
template: '<div><div class="slider" /><span class="slider-span">px</span><input class="slider-input" ng-model="value"></div>',
replace: true,
compile: function () {
return function (scope, elm, attrs, ngModel) {
scope.value = ngModel.$viewValue;
function parseNumber(n, decimals) {
return (decimals) ? parseFloat(n) : parseInt(n);
};
var options = angular.extend(scope.$eval(attrs.uiSlider) || {}, uiSliderConfig);
// Object holding range values
var prevRangeValues = {
min: null,
max: null
};
// convenience properties
var properties = ['min', 'max', 'step'];
var useDecimals = (!angular.isUndefined(attrs.useDecimals)) ? true : false;
var init = function () {
// When ngModel is assigned an array of values then range is expected to be true.
// Warn user and change range to true else an error occurs when trying to drag handle
if (angular.isArray(ngModel.$viewValue) && options.range !== true) {
console.warn('Change your range option of ui-slider. When assigning ngModel an array of values then the range option should be set to true.');
options.range = true;
}
// Ensure the convenience properties are passed as options if they're defined
// This avoids init ordering issues where the slider's initial state (eg handle
// position) is calculated using widget defaults
// Note the properties take precedence over any duplicates in options
angular.forEach(properties, function (property) {
if (angular.isDefined(attrs[property])) {
options[property] = parseNumber(attrs[property], useDecimals);
}
});
elm.find(".slider").slider(options);
init = angular.noop;
};
// Find out if decimals are to be used for slider
angular.forEach(properties, function (property) {
// support {{}} and watch for updates
attrs.$observe(property, function (newVal) {
if (!!newVal) {
init();
elm.find(".slider").slider('option', property, parseNumber(newVal, useDecimals));
}
});
});
attrs.$observe('disabled', function (newVal) {
init();
elm.find(".slider").slider('option', 'disabled', !!newVal);
});
// Watch ui-slider (byVal) for changes and update
scope.$watch(attrs.uiSlider, function (newVal) {
init();
if (newVal != undefined) {
elm.find(".slider").slider('option', newVal);
}
}, true);
// Late-bind to prevent compiler clobbering
$timeout(init, 0, true);
// Update model value from slider
elm.find(".slider").bind('slidestop', function (event, ui) {
ngModel.$setViewValue(ui.values || ui.value);
scope.$apply();
});
elm.bind('slide', function (event, ui) {
event.stopPropagation();
elm.find(".slider-input").val(ui.value);
});
// Update slider from model value
ngModel.$render = function () {
init();
var method = options.range === true ? 'values' : 'value';
if (isNaN(ngModel.$viewValue) && !(ngModel.$viewValue instanceof Array))
ngModel.$viewValue = 0;
scope.value = ngModel.$viewValue;
// Do some sanity check of range values
if (options.range === true) {
// Check outer bounds for min and max values
if (angular.isDefined(options.min) && options.min > ngModel.$viewValue[0]) {
ngModel.$viewValue[0] = options.min;
}
if (angular.isDefined(options.max) && options.max < ngModel.$viewValue[1]) {
ngModel.$viewValue[1] = options.max;
}
// Check min and max range values
if (ngModel.$viewValue[0] >= ngModel.$viewValue[1]) {
// Min value should be less to equal to max value
if (prevRangeValues.min >= ngModel.$viewValue[1])
ngModel.$viewValue[0] = prevRangeValues.min;
// Max value should be less to equal to min value
if (prevRangeValues.max <= ngModel.$viewValue[0])
ngModel.$viewValue[1] = prevRangeValues.max;
}
// Store values for later user
prevRangeValues.min = ngModel.$viewValue[0];
prevRangeValues.max = ngModel.$viewValue[1];
}
elm.find(".slider").slider(method, ngModel.$viewValue);
};
scope.$watch("value", function () {
ngModel.$setViewValue(scope.value);
}, true);
scope.$watch(attrs.ngModel, function () {
if (options.range === true) {
ngModel.$render();
}
}, true);
function destroy() {
elm.find(".slider").slider('destroy');
}
elm.find(".slider").bind('$destroy', destroy);
};
}
};
}]);
@@ -0,0 +1,50 @@
/*********************************************************************************************************/
/* spectrum color picker directive */
/*********************************************************************************************************/
angular.module('spectrumcolorpicker', [])
.directive('spectrum', function () {
return {
restrict: 'E',
transclude: true,
scope: {
colorselected: '='
},
link: function (scope, $element) {
var initColor;
$element.find("input").spectrum({
color: scope.colorselected,
preferredFormat: "rgb",
showAlpha: true,
showInput: true,
change: function (color) {
scope.colorselected = color.toRgbString();
scope.$apply();
},
move: function (color) {
scope.colorselected = color.toRgbString();
scope.$apply();
},
beforeShow: function (color) {
initColor = angular.copy(scope.colorselected);
$(this).spectrum("container").find(".sp-cancel").click(function (e) {
scope.colorselected = initColor;
scope.$apply();
});
},
});
scope.$watch('colorselected', function () {
$element.find("input").spectrum("set", scope.colorselected);
}, true);
},
template:
'<div><input type=\'text\' ng-model=\'colorselected\' /></div>',
replace: true
};
})
@@ -0,0 +1,758 @@
/*********************************************************************************************************/
/* tuning panel app and controller */
/*********************************************************************************************************/
// tuning main app
angular.module("umbraco.tuning", ['ui.bootstrap', 'spectrumcolorpicker', 'ui.slider', 'umbraco.resources', 'umbraco.services'])
// panel main controller
.controller("Umbraco.tuningController", function ($scope, $modal, $http, $window, $timeout, $location) {
$scope.isOpen = false;
$scope.frameLoaded = false;
$scope.enableTuning = 0;
$scope.tuningParameterUrl = "";
$scope.tuningGridList = "";
$scope.schemaFocus = "body";
$scope.settingIsOpen = 'previewDevice';
$scope.BackgroundPositions = ['center', 'left', 'right', 'bottom center', 'bottom left', 'bottom right', 'top center', 'top left', 'top right'];
$scope.BackgroundRepeats = ['no-repeat', 'repeat', 'repeat-x', 'repeat-y'];
$scope.BackgroundAttachments = ['scroll', 'fixed'];
$scope.Layouts = ['boxed', 'wide', 'full'];
$scope.displays = ['float-left', 'float-right', 'block-left', 'block-right', 'none'];
$scope.optionHomes = ['icon', 'text', 'none'];
$scope.googleFontFamilies = {};
$scope.pageId = "../dialogs/Preview.aspx?id=" + $location.search().id;
$scope.devices = [
{ name: "desktop", css: "desktop", icon: "icon-display" },
{ name: "laptop - 1366px", css: "laptop border", icon: "icon-laptop" },
{ name: "iPad portrait - 768px", css: "iPad-portrait border", icon: "icon-ipad" },
{ name: "iPad landscape - 1024px", css: "iPad-landscape border", icon: "icon-ipad flip" },
{ name: "smartphone portrait - 480px", css: "smartphone-portrait border", icon: "icon-iphone" },
{ name: "smartphone landscape - 320px", css: "smartphone-landscape border", icon: "icon-iphone flip" }
];
$scope.previewDevice = $scope.devices[0];
/*****************************************************************************/
/* Preview devices */
/*****************************************************************************/
// Set preview device
$scope.updatePreviewDevice = function (device) {
$scope.previewDevice = device;
}
/*****************************************************************************/
/* UI designer managment */
/*****************************************************************************/
// Update all tuningConfig's values from data
var updateConfigValue = function (data) {
var fonts = [];
$.each(tuningConfig.categories, function (indexCategory, category) {
$.each(category.sections, function (indexSection, section) {
$.each(section.subSections, function (indexSubSection, subSection) {
$.each(subSection.fields, function (indexField, field) {
try {
// value
var newValue = eval("data." + field.alias.replace("@", ""));
if (newValue != undefined) {
field.value = newValue;
if (field.value == "''") { field.value = ""; }
}
// special init for font family picker
if (field.type == "fontFamilyPicker") {
var fontWeight = eval("data." + field.alias.replace("@", "") + "_weight");
var fontStyle = eval("data." + field.alias.replace("@", "") + "_style");
var fontType = eval("data." + field.alias.replace("@", "") + "_type");
if (fontWeight != undefined) {
field.fontWeight = fontWeight;
if (field.fontWeight == "''") { field.fontWeight = ""; }
}
if (fontStyle != undefined) {
field.fontStyle = fontStyle;
if (field.fontStyle == "''") { field.fontStyle = ""; }
}
if (fontType != undefined) {
field.fontType = fontType;
if (field.fontType == "''") { field.fontType = ""; }
}
if (fontType == 'google' && field.value + field.fontWeight && $.inArray(field.value + ":" + field.fontWeight, fonts) < 0) {
fonts.splice(0, 0, field.value + ":" + field.fontWeight);
}
}
}
catch (err) {
console.info("Style parameter not found " + field.alias);
}
})
})
})
});
// Load google font
$.each(fonts, function (indexFont, font) {
loadGoogleFont(font);
loadGoogleFontInFront(font);
});
}
// Load parameters from GetLessParameters and init data of the tuning config
var initTuning = function () {
$http.get('/Umbraco/Api/tuning/Load', { params: { tuningStyleUrl: $scope.tuningParameterUrl, pageId: $location.search().id } })
.success(function (data) {
updateConfigValue(data);
$scope.tuningModel = tuningConfig;
$scope.tuningPalette = tuningPalette;
if ($scope.settingIsOpen == "setting") {
openIntelTuning();
}
});
}
// Add Less parameters for each grid row
var initGridConfig = function () {
if ($scope.tuningGridList) {
$.each($scope.tuningGridList, function (index, row) {
var stylingSubSection = tuningConfig.categories[1].sections[0].subSections
var newIndex = stylingSubSection.length + 1;
var rowFieldModel = angular.copy(rowModel);
var rowNumber = index + 1;
rowFieldModel.name = "Grid Row " + rowNumber;
stylingSubSection.splice(newIndex + 1, 0, rowFieldModel);
stylingSubSection[newIndex - 1].schema = "." + row;
$.each(stylingSubSection[newIndex - 1].fields, function (indexField, field) {
field.alias = field.alias + "__" + row;
});
})
}
}
// Refresh all less parameters for every changes watching tuningModel
var refreshtuning = function () {
var parameters = [];
if ($scope.tuningModel) {
$.each($scope.tuningModel.categories, function (indexCategory, category) {
$.each(category.sections, function (indexSection, section) {
$.each(section.subSections, function (indexSubSection, subSection) {
$.each(subSection.fields, function (indexField, field) {
// value
parameters.splice(parameters.length + 1, 0, "'@" + field.alias + "':'" + field.value + "'");
// special init for font family picker
if (field.type == "fontFamilyPicker") {
parameters.splice(parameters.length + 1, 0, "'@" + field.alias + "_weight':'" + field.fontWeight + "'");
parameters.splice(parameters.length + 1, 0, "'@" + field.alias + "_Style':'" + field.fontStyle + "'");
}
})
})
})
});
// Refrech page style
refreshFrontStyles(parameters);
}
}
// Save all parameter in tuningParameters.less file
$scope.saveLessParameters = function () {
var parameters = [];
var parametersGrid = [];
$.each($scope.tuningModel.categories, function (indexCategory, category) {
$.each(category.sections, function (indexSection, section) {
$.each(section.subSections, function (indexSubSection, subSection) {
$.each(subSection.fields, function (indexField, field) {
if (subSection.schema && subSection.schema.indexOf("gridrow_") >= 0) {
var value = (field.value != 0 && (field.value == undefined || field.value == "")) ? "''" : field.value;
parametersGrid.splice(parametersGrid.length + 1, 0, "@" + field.alias + ":" + value + ";");
}
else {
// value
var value = (field.value != 0 && (field.value == undefined || field.value == "")) ? "''" : field.value;
parameters.splice(parameters.length + 1, 0, "@" + field.alias + ":" + value + ";");
// special init for font family picker
if (field.type == "fontFamilyPicker") {
if (field.fontType == "google" && value != "''") {
var variant = field.fontWeight != "" || field.fontStyle != "" ? ":" + field.fontWeight + field.fontStyle : "";
var gimport = "@import url('http://fonts.googleapis.com/css?family=" + value + variant + "');";
if ($.inArray(gimport, parameters) < 0) {
parameters.splice(0, 0, gimport);
}
}
var fontWeight = (field.fontWeight != 0 && (field.fontWeight == undefined || field.fontWeight == "")) ? "''" : field.fontWeight;
var fontStyle = (field.fontStyle != 0 && (field.fontStyle == undefined || field.fontStyle == "")) ? "''" : field.fontStyle;
var fontType = (field.fontType != 0 && (field.fontType == undefined || field.fontType == "")) ? "''" : field.fontType;
parameters.splice(parameters.length + 1, 0, "@" + field.alias + "_weight:" + fontWeight + ";");
parameters.splice(parameters.length + 1, 0, "@" + field.alias + "_style:" + fontStyle + ";");
parameters.splice(parameters.length + 1, 0, "@" + field.alias + "_type:" + fontType + ";");
}
}
})
})
})
});
var resultParameters = { parameters: parameters.join(""), parametersGrid: parametersGrid.join(""), pageId: $location.search().id };
var transform = function (result) {
return $.param(result);
}
$('.btn-default-save').attr("disabled", true);
$http.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded";
$http.post('/Umbraco/Api/tuning/Save', resultParameters, {
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
transformRequest: transform
})
.success(function (data) {
$('.btn-default-save').attr("disabled", false);
$('#speechbubble').fadeIn('slow').delay(5000).fadeOut('slow');
});
}
// Delete current page tuning
$scope.deleteTuning = function () {
$('.btn-default-delete').attr("disabled", true);
$http.get('/Umbraco/Api/tuning/Delete', { params: { pageId: $location.search().id } })
.success(function (data) {
$scope.enableTuning++;
$scope.pageId = $scope.pageId + "&n=123456";
$('.btn-default-delete').attr("disabled", false);
})
}
/*****************************************************************************/
/* Preset design */
/*****************************************************************************/
// Refresh with selected tuning palette
$scope.refreshtuningByPalette = function (palette) {
updateConfigValue(palette.colors);
refreshtuning();
}
// Hidden botton to make preset from the current settings
$scope.makePreset = function () {
var parameters = [];
$.each($scope.tuningModel.categories, function (indexCategory, category) {
$.each(category.sections, function (indexSection, section) {
$.each(section.subSections, function (indexSubSection, subSection) {
$.each(subSection.fields, function (indexField, field) {
if (!subSection.schema || subSection.schema.indexOf("gridrow_") < 0) {
// value
var value = (field.value != 0 && (field.value == undefined || field.value == "")) ? "''" : field.value;
parameters.splice(parameters.length + 1, 0, "\"" + field.alias + "\":" + " \"" + value + "\"");
// special init for font family picker
if (field.type == "fontFamilyPicker") {
var fontWeight = (field.fontWeight != 0 && (field.fontWeight == undefined || field.fontWeight == "")) ? "''" : field.fontWeight;
var fontStyle = (field.fontStyle != 0 && (field.fontStyle == undefined || field.fontStyle == "")) ? "''" : field.fontStyle;
var fontType = (field.fontType != 0 && (field.fontType == undefined || field.fontType == "")) ? "''" : field.fontType;
parameters.splice(parameters.length + 1, 0, "\"" + field.alias + "_weight" + "\":" + " \"" + fontWeight + "\"");
parameters.splice(parameters.length + 1, 0, "\"" + field.alias + "_style" + "\":" + " \"" + fontStyle + "\"");
parameters.splice(parameters.length + 1, 0, "\"" + field.alias + "_type" + "\":" + " \"" + fontType + "\"");
}
}
})
})
})
});
$("body").append("<textarea>{name:\"\", mainColor:\"\", colors:{" + parameters.join(",") + "}}</textarea>");
}
/*****************************************************************************/
/* Panel managment */
/*****************************************************************************/
// Toggle panel
$scope.togglePanel = function () {
if ($scope.isOpen) {
$scope.isOpen = false;
closeIntelTuning();
}
else {
$scope.isOpen = true;
$scope.settingOpen($scope.settingIsOpen);
}
}
// Toggle setting
$scope.settingOpen = function (a) {
if ($scope.settingIsOpen == "setting" && a != "setting") {
closeIntelTuning();
}
if (a == "setting") {
openIntelTuning();
}
$scope.settingIsOpen = a;
}
// Remove value from field
$scope.removeField = function (field) {
field.value = "";
}
// Open image picker modal
$scope.open = function (field) {
$scope.data = {
newFolder: "",
modalField: field
};
var modalInstance = $modal.open({
scope: $scope,
templateUrl: 'myModalContent.html',
controller: 'tuning.mediapickercontroller',
resolve: {
items: function () {
return field.value;
}
}
});
modalInstance.result.then(function (selectedItem) {
field.value = selectedItem;
});
};
// Open font family picker modal
$scope.openFontFamilyPickerModal = function (field) {
$scope.data = {
modalField: field
};
var modalInstance = $modal.open({
scope: $scope,
templateUrl: 'fontFamilyPickerModel.html',
controller: 'tuning.fontfamilypickercontroller',
resolve: {
googleFontFamilies: function () {
return $scope.googleFontFamilies;
},
item: function () {
return field;
}
}
});
modalInstance.result.then(function (selectedItem) {
field.value = selectedItem.fontFamily;
field.fontType = selectedItem.fontType;
field.fontWeight = selectedItem.fontWeight;
field.fontStyle = selectedItem.fontStyle;
});
};
// Accordion open event
$scope.accordionOpened = function (schema) {
$scope.schemaFocus = schema;
}
// Focus schema in front
$scope.accordionWillBeOpened = function (schema) {
if (schema) {
setSelectedSchema(schema);
}
}
/*****************************************************************************/
/* Call function into the front-end */
/*****************************************************************************/
var openIntelTuning = function () {
if (document.getElementById("resultFrame").contentWindow.initIntelTuning)
document.getElementById("resultFrame").contentWindow.initIntelTuning($scope.tuningModel);
}
var closeIntelTuning = function () {
if (document.getElementById("resultFrame").contentWindow.closeIntelTuning)
document.getElementById("resultFrame").contentWindow.closeIntelTuning($scope.tuningModel);
}
var loadGoogleFontInFront = function (font) {
if (document.getElementById("resultFrame").contentWindow.getFont)
document.getElementById("resultFrame").contentWindow.getFont(font);
}
var setSelectedSchema = function (schema) {
if (document.getElementById("resultFrame").contentWindow.setSelectedSchema)
document.getElementById("resultFrame").contentWindow.setSelectedSchema(schema);
}
var refreshFrontStyles = function (parameters) {
if (document.getElementById("resultFrame").contentWindow.refrechLayout)
document.getElementById("resultFrame").contentWindow.refrechLayout(parameters);
}
/*****************************************************************************/
/* Google font loader, TODO: put together from directive, front and back */
/*****************************************************************************/
var webFontScriptLoaded = false;
var loadGoogleFont = function (font) {
if (!webFontScriptLoaded) {
$.getScript('http://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js')
.done(function () {
webFontScriptLoaded = true;
// Recursively call once webfont script is available.
loadGoogleFont(font);
})
.fail(function () {
console.log('error loading webfont');
});
}
else {
WebFont.load({
google: {
families: [font]
},
loading: function () {
console.log('loading font' + font + ' in UI designer');
},
active: function () {
console.log('loaded font ' + font + ' in UI designer');
},
inactive: function () {
console.log('error loading font ' + font + ' in UI designer');
}
});
}
}
/*****************************************************************************/
/* Init */
/*****************************************************************************/
// Preload of the google font
$http.get('/Umbraco/Api/tuning/GetGoogleFont').success(function (data) {
$scope.googleFontFamilies = data;
})
// watch framLoaded, only if iframe page have EnableTuning()
$scope.$watch("enableTuning", function () {
if ($scope.enableTuning > 0) {
if ($scope.enableTuning == 1) {
initGridConfig();
}
initTuning();
$scope.$watch('tuningModel', function () {
refreshtuning();
}, true);
}
}, true)
// First default load
$timeout(function () {
// toggle panel
$scope.frameLoaded = true;
//$timeout(function () { 1000, $scope.togglePanel(); });
}, 1000);
})
// Image picker controller
.controller('tuning.mediapickercontroller', function ($scope, $modalInstance, items, $http, mediaResource, umbRequestHelper, entityResource, mediaHelper) {
if (mediaHelper && mediaHelper.registerFileResolver) {
mediaHelper.registerFileResolver("Umbraco.UploadField", function (property, entity, thumbnail) {
if (thumbnail) {
if (mediaHelper.detectIfImageByExtension(property.value)) {
var thumbnailUrl = umbRequestHelper.getApiUrl(
"imagesApiBaseUrl",
"GetBigThumbnail",
[{ originalImagePath: property.value }]);
return thumbnailUrl;
}
else {
return null;
}
}
else {
return property.value;
}
});
}
var modalFieldvalue = $scope.data.modalField.value;
$scope.currentFolder = {};
$scope.currentFolder.children = [];
$scope.currentPath = [];
$scope.startNodeId = -1;
$scope.options = {
url: umbRequestHelper.getApiUrl("mediaApiBaseUrl", "PostAddFile"),
formData: {
currentFolder: $scope.startNodeId
}
};
//preload selected item
$scope.selectedMedia = undefined;
$scope.submitFolder = function (e) {
if (e.keyCode === 13) {
e.preventDefault();
$scope.$parent.data.showFolderInput = false;
if ($scope.$parent.data.newFolder && $scope.$parent.data.newFolder != "") {
mediaResource
.addFolder($scope.$parent.data.newFolder, $scope.currentFolder.id)
.then(function (data) {
$scope.$parent.data.newFolder = undefined;
$scope.gotoFolder(data);
});
}
}
};
$scope.gotoFolder = function (folder) {
if (!folder) {
folder = { id: $scope.startNodeId, name: "Media", icon: "icon-folder" };
}
if (folder.id > 0) {
var matches = _.filter($scope.currentPath, function (value, index) {
if (value.id == folder.id) {
value.indexInPath = index;
return value;
}
});
if (matches && matches.length > 0) {
$scope.currentPath = $scope.currentPath.slice(0, matches[0].indexInPath + 1);
}
else {
$scope.currentPath.push(folder);
}
}
else {
$scope.currentPath = [];
}
//mediaResource.rootMedia()
mediaResource.getChildren(folder.id)
.then(function (data) {
folder.children = data.items ? data.items : [];
angular.forEach(folder.children, function (child) {
child.isFolder = child.contentTypeAlias == "Folder" ? true : false;
if (!child.isFolder) {
child.thumbnail = mediaHelper.resolveFile(child, true);
child.image = mediaHelper.resolveFile(child, false);
}
});
$scope.options.formData.currentFolder = folder.id;
$scope.currentFolder = folder;
});
};
$scope.iconFolder = "glyphicons-icon folder-open"
$scope.selectMedia = function (media) {
if (!media.isFolder) {
//we have 3 options add to collection (if multi) show details, or submit it right back to the callback
$scope.selectedMedia = media;
$scope.data.modalField.value = "url(" + $scope.selectedMedia.image + ")";
}
else {
$scope.gotoFolder(media);
}
};
//default root item
if (!$scope.selectedMedia) {
$scope.gotoFolder();
}
$scope.ok = function () {
$modalInstance.close($scope.data.modalField.value);
};
$scope.cancel = function () {
$scope.data.modalField.value = modalFieldvalue;
$modalInstance.dismiss('cancel');
};
})
// Font picker controller
.controller('tuning.fontfamilypickercontroller', function ($scope, $modalInstance, item, googleFontFamilies, $http) {
$scope.safeFonts = ["Arial, Helvetica", "Impact", "Lucida Sans Unicode", "Tahoma", "Trebuchet MS", "Verdana", "Georgia", "Times New Roman", "Courier New, Courier"];
$scope.fonts = [];
$scope.selectedFont = {};
var originalFont = {};
originalFont.fontFamily = $scope.data.modalField.value;
originalFont.fontType = $scope.data.modalField.fontType;
originalFont.fontWeight = $scope.data.modalField.fontWeight;
originalFont.fontStyle = $scope.data.modalField.fontStyle;
var googleGetWeight = function (googleVariant) {
return (googleVariant != undefined && googleVariant != "") ? googleVariant.replace("italic", "") : "";
};
var googleGetStyle = function (googleVariant) {
var variantStyle = "";
if (googleVariant != undefined && googleVariant != "" && googleVariant.indexOf("italic") >= 0) {
variantWeight = googleVariant.replace("italic", "");
variantStyle = "italic";
}
return variantStyle;
};
angular.forEach($scope.safeFonts, function (value, key) {
$scope.fonts.push({
groupName: "Safe fonts",
fontType: "safe",
fontFamily: value,
fontWeight: "normal",
fontStyle: "normal",
});
});
angular.forEach(googleFontFamilies.items, function (value, key) {
var variants = value.variants;
var variant = value.variants.length > 0 ? value.variants[0] : "";
var fontWeight = googleGetWeight(variant);
var fontStyle = googleGetStyle(variant);
$scope.fonts.push({
groupName: "Google fonts",
fontType: "google",
fontFamily: value.family,
variants: value.variants,
variant: variant,
fontWeight: fontWeight,
fontStyle: fontStyle
});
});
$scope.setStyleVariant = function () {
if ($scope.selectedFont != undefined) {
return {
'font-family': $scope.selectedFont.fontFamily,
'font-weight': $scope.selectedFont.fontWeight,
'font-style': $scope.selectedFont.fontStyle
}
}
};
$scope.showFontPreview = function (font) {
if (font != undefined && font.fontFamily != "" && font.fontType == "google") {
// Font needs to be independently loaded in the iframe for live preview to work.
document.getElementById("resultFrame").contentWindow.getFont(font.fontFamily + ":" + font.variant);
WebFont.load({
google: {
families: [font.fontFamily + ":" + font.variant]
},
loading: function () {
console.log('loading');
},
active: function () {
// If $apply isn't called, the new font family isn't applied until the next user click.
$scope.$apply(function () {
$scope.selectedFont = font;
$scope.selectedFont.fontWeight = googleGetWeight($scope.selectedFont.variant);
$scope.selectedFont.fontStyle = googleGetStyle($scope.selectedFont.variant);
// Apply to the page content as a preview.
$scope.data.modalField.value = $scope.selectedFont.fontFamily;
$scope.data.modalField.fontType = $scope.selectedFont.fontType;
$scope.data.modalField.fontWeight = $scope.selectedFont.fontWeight;
$scope.data.modalField.fontStyle = $scope.selectedFont.fontStyle;
});
}
});
}
else {
// Font is available, apply it immediately in modal preview.
$scope.selectedFont = font;
// And to page content.
$scope.data.modalField.value = $scope.selectedFont.fontFamily;
$scope.data.modalField.fontType = $scope.selectedFont.fontType;
$scope.data.modalField.fontWeight = $scope.selectedFont.fontWeight;
$scope.data.modalField.fontStyle = $scope.selectedFont.fontStyle;
}
}
$scope.ok = function () {
$modalInstance.close({
fontFamily: $scope.selectedFont.fontFamily,
fontType: $scope.selectedFont.fontType,
fontWeight: $scope.selectedFont.fontWeight,
fontStyle: $scope.selectedFont.fontStyle,
});
};
$scope.cancel = function () {
// Discard font change.
$modalInstance.close({
fontFamily: originalFont.fontFamily,
fontType: originalFont.fontType,
fontWeight: originalFont.fontWeight,
fontStyle: originalFont.fontStyle,
});
};
if (item != undefined) {
angular.forEach($scope.fonts, function (value, key) {
if (value.fontFamily == item.value) {
$scope.selectedFont = value;
$scope.selectedFont.variant = item.fontWeight + item.fontStyle;
$scope.selectedFont.fontWeight = item.fontWeight;
$scope.selectedFont.fontStyle = item.fontStyle;
}
});
}
});
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,230 @@
/*********************************************************************************************************/
/* Global function and variable for panel/page com */
/*********************************************************************************************************/
var refrechLayout = function (parameters) {
// hide preview badget
$("#umbracoPreviewBadge").hide();
var string = "less.modifyVars({" + parameters.join(",") + "})";
eval(string);
}
/* Fonts loaded in the tuning panel need to be loaded independently in
* the content iframe to allow live previewing.
*/
var webFontScriptLoaded = false;
var getFont = function (font) {
if (!webFontScriptLoaded) {
$.getScript('http://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js')
.done(function () {
webFontScriptLoaded = true;
// Recursively call once webfont script is available.
getFont(font);
})
.fail(function () {
console.log('error loading webfont');
});
}
else {
WebFont.load({
google: {
families: [font]
},
loading: function () {
console.log('loading font' + font + ' in iframe');
},
active: function () {
console.log('loaded font ' + font + ' in iframe');
},
inactive: function () {
console.log('error loading font ' + font + ' in iframe');
}
});
}
}
var setSelectedSchema = function (schema) {
outlinePosition($(schema));
}
var closeIntelTuning = function (tuningModel) {
if (tuningModel) {
$("[tuning-over]").css('outline', 'none');
$.each(tuningModel.categories, function (key, category) {
$.each(category.sections, function (key, section) {
$.each(section.subSections, function (key, subSection) {
if (subSection.schema) {
$(subSection.schema).unbind();
$(subSection.schema).removeAttr("tuning-over");
}
});
});
});
}
}
var initIntelTuning = function (tuningModel) {
if (tuningModel) {
// Add tuning-over attr for each schema from config
$.each(tuningModel.categories, function (key, category) {
$.each(category.sections, function (key, section) {
$.each(section.subSections, function (key, subSection) {
if (subSection.schema) {
$(subSection.schema).attr("tuning-over", subSection.name);
}
});
});
});
// Outline tuning-over
$(document).mousemove(function (e) {
e.stopPropagation();
var target = $(e.target);
while (target.length > 0 && (target.attr('tuning-over') == undefined || target.attr('tuning-over') == '')) {
target = target.parent();
}
if (target.attr('tuning-over') != undefined && target.attr('tuning-over') != '') {
target.unbind();
outlinePosition(target);
target.click(function (e) {
e.stopPropagation();
e.preventDefault();
console.info(target.attr('tuning-over'));
parent.refrechIntelTuning(target.attr('tuning-over'));
return false;
});
}
else {
outlineHide();
}
});
}
}
var outlinePosition = function (target) {
if (target.length > 0 && target.attr('tuning-over') != undefined && target.attr('tuning-over') != '') {
var localname = target[0].localName;
var height = $(target).outerHeight();
var width = $(target).outerWidth();
var position = $(target).offset();
var posY = position.top - $(window).scrollTop();
var posX = position.left - $(window).scrollLeft();
console.info("element select " + localname);
$("#outline-data").html(target.attr('tuning-over'));
$("#outline-data").css('position', 'fixed');
$("#outline-data").css('top', posY);
$("#outline-data").css('left', posX);
$("#outline-data").css('display', 'block');
$("#outline-data").css('position', 'fixed');
$("#outline-data").css('background-color', 'rgb(164, 198, 253)');
$("#outline-data").css('color', '#000000');
$("#outline-data").css('padding', '0px 5px 0px 5px');
$("#outline-data").css('font-size', '11px');
$("#outline-data").css('transition', 'all .05s ease-in-out');
$("#outline-data").css('-moz-transition', 'all .05s ease-in-out');
$("#outline-data").css('-webkit-transition', 'all .05s ease-in-out');
$("#outline-sup").css('display', "block");
$("#outline-sup").css('height', "2px");
$("#outline-sup").css('width', width + "px");
$("#outline-sup").css('position', 'fixed');
$("#outline-sup").css('top', posY);
$("#outline-sup").css('left', posX);
$("#outline-sup").css('background-color', '#a4c6fd');
$("#outline-sup").css('transition', 'all .05s ease-in-out');
$("#outline-sup").css('-moz-transition', 'all .05s ease-in-out');
$("#outline-sup").css('-webkit-transition', 'all .05s ease-in-out');
$("#outline-inf").css('display', "block");
$("#outline-inf").css('height', "2px");
$("#outline-inf").css('width', Number(width + 2) + "px");
$("#outline-inf").css('position', 'fixed');
$("#outline-inf").css('top', posY + height);
$("#outline-inf").css('left', posX);
$("#outline-inf").css('background-color', '#a4c6fd');
$("#outline-inf").css('transition', 'all .05s ease-in-out');
$("#outline-inf").css('-moz-transition', 'all .05s ease-in-out');
$("#outline-inf").css('-webkit-transition', 'all .05s ease-in-out');
$("#outline-left").css('display', "block");
$("#outline-left").css('height', height + "px");
$("#outline-left").css('width', "2px");
$("#outline-left").css('position', 'fixed');
$("#outline-left").css('top', posY);
$("#outline-left").css('left', posX);
$("#outline-left").css('background-color', '#a4c6fd');
$("#outline-left").css('transition', 'all .05s ease-in-out');
$("#outline-left").css('-moz-transition', 'all .05s ease-in-out');
$("#outline-left").css('-webkit-transition', 'all .05s ease-in-out');
$("#outline-right").css('display', "block");
$("#outline-right").css('height', height + "px");
$("#outline-right").css('width', "2px");
$("#outline-right").css('position', 'fixed');
$("#outline-right").css('top', posY);
$("#outline-right").css('left', posX + width);
$("#outline-right").css('background-color', '#a4c6fd');
$("#outline-right").css('transition', 'all .05s ease-in-out');
$("#outline-right").css('-moz-transition', 'all .05s ease-in-out');
$("#outline-right").css('-webkit-transition', 'all .05s ease-in-out');
}
else {
outlineHide();
console.info("element not found select");
}
}
var outlineHide = function () {
$("#outline-data").css('display', "none");
$("#outline-sup").css('display', "none");
$("#outline-inf").css('display', "none");
$("#outline-right").css('display', "none");
$("#outline-left").css('display', "none");
}
var initTuningPanel = function () {
// Looking for grid row
var tuningGridList = []
$("div[class^='gridrow_']").each(function (index, value) {
tuningGridList.splice(tuningGridList.length + 1, 0, $(value).attr("class"))
});
// Init panel
if (parent.setFrameIsLoaded) {
parent.setFrameIsLoaded(tuningParameterUrl, tuningGridList);
}
}
$(function () {
// Init ouline layer
$("body").append("<div id=\"outline-data\"></div><div id=\"outline-sup\"></div><div id=\"outline-inf\"></div><div id=\"outline-left\"></div><div id=\"outline-right\"></div>");
// Init tuning panel
initTuningPanel();
});
@@ -0,0 +1,52 @@
/*********************************************************************************************************/
/* Global function and variable for panel/page com */
/*********************************************************************************************************/
/* Called for every tuning-over rollover */
var refrechIntelTuning = function (name) {
var scope = angular.element($("#tuningPanel")).scope();
if (scope.schemaFocus != name.toLowerCase()) {
var notFound = true;
angular.forEach(scope.tuningModel.categories, function (category, key) {
var isContainer = false;
angular.forEach(category.sections, function (section, key) {
angular.forEach(section.subSections, function (subSection, key) {
if (subSection.name && name.toLowerCase() == subSection.name.toLowerCase()) {
isContainer = true;
notFound = false
}
});
});
if (!category.active) {
category.active = isContainer;
}
});
if (notFound) {
scope.tuningModel.categories[0].active = true;
}
scope.$apply();
if (notFound) {
scope.schemaFocus = "body";
}
else {
scope.schemaFocus = name.toLowerCase();
}
scope.$apply();
}
}
/* Called when the iframe is first loaded */
var setFrameIsLoaded = function (tuningParameterUrl, tuningGridList) {
var scope = angular.element($("#tuningPanel")).scope();
scope.tuningParameterUrl = tuningParameterUrl;
scope.tuningGridList = tuningGridList
scope.enableTuning++;
scope.$apply();
}
@@ -0,0 +1,53 @@

/************************************************************************/
/* Grid row -ID- */
/************************************************************************/
/***start-lessParam--ID-***/
@backgroundRowColor__-ID-:'';
@backgroundRowImageOrPattern__-ID-:'';
@backgroundRowGradientColor__-ID-:'';
@backgroundRowPosition__-ID-:'';
@backgroundRowCover__-ID-:'';
@backgroundRowRepeat__-ID-:'';
@backgroundRowAttachment__-ID-:'';
@rowFullSize__-ID-:false;
/***end-lessParam--ID-***/
/***start-gridStyle--ID-***/
.Row-cover__-ID- () when (@backgroundRowCover__-ID- = true) {
-webkit-background-size: cover;
-moz-background-size: cover;
-o-background-size: cover;
background-size: cover;
}
.Row-full-size__-ID- () when (@rowFullSize__-ID- = true) {
width:100%;
padding-right: 0px;
padding-left: 0px;
padding-top: 0px;
padding-bottom: 0px
}
.-ID- {
background-color:@backgroundRowColor__-ID-;
background: -moz-linear-gradient(top, @backgroundRowColor__-ID- 41%,@backgroundRowGradientColor__-ID- 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(41%,@backgroundRowColor__-ID-), color-stop(100%,@backgroundRowGradientColor__-ID-));
background: -webkit-linear-gradient(top, @backgroundRowColor__-ID- 41%,@backgroundRowGradientColor__-ID- 100%);
background: -o-linear-gradient(top, @backgroundRowColor__-ID- 41%,@backgroundRowGradientColor__-ID- 100%);
background: -ms-linear-gradient(top, @backgroundRowColor__-ID- 41%,@backgroundRowGradientColor__-ID- 100%);
background: linear-gradient(to bottom, @backgroundRowColor__-ID- 41%,@backgroundRowGradientColor__-ID- 100%);
background-image: @backgroundRowImageOrPattern__-ID-;
background-position: @backgroundRowPosition__-ID-;
background-repeat: @backgroundRowRepeat__-ID-;
background-attachment: @backgroundRowAttachment__-ID-;
.Row-cover__-ID-;}
.-ID- .container {
.Row-full-size__-ID-;
}
/***end-gridStyle--ID-***/
@@ -0,0 +1,25 @@
angular.module("umbraco")
.controller("Umbraco.Dialogs.ApprovedColorPickerController", function ($scope, $http, umbPropEditorHelper, assetsService) {
assetsService.loadJs("lib/cssparser/cssparser.js")
.then(function () {
var cssPath = $scope.dialogData.cssPath;
$scope.cssClass = $scope.dialogData.cssClass;
$scope.classes = [];
$scope.change = function (newClass) {
$scope.model.value = newClass;
}
$http.get(cssPath)
.success(function (data) {
var parser = new CSSParser();
$scope.classes = parser.parse(data, false, false).cssRules;
$scope.classes.splice(0, 0, "noclass");
})
assetsService.loadCss("/App_Plugins/Lecoati.uSky.Grid/lib/uSky.Grid.ApprovedColorPicker.css");
assetsService.loadCss(cssPath);
});
});
@@ -0,0 +1,7 @@
<div ng-controller="Umbraco.Dialogs.ApprovedColorController">
<div class='CssClassPickerBox {{(class.mSelectorText.substring(1) == cssClass)? "select" : "" }}'
ng-click="submit(class.mSelectorText.substring(1))" ng-repeat='class in classes'>
<div class='{{class.mSelectorText.substring(1)}}'>
</div>
</div>
</div>
@@ -1,15 +1,116 @@
function ContentEditDialogController($scope) {
function ContentEditDialogController($scope, $routeParams, $q, $timeout, $window, appState, contentResource, entityResource, navigationService, notificationsService, angularHelper, serverValidationManager, contentEditingHelper, treeService, fileManager, formHelper, umbRequestHelper, umbModelMapper, $http) {
//setup scope vars
$scope.model = {};
$scope.model.defaultButton = null;
$scope.model.subButtons = [];
var dialogOptions = $scope.dialogOptions;
if(dialogOptions.entity){
$scope.model.entity = dialogOptions.entity;
$scope.loaded = true;
$scope.model.nodeId = 0;
var dialogOptions = $scope.$parent.dialogOptions;
if(angular.isObject(dialogOptions.entity)){
$scope.model.entity = $scope.filterTabs(dialogOptions.entity, dialogOptions.tabFilter);
$scope.loaded = true;
}else{
if (dialogOptions.create) {
//we are creating so get an empty content item
contentResource.getScaffold(dialogOptions.id, dialogOptions.contentType)
.then(function(data) {
$scope.loaded = true;
$scope.model.entity = $scope.filterTabs(data, dialogOptions.tabFilter);
});
}
else {
//we are editing so get the content item from the server
contentResource.getById(dialogOptions.id)
.then(function(data) {
$scope.loaded = true;
$scope.model.entity = $scope.filterTabs(data, dialogOptions.tabFilter);
//in one particular special case, after we've created a new item we redirect back to the edit
// route but there might be server validation errors in the collection which we need to display
// after the redirect, so we will bind all subscriptions which will show the server validation errors
// if there are any and then clear them so the collection no longer persists them.
serverValidationManager.executeAndClearAllSubscriptions();
});
}
}
function performSave(args) {
var deferred = $q.defer();
$scope.busy = true;
if (formHelper.submitForm({ scope: $scope, statusMessage: args.statusMessage })) {
args.saveMethod($scope.model.entity, $routeParams.create, fileManager.getFiles())
.then(function (data) {
formHelper.resetForm({ scope: $scope, notifications: data.notifications });
contentEditingHelper.handleSuccessfulSave({
scope: $scope,
savedContent: data,
rebindCallback: contentEditingHelper.reBindChangedProperties($scope.model.entity, data)
});
$scope.busy = false;
deferred.resolve(data);
}, function (err) {
contentEditingHelper.handleSaveError({
redirectOnFailure: true,
err: err,
rebindCallback: contentEditingHelper.reBindChangedProperties($scope.model.entity, err.data)
});
$scope.busy = false;
deferred.reject(err);
});
}
else {
$scope.busy = false;
deferred.reject();
}
return deferred.promise;
}
$scope.filterTabs = function(entity, blackList){
if(blackList){
_.each(entity.tabs, function(tab){
tab.hide = _.contains(blackList, tab.alias);
});
}
return entity;
};
$scope.saveAndPublish = function() {
$scope.submit($scope.model.entity);
};
$scope.saveAndPublish = function() {
performSave({ saveMethod: contentResource.publish, statusMessage: "Publishing..." })
.then(function(content){
if(dialogOptions.closeOnSave){
$scope.submit(content);
}
});
};
$scope.save = function () {
performSave({ saveMethod: contentResource.save, statusMessage: "Saving..." })
.then(function(content){
if(dialogOptions.closeOnSave){
$scope.submit(content);
}
});
};
}
angular.module("umbraco")
.controller("Umbraco.Dialogs.Content.EditController", ContentEditDialogController);
@@ -6,12 +6,15 @@
<div class="umb-panel">
<div class="umb-panel-header">
<h1>{{model.entity.name}}</h1>
<umb-content-name
placeholder="@placeholders_entername"
ng-model="model.entity.name"/>
</div>
<div class="umb-panel-body with-footer">
<div id="tab{{tab.id}}" ng-repeat="tab in model.entity.tabs">
<div class="umb-pane">
<div class="umb-pane" ng-if="!tab.hide">
<h5>{{tab.label}} - {{tab.alias}}</h5>
<umb-property
property="property"
ng-repeat="property in tab.properties">
@@ -26,31 +29,31 @@
<div class="umb-el-wrap umb-panel-buttons">
<div class="btn-toolbar umb-btn-toolbar pull-right">
<a href ng-click="close()" class="btn btn-link">
<localize key="general_cancel">Cancel</localize>
<localize key="general_close">Close</localize>
</a>
<div class="btn-group dropup">
<!-- primary button -->
<a class="btn btn-success" href="#" ng-click="saveAndPublish()" prevent-default>
Publish
</a>
<a class="btn btn-success dropdown-toggle" data-toggle="dropdown" ng-if="subButtons.length > 0">
<span class="caret"></span>
</a>
<!-- sub buttons -->
<ul class="dropdown-menu bottom-up" role="menu" aria-labelledby="dLabel">
<a href ng-click="save()" prevent-default>
Save
</a>
</li>
</ul>
</div>
</div>
<div class="btn-group dropup" ng-if="defaultButton" >
<!-- primary button -->
<a class="btn btn-success" href="#" ng-click="performAction(model.defaultButton)" prevent-default>
<localize key="{{model.defaultButton.labelKey}}">{{defaultButton.labelKey}}</localize>
</a>
<a class="btn btn-success dropdown-toggle" data-toggle="dropdown" ng-if="subButtons.length > 0">
<span class="caret"></span>
</a>
<!-- sub buttons -->
<ul class="dropdown-menu bottom-up" role="menu" aria-labelledby="dLabel"
ng-if="model.subButtons.length > 0">
<li ng-repeat="btn in model.subButtons">
<a href="#" ng-click="performAction(btn)" ng-disabled="true" prevent-default>
<localize key="{{btn.labelKey}}">{{btn.labelKey}}</localize>
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
@@ -0,0 +1,102 @@
angular.module("umbraco").controller('Umbraco.Dialogs.Template.QueryBuilderController',
function($scope, $http, dialogService){
$http.get("backoffice/UmbracoApi/TemplateQuery/GetAllowedProperties").then(function(response) {
$scope.properties = response.data;
});
$http.get("backoffice/UmbracoApi/TemplateQuery/GetContentTypes").then(function (response) {
$scope.contentTypes = response.data;
});
$http.get("backoffice/UmbracoApi/TemplateQuery/GetFilterConditions").then(function (response) {
$scope.conditions = response.data;
});
$scope.query = {
contentType: {
name: "Everything"
},
source:{
name: "My website"
},
filters:[
{
property:undefined,
operator: undefined
}
],
sort:{
property:{
alias: "",
name: "",
},
direction: "Ascending"
}
};
$scope.chooseSource = function(query){
dialogService.contentPicker({
callback: function (data) {
if (data.id > 0) {
query.source = { id: data.id, name: data.name };
} else {
query.source.name = "My website";
delete query.source.id;
}
}
});
};
var throttledFunc = _.throttle(function() {
$http.post("backoffice/UmbracoApi/TemplateQuery/PostTemplateQuery", $scope.query).then(function (response) {
$scope.result = response.data;
});
}, 200);
$scope.$watch("query", function(value) {
throttledFunc();
}, true);
$scope.getPropertyOperators = function (property) {
var conditions = _.filter($scope.conditions, function(condition) {
var index = condition.appliesTo.indexOf(property.type);
return index >= 0;
});
return conditions;
};
$scope.addFilter = function(query){
query.filters.push({});
};
$scope.trashFilter = function (query) {
query.filters.splice(query,1);
};
$scope.changeSortOrder = function(query){
if(query.sort.direction === "ascending"){
query.sort.direction = "descending";
}else{
query.sort.direction = "ascending";
}
};
$scope.setSortProperty = function(query, property){
query.sort.property = property;
if(property.type === "datetime"){
query.sort.direction = "descending";
}else{
query.sort.direction = "ascending";
}
};
});
@@ -0,0 +1,158 @@
<form novalidate name="contentForm"
ng-controller="Umbraco.Dialogs.Template.QueryBuilderController"
ng-submit="close()"
val-form-manager>
<style>
.umb-querybuilder .row {font-size: 12px; line-height: 12px}
.umb-querybuilder .row a.btn{font-size: 12px; background: lightyellow}
.umb-querybuilder .row > div{
padding: 20px;
border-bottom: 1px solid #efefef;
}
</style>
<div class="umb-panel">
<div class="umb-panel-header">
<h1 class="headline" style="margin: 10px 0 0 0">Build a query</h1>
</div>
<div class="umb-panel-body with-footer umb-querybuilder">
<div class="row">
<div>
I want
<div class="btn-group">
<a class="btn btn-link dropdown-toggle"
data-toggle="dropdown" href="#">
{{query.contentType.name}}
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li ng-repeat="contentType in contentTypes">
<a href ng-click="query.contentType = contentType">
{{contentType.name}}
</a>
</li>
</ul>
</div>
<span>from</span>
<a href class="btn btn-link" ng-click="chooseSource(query)">
{{query.source.name}}
<span class="caret"></span>
</a>
</div>
<div ng-repeat="filter in query.filters">
<span ng-if="$first">Where</span>
<span ng-if="!$first">And</span>
<div class="btn-group">
<a class="btn btn-link dropdown-toggle"
data-toggle="dropdown" href="#">
{{filter.property.name}}
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li ng-repeat="property in properties">
<a href ng-click="filter.property = property">
{{property.name}}
</a>
</li>
</ul>
</div>
<div class="btn-group" ng-if="filter.property">
<a class="btn btn-link dropdown-toggle"
data-toggle="dropdown" href="#">
{{filter.term.name}}
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li ng-repeat="term in getPropertyOperators(filter.property)">
<a href ng-click="filter.term = term">
{{term.name}}
</a>
</li>
</ul>
</div>
<input type="text" ng-if="filter.term" style="width:90px;" ng-model="filter.constraintValue" />
<a href ng-click="addFilter(query)">
<i class="icon-add"></i>
</a>
<a href ng-if="query.filters.length > 1" ng-click="trashFilter(query)">
<i class="icon-trash"></i>
</a>
</div>
<div>
Order by
<div class="btn-group">
<a class="btn btn-link dropdown-toggle"
data-toggle="dropdown" href="#">
{{query.sort.property.name}}
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li ng-repeat="property in properties">
<a href ng-click="setSortProperty(query, property)">
{{property.name}}
</a>
</li>
</ul>
</div>
<a href class="btn" ng-click="changeSortOrder(query)">
{{query.sort.direction}}
</a>
</div>
</div>
<h4>Returns {{result.resultCount}} items in {{result.executionTime}} miliseconds</h4>
<ul class="nav unstyled">
<li ng-repeat="item in result.sampleResults">
<i class="icon icon-document blue"></i> {{item.name}}
</li>
</ul>
<pre>{{result.queryExpression}}</pre>
</div>
<div class="umb-panel-footer" >
<div class="umb-el-wrap umb-panel-buttons">
<div class="btn-toolbar umb-btn-toolbar pull-right">
<a href ng-click="close()" class="btn btn-link">
<localize key="general_close">Close</localize>
</a>
<a href ng-click="submit(result.queryExpression)" class="btn btn-primary">Insert</a>
</div>
</div>
</div>
</div>
</div>
</form>
@@ -264,10 +264,11 @@ function ContentEditController($scope, $routeParams, $q, $timeout, $window, appS
$scope.preview = function (content) {
// Chromes popup blocker will kick in if a window is opened
// outwith the initial scoped request. This trick will fix that.
var previewWindow = $window.open("/umbraco/views/content/umbpreview.html", "umbpreview");
//
var previewWindow = $window.open('preview/?id=' + content.id, 'umbpreview');
$scope.save().then(function (data) {
// Build the correct path so both /#/ and #/ work.
var redirect = Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath + '/dialogs/preview.aspx?id=' + data.id;
var redirect = Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath + '/preview/?id=' + data.id;
previewWindow.location.href = redirect;
});
};
@@ -39,6 +39,7 @@
<div class="umb-tab-buttons" detect-fold ng-class="{'umb-dimmed': busy}">
<div class="btn-group" ng-show="content.template">
<a class="btn" ng-click="preview(content)">
<localize key="buttons_showPage">Preview page</localize>
@@ -4,35 +4,35 @@ angular.module('umbraco')
.controller("Umbraco.PropertyEditors.ContentPickerController",
function($scope, dialogService, entityResource, editorState, $log, iconHelper, $routeParams){
$scope.renderModel = [];
$scope.ids = $scope.model.value ? $scope.model.value.split(',') : [];
//configuration
$scope.cfg = {
multiPicker: "0",
entityType: "Document",
$scope.renderModel = [];
$scope.ids = $scope.model.value ? $scope.model.value.split(',') : [];
//configuration
$scope.cfg = {
multiPicker: "0",
entityType: "Document",
filterCssClass: "not-allowed not-published",
startNode: {
startNode: {
query: "",
type: "content",
id: -1
}
};
type: "content",
id: -1
}
};
if ($scope.model.config) {
$scope.cfg = angular.extend($scope.cfg, $scope.model.config);
}
if ($scope.model.config) {
$scope.cfg = angular.extend($scope.cfg, $scope.model.config);
}
//Umbraco persists boolean for prevalues as "0" or "1" so we need to convert that!
$scope.cfg.multiPicker = ($scope.cfg.multiPicker === "0" ? false : true);
//Umbraco persists boolean for prevalues as "0" or "1" so we need to convert that!
$scope.cfg.multiPicker = ($scope.cfg.multiPicker === "0" ? false : true);
if ($scope.cfg.startNode.type === "member") {
$scope.cfg.entityType = "Member";
}
else if ($scope.cfg.startNode.type === "media") {
$scope.cfg.entityType = "Media";
}
if ($scope.cfg.startNode.type === "member") {
$scope.cfg.entityType = "Member";
}
else if ($scope.cfg.startNode.type === "media") {
$scope.cfg.entityType = "Media";
}
//if we have a query for the startnode, we will use that.
if($scope.cfg.startNode.query){
@@ -44,90 +44,102 @@ angular.module('umbraco')
$scope.cfg.startNodeId = $scope.cfg.startNode.id;
}
$scope.cfg.callback = populate;
$scope.cfg.treeAlias = $scope.cfg.startNode.type;
$scope.cfg.callback = populate;
$scope.cfg.treeAlias = $scope.cfg.startNode.type;
$scope.cfg.section = $scope.cfg.startNode.type;
//load current data
entityResource.getByIds($scope.ids, $scope.cfg.entityType).then(function (data) {
_.each(data, function (item, i) {
item.icon = iconHelper.convertFromLegacyIcon(item.icon);
$scope.renderModel.push({ name: item.name, id: item.id, icon: item.icon });
});
});
//load current data
entityResource.getByIds($scope.ids, $scope.cfg.entityType).then(function (data) {
_.each(data, function (item, i) {
item.icon = iconHelper.convertFromLegacyIcon(item.icon);
$scope.renderModel.push({ name: item.name, id: item.id, icon: item.icon });
});
});
//dialog
$scope.openContentPicker = function () {
var d = dialogService.treePicker($scope.cfg);
};
//dialog
$scope.openContentPicker = function () {
var d = dialogService.treePicker($scope.cfg);
};
$scope.remove = function (index) {
$scope.renderModel.splice(index, 1);
};
$scope.remove = function (index) {
$scope.renderModel.splice(index, 1);
};
$scope.edit = function (node) {
dialogService.open(
{
template: "views/common/dialogs/content/edit.html",
id: node.id,
closeOnSave:true,
tabFilter: ["Generic properties"],
callback: function(data){
node.name = data.name;
}
});
};
$scope.add = function (item) {
if ($scope.ids.indexOf(item.id) < 0) {
item.icon = iconHelper.convertFromLegacyIcon(item.icon);
$scope.renderModel.push({ name: item.name, id: item.id, icon: item.icon });
}
};
$scope.add = function (item) {
if ($scope.ids.indexOf(item.id) < 0) {
item.icon = iconHelper.convertFromLegacyIcon(item.icon);
$scope.renderModel.push({ name: item.name, id: item.id, icon: item.icon });
}
};
$scope.clear = function () {
$scope.renderModel = [];
};
$scope.clear = function () {
$scope.renderModel = [];
};
//We need to watch our renderModel so that we can update the underlying $scope.model.value properly, this is required
// because the ui-sortable doesn't dispatch an event after the digest of the sort operation. Any of the events for UI sortable
// occur after the DOM has updated but BEFORE the digest has occured so the model has NOT changed yet - it even states so in the docs.
// In their source code there is no event so we need to just subscribe to our model changes here.
//This also makes it easier to manage models, we update one and the rest will just work.
$scope.$watch(function () {
//return the joined Ids as a string to watch
return _.map($scope.renderModel, function (i) {
return i.id;
}).join();
}, function (newVal) {
$scope.ids = _.map($scope.renderModel, function (i) {
return i.id;
});
$scope.model.value = trim($scope.ids.join(), ",");
//We need to watch our renderModel so that we can update the underlying $scope.model.value properly, this is required
// because the ui-sortable doesn't dispatch an event after the digest of the sort operation. Any of the events for UI sortable
// occur after the DOM has updated but BEFORE the digest has occured so the model has NOT changed yet - it even states so in the docs.
// In their source code there is no event so we need to just subscribe to our model changes here.
//This also makes it easier to manage models, we update one and the rest will just work.
$scope.$watch(function () {
//return the joined Ids as a string to watch
return _.map($scope.renderModel, function (i) {
return i.id;
}).join();
}, function (newVal) {
$scope.ids = _.map($scope.renderModel, function (i) {
return i.id;
});
$scope.model.value = trim($scope.ids.join(), ",");
//Validate!
if ($scope.model.config && $scope.model.config.minNumber && parseInt($scope.model.config.minNumber) > $scope.renderModel.length) {
$scope.contentPickerForm.minCount.$setValidity("minCount", false);
}
else {
$scope.contentPickerForm.minCount.$setValidity("minCount", true);
}
if ($scope.model.config && $scope.model.config.minNumber && parseInt($scope.model.config.minNumber) > $scope.renderModel.length) {
$scope.contentPickerForm.minCount.$setValidity("minCount", false);
}
else {
$scope.contentPickerForm.minCount.$setValidity("minCount", true);
}
if ($scope.model.config && $scope.model.config.maxNumber && parseInt($scope.model.config.maxNumber) < $scope.renderModel.length) {
$scope.contentPickerForm.maxCount.$setValidity("maxCount", false);
}
else {
$scope.contentPickerForm.maxCount.$setValidity("maxCount", true);
}
});
if ($scope.model.config && $scope.model.config.maxNumber && parseInt($scope.model.config.maxNumber) < $scope.renderModel.length) {
$scope.contentPickerForm.maxCount.$setValidity("maxCount", false);
}
else {
$scope.contentPickerForm.maxCount.$setValidity("maxCount", true);
}
});
$scope.$on("formSubmitting", function (ev, args) {
$scope.model.value = trim($scope.ids.join(), ",");
});
$scope.$on("formSubmitting", function (ev, args) {
$scope.model.value = trim($scope.ids.join(), ",");
});
function trim(str, chr) {
var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^' + chr + '+|' + chr + '+$', 'g');
return str.replace(rgxtrim, '');
}
function trim(str, chr) {
var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^' + chr + '+|' + chr + '+$', 'g');
return str.replace(rgxtrim, '');
}
function populate(data) {
if (angular.isArray(data)) {
_.each(data, function (item, i) {
$scope.add(item);
});
} else {
$scope.clear();
$scope.add(data);
}
}
});
function populate(data) {
if (angular.isArray(data)) {
_.each(data, function (item, i) {
$scope.add(item);
});
} else {
$scope.clear();
$scope.add(data);
}
}
});
@@ -15,6 +15,7 @@
<i class="{{node.icon}} hover-hide"></i>
{{node.name}}
</a>
<br/><small><a href ng-click="edit(node)">Edit</a></small>
</li>
</ul>
@@ -0,0 +1,324 @@
var uSkyGridConfig = [
{
columns: [
{
grid: 12,
percentage: 100,
cellModels: [
{
name: "Single column",
models: [{
grid: 12,
percentage: 100
}]
}, {
name: "Article",
models: [{
grid: 4,
percentage: 33.3,
allowed: ["media","quote"]
}, {
grid: 8,
percentage: 66.6,
allowed: ["rte"]
}]
},
{
name: "Article, reverse",
models: [
{
grid: 8,
percentage: 66.6,
allowed: ["rte","macro"]
},
{
grid: 4,
percentage: 33.3,
allowed: ["media","quote","embed"]
}]
},
{
name: "Profile page",
models: [
{
grid: 4,
percentage: 33.3,
allowed: ["media"]
},
{
grid: 8,
percentage: 66.6,
allowed: ["rte"]
}
]
},
{
name: "Headline",
models: [
{
grid: 12,
percentage: 100,
max: 1,
allowed: ["headline"]
}
]
},
{
name: "Three columns",
models: [{
grid: 4,
percentage: 33.3,
allowed: ["rte"]
},
{
grid: 4,
percentage: 33.3,
allowed: ["rte"]
},
{
grid: 4,
percentage: 33.3,
allowed: ["rte"]
}]
}
]
}
]
},
{
columns: [
{
grid: 9,
percentage: 70,
cellModels: [
{
models: [{
grid: 12,
percentage: 100
}]
}, {
models: [{
grid: 6,
percentage: 50
}, {
grid: 6,
percentage: 50
}]
}, {
models: [{
grid: 4,
percentage: 33.3
}, {
grid: 4,
percentage: 33.3
}, {
grid: 4,
percentage: 33.3
}]
}, {
models: [{
grid: 3,
percentage: 25
}, {
grid: 3,
percentage: 25
}, {
grid: 3,
percentage: 25
}, {
grid: 3,
percentage: 25
}, ]
}, {
models: [{
grid: 2,
percentage: 16.6
}, {
grid: 2,
percentage: 16.6
}, {
grid: 2,
percentage: 16.6
}, {
grid: 2,
percentage: 16.6
}, {
grid: 2,
percentage: 16.6
}, {
grid: 2,
percentage: 16.6
}]
}, {
models: [{
grid: 8,
percentage: 60
}, {
grid: 4,
percentage: 40
}]
}, {
models: [{
grid: 4,
percentage: 40
}, {
grid: 8,
percentage: 60
}]
}
]
},
{
grid: 3,
percentage: 30,
cellModels: [
{
models: [{
grid: 12,
percentage: 100
}]
}
]
}
]
},
{
columns: [
{
grid: 3,
percentage: 30,
cellModels: [
{
models: [{
grid: 12,
percentage: 100
}]
}
]
},
{
grid: 9,
percentage: 70,
cellModels: [
{
models: [{
grid: 12,
percentage: 100
}]
}, {
models: [{
grid: 6,
percentage: 50
}, {
grid: 6,
percentage: 50
}]
}, {
models: [{
grid: 4,
percentage: 33.3
}, {
grid: 4,
percentage: 33.3
}, {
grid: 4,
percentage: 33.3
}]
}, {
models: [{
grid: 3,
percentage: 25
}, {
grid: 3,
percentage: 25
}, {
grid: 3,
percentage: 25
}, {
grid: 3,
percentage: 25
}, ]
}, {
models: [{
grid: 2,
percentage: 16.6
}, {
grid: 2,
percentage: 16.6
}, {
grid: 2,
percentage: 16.6
}, {
grid: 2,
percentage: 16.6
}, {
grid: 2,
percentage: 16.6
}, {
grid: 2,
percentage: 16.6
}]
}, {
models: [{
grid: 8,
percentage: 60
}, {
grid: 4,
percentage: 40
}]
}, {
models: [{
grid: 4,
percentage: 40
}, {
grid: 8,
percentage: 60
}]
}
]
}
]
},
{
columns: [
{
grid: 4,
percentage: 33.3,
cellModels: [
{
models: [{
grid: 12,
percentage: 100
}]
}
]
},
{
grid: 4,
percentage: 33.3,
cellModels: [
{
models: [{
grid: 12,
percentage: 100
}]
}
]
},
{
grid: 4,
percentage: 33.3,
cellModels: [
{
models: [{
grid: 12,
percentage: 100
}]
}
]
}
]
}
];
@@ -0,0 +1,15 @@
body.mce-content-body {
background: transparent !important;
overflow-x:hidden !important;
padding-bottom: 10px !important;
/*margin:0px;*/
}
p {
margin: 0 0 10px;
}
.button {
display: inline-block;
border-radius:4px;
}
@@ -0,0 +1,46 @@
[
{
"name": "Headline",
"alias": "headline",
"view": "textstring",
"icon": "icon-coin",
"config": {
"style": "font-size: 36px; font-weight: bold",
"markup" : "<h1>#value#</h1>"
}
},
{
"name": "Quote",
"alias": "quote",
"view": "textstring",
"icon": "icon-quote",
"config": {
"style": "border-left: 3px solid #ccc; padding: 10px; color: #ccc; font-family: serif; font-variant: italic; font-size: 18px",
"markup": "<blockquote>#value#</blockquote>"
}
},
{
"name": "Rich text editor",
"alias": "rte",
"view": "rte",
"icon": "icon-article"
},
{
"name": "Media",
"alias": "media",
"view": "media",
"icon": "icon-picture"
},
{
"name": "Macro",
"alias": "macro",
"view": "macro",
"icon": "icon-settings-alt"
},
{
"name": "Embed",
"alias": "embed",
"view": "embed",
"icon": "icon-movie-alt"
}
]
@@ -1,13 +0,0 @@
/*
body{
background: white;
}
.editor{padding: 20px; margin: 20px; border: 3px dashed black; }
.editor:hover{border-color: red;}
*/
@@ -0,0 +1,18 @@
angular.module("umbraco")
.controller("Umbraco.PropertyEditors.Grid.EmbedController",
function ($scope, $rootScope, $timeout, dialogService) {
$scope.setEmbed = function(){
dialogService.embedDialog({
callback: function (data) {
$scope.control.value = data;
}
});
};
$timeout(function(){
if($scope.control.value === null){
$scope.setEmbed();
}
}, 200);
});
@@ -0,0 +1,9 @@
<div ng-controller="Umbraco.PropertyEditors.Grid.EmbedController">
<div class="usky-editor-placeholder" ng-click="setEmbed()" ng-if="control.value === null">
<i class="icon icon-movie-alt"></i>
<div class="help-text">Click to embed</div>
</div>
<div ng-if="control.value" ng-bind-html-unsafe="control.value"></div>
</div>
@@ -0,0 +1,42 @@
angular.module("umbraco")
.controller("Umbraco.PropertyEditors.Grid.MacroController",
function ($scope, $rootScope, $timeout, dialogService, macroResource, macroService, $routeParams) {
$scope.title = "Click to insert macro";
$scope.setMacro = function(){
dialogService.macroPicker({
callback: function (data) {
$scope.control.value = {
syntax: data.syntax,
macroAlias: data.macroAlias,
marcoParamsDictionary: data.macroParamsDictionary
};
$scope.setPreview(data);
}
});
};
$scope.setPreview = function(macro){
var contentId = $routeParams.id;
macroResource.getMacroResultAsHtmlForEditor(macro.macroAlias, contentId, macro.macroParamsDictionary)
.then(function (htmlResult) {
$scope.title = macro.macroAlias;
if(htmlResult.trim().length > 0 && htmlResult.indexOf("Macro:") < 0){
$scope.preview = htmlResult;
}
});
};
$timeout(function(){
if($scope.control.value === null){
$scope.setMacro();
}else{
var parsed = macroService.parseMacroSyntax($scope.control.value.syntax);
$scope.setPreview(parsed);
}
}, 200);
});
@@ -0,0 +1,16 @@
<div ng-controller="Umbraco.PropertyEditors.Grid.MacroController">
<div class="usky-editor-placeholder" ng-click="setMacro()">
<div ng-if="!preview">
<i class="icon icon-settings-alt"></i>
<div class="help-text">{{title}}</div>
</div>
<div ng-if="preview">
<div
ng-if="preview" style="text-align: left"
ng-bind-html-unsafe="preview">
</div>
</div>
</div>
</div>
@@ -0,0 +1,23 @@
angular.module("umbraco")
.controller("Umbraco.PropertyEditors.Grid.MediaController",
function ($scope, $rootScope, $timeout, dialogService) {
$scope.setImage = function(){
dialogService.mediaPicker({
multiPicker: false,
callback: function (data) {
$scope.control.value = {
id: data.id,
image: data.image,
thumbnail: data.thumbnail
};
}
});
};
$timeout(function(){
if($scope.control.value === null){
$scope.setImage();
}
}, 200);
});
@@ -0,0 +1,16 @@
<div ng-controller="Umbraco.PropertyEditors.Grid.MediaController">
<div class="usky-editor-placeholder" ng-click="setImage()" ng-if="control.value === null">
<i class="icon icon-picture"></i>
<div class="help-text">Click to insert image</div>
</div>
<div ng-if="control.value">
<img
ng-if="control.value"
ng-click="setImage()"
ng-src="{{control.value.image}}"
class="fullSizeImage" />
<input type="text" class="caption" ng-model="control.value.caption" placeholder="Image caption..."/>
</div>
</div>
@@ -0,0 +1,4 @@
<div
unique-id="control.uniqueId"
value="control.value"
grid-rte configuration="model.config.rte"></div>
@@ -0,0 +1,17 @@
angular.module("umbraco")
.controller("Umbraco.PropertyEditors.Grid.TextStringController",
function ($scope, $rootScope, $timeout, dialogService) {
$scope.adjustSize = function(ev){
if(ev.target.scrollHeight > ev.target.clientHeight){
$(ev.target).height(ev.target.scrollHeight);
}
};
if($scope.control.value === null){
$timeout(function(){
$("#" + $scope.control.uniqueId +"_text").focus();
}, 200);
}
});
@@ -0,0 +1,3 @@
<div ng-controller="Umbraco.PropertyEditors.Grid.TextStringController">
<textarea class="textstring" id="{{control.uniqueId}}_text" ng-keyup="adjustSize($event)" ng-model="control.value" style="{{control.editor.config.style}}" placeholder="Write here..."></textarea>
</div>
@@ -1,52 +1,262 @@
'use strict';
//this controller simply tells the dialogs service to open a mediaPicker window
//with a specified callback, this callback will receive an object with a selection on it
angular.module("umbraco").controller("Umbraco.PropertyEditors.GridController",
function($rootScope, $scope, dialogService, $log){
//we most likely will need some iframe-motherpage interop here
//we most likely will need some iframe-motherpage interop here
$scope.openMediaPicker =function(){
var d = dialogService.mediaPicker({callback: renderImages});
};
angular.module("umbraco")
.controller("Umbraco.PropertyEditors.GridController",
function ($scope, $http, assetsService, $rootScope, dialogService, gridService, mediaResource, imageHelper, $timeout) {
$scope.openPropertyDialog =function(){
var d = dialogService.property({callback: renderProperty});
};
var gridConfigPath = $scope.model.config.items.gridConfigPath;
$scope.openMacroDialog =function(){
var d = dialogService.macroPicker({callback: renderMacro});
};
if(!gridConfigPath){
gridConfigPath = "views/propertyeditors/grid/grid.default.config.js";
}
function renderProperty(data){
$scope.currentElement.html("<h1>boom, property!</h1>");
}
assetsService.loadJs(gridConfigPath).then(function(){
function renderMacro(data){
// $scope.currentElement.html( macroFactory.renderMacro(data.macro, -1) );
}
function renderImages(data) {
var list = $("<ul class='thumbnails'></ul>");
$.each(data.selection, function(i, image) {
list.append( $("<li class='span2'><img class='thumbnail' src='" + image.src + "'></li>") );
});
// Grid config
$scope.uSkyGridConfig = uSkyGridConfig;
$scope.currentElement.html( list[0].outerHTML);
}
// Grid status variables
$scope.currentRow = null;
$scope.currentCell = null;
$scope.currentToolsControl = null;
$scope.currentControl = null;
$scope.openRTEToolbarId = null;
$(window).bind("umbraco.grid.click", function(event){
gridService.getGridEditors().then(function(response){
$scope.availableEditors = response.data;
});
// *********************************************
// Sortable options
// *********************************************
$scope.$apply(function () {
$scope.currentEditor = event.editor;
$scope.currentElement = $(event.element);
$scope.sortableOptions = {
distance: 10,
cursor: "move",
placeholder: 'ui-sortable-placeholder',
handle: '.cell-tools-move',
start: function (e, ui) {
ui.item.find('.mceNoEditor').each(function () {
tinyMCE.execCommand('mceRemoveEditor', false, $(this).attr('id'));
if(event.editor == "macro")
$scope.openMacroDialog();
else if(event.editor == "image")
$scope.openMediaPicker();
else
$scope.propertyDialog();
});
})
});
});
},
stop: function (e, ui) {
ui.item.find('.mceNoEditor').each(function () {
tinyMCE.execCommand('mceAddEditor', false, $(this).attr('id'));
});
}
};
// *********************************************
// Template management functions
// *********************************************
$scope.checkContent = function() {
var isEmpty = true;
if ($scope.model.value &&
$scope.model.value.columns) {
angular.forEach($scope.model.value.columns, function (value, key) {
if ( value.rows && value.rows.length > 0) {
isEmpty = false;
}
});
}
if (isEmpty)
{
$scope.model.value = undefined;
}
}
$scope.addTemplate = function (template) {
$scope.model.value = {
gridWidth: "",
columns: []
};
angular.forEach(template.columns, function (value, key) {
var newCol = angular.copy(value);
newCol.rows = [];
$scope.model.value.columns.splice($scope.model.value.columns.length + 1, 0, newCol);
});
}
// *********************************************
// Row management function
// *********************************************
$scope.setCurrentRow = function (row) {
$scope.currentRow = row;
}
$scope.disableCurrentRow = function () {
$scope.currentRow = null;
}
$scope.addRow = function (column, cellModel) {
column.rows.splice(column.rows.length + 1, 0,
{
uniqueId: $scope.setUniqueId(),
cells: [],
cssClass: ''
});
for (var i = 0; i < cellModel.models.length; i++) {
var cells = column.rows[column.rows.length - 1].cells;
var model = angular.copy(cellModel.models[i]);
cells.splice(
cells.length + 1, 0,
{
model: model,
controls: []
});
}
};
$scope.removeRow = function (column, $index) {
if (column.rows.length > 0) {
column.rows.splice($index, 1);
$scope.openRTEToolbarId = null;
$scope.checkContent();
}
}
// *********************************************
// Cell management functions
// *********************************************
$scope.setCurrentCell = function (cell) {
$scope.currentCell = cell;
}
$scope.disableCurrentCell = function (cell) {
$scope.currentCell = null;
}
$scope.cellPreview = function(cell){
if($scope.availableEditors && cell && cell.allowed && angular.isArray(cell.allowed)){
var editor = $scope.getEditor(cell.allowed[0]);
return editor.icon;
}else{
return "icon-layout";
}
}
// *********************************************
// Control management functions
// *********************************************
$scope.setCurrentControl = function (Control) {
$scope.currentControl = Control;
};
$scope.disableCurrentControl = function (Control) {
$scope.currentControl = null;
};
$scope.setCurrentToolsControl = function (Control) {
$scope.currentToolsControl = Control;
};
$scope.disableCurrentToolsControl = function (Control) {
$scope.currentToolsControl = null;
};
$scope.setCurrentRemoveControl = function (Control) {
$scope.currentRemoveControl = Control;
};
$scope.disableCurrentRemoveControl = function (Control) {
$scope.currentRemoveControl = null;
};
$scope.setCurrentMoveControl = function (Control) {
$scope.currentMoveControl = Control;
};
$scope.disableCurrentMoveControl = function (Control) {
$scope.currentMoveControl = null;
};
$scope.setUniqueId = function (cell, index) {
var date = new Date();
var components = [
date.getYear(),
date.getMonth(),
date.getDate(),
date.getHours(),
date.getMinutes(),
date.getSeconds(),
date.getMilliseconds()
];
return components.join("");
};
$scope.setEditorPath = function(control){
control.editorPath = "views/propertyeditors/grid/editors/" + control.editor.view + ".html";
};
$scope.addControl = function (editor, cell, index){
var newId = $scope.setUniqueId();
var newControl = {
uniqueId: newId,
value: null,
editor: editor
};
if (index === undefined) {
index = cell.controls.length;
}
cell.controls.splice(index + 1, 0, newControl);
};
$scope.addTinyMce = function(cell){
var rte = _.find($scope.availableEditors, function(editor){return editor.alias === "rte";});
$scope.addControl(rte, cell);
};
$scope.getEditor = function(alias){
return _.find($scope.availableEditors, function(editor){return editor.alias === alias});
};
$scope.removeControl = function (cell, $index) {
cell.controls.splice($index, 1);
};
$scope.allowedControl = function (editor, cell){
if(cell.model.allowed && angular.isArray(cell.model.allowed)){
return _.contains(cell.model.allowed, editor.alias);
}else{
return true;
}
};
// *********************************************
// Init grid
// *********************************************
/* init grid data */
if ($scope.model.value && $scope.model.value != "") {
if (!$scope.model.config.items.enableGridWidth) {
$scope.model.value.gridWidth = $scope.model.config.items.defaultGridWith;
}
}
$scope.checkContent();
});
// *********************************************
// asset styles
// *********************************************
//assetsService.loadCss("/App_Plugins/Lecoati.uSky.Grid/lib/jquery-ui-1.10.4.custom/css/ui-lightness/jquery-ui-1.10.4.custom.min.css");
//assetsService.loadCss($scope.model.config.items.approvedBackgroundCss);
});
@@ -1,3 +1,246 @@
<div class="umb-editor umb-grid" ng-controller="Umbraco.PropertyEditors.GridController" auto-scale="10">
<iframe style="width: 100%; height: 100%;" src="views/propertyeditors/grid/iframe.html"></iframe>
<div ng-controller="Umbraco.PropertyEditors.GridController" class="usky-grid">
<!-- Template picker -->
<div class="uSky-templates tb" ng-show="!model.value || model.value == ''">
<div class="tr">
<div class="td middle">
<div class="uSky-templates-template" ng-repeat="template in uSkyGridConfig" style="">
<div class="tb" ng-click="addTemplate(template)">
<div class="tr">
<div class="td uSky-templates-column"
ng-class="{last:$last}"
ng-repeat="column in template.columns"
style="width:{{ column.percentage }}%">
</div>
</div>
</div>
</div>
<br/>
<div class="help-text">To start, select a layout for your grid</div>
</div>
</div>
</div>
<!-- template picker end -->
<!-- Grids -->
<div class="usky-grid-width" style="width:{{ model.value.gridWidth }}px">
<div class="tb">
<!-- for each column in model -->
<div
class="usky-column td"
ng-class="{last:$index==model.value.columns.length-1}"
ng-repeat="column in model.value.columns"
style="width:{{column .percentage}}%">
<div ui-sortable="sortableOptions" ng-model="column.rows">
<!-- for each row in column -->
<div
ng-repeat="row in column.rows"
class="usky-row"
ng-mouseover="setCurrentRow(row)"
ng-mouseleave="disableCurrentRow()"
ng-class="{removingControl:currentRemoveControl == row, movingControl:currentMoveControl == row}">
<!-- Row tool -->
<div class="row-tools" ng-animate="'fade'"
ng-if="currentRow == row && currentControl === null">
<!-- delete row -->
<div class="cell-tools-remove">
<div class="iconBox"
ng-click="deletePrompt = true"
ng-mouseover="setCurrentRemoveControl(row)"
ng-mouseleave="disableCurrentRemoveControl(row)">
<span ng-if="deletePrompt">
Are you sure?
<a href style="text-decoration: underline" ng-click="removeRow(column, $index)">Yes</a>
</span>
<i ng-if="!deletePrompt" class="icon icon-trash"></i>
</div>
</div>
<div class="cell-tools-move">
<div class="iconBox"
ng-mouseover="setCurrentMoveControl(row)"
ng-mouseleave="disableCurrentMoveControl(row)">
<i class="icon icon-navigation"></i>
</div>
</div>
</div>
<!-- row container -->
<div
ng-class="{last:$index==model.value.length-1,first:$first}"
class="{{row.cssClass}} mainContainer">
<div class="mainTb">
<div class="tb">
<div class="tr">
<!-- cells in row -->
<div
style="width:{{ cell.model.percentage }}%"
ng-class="{last:$index==row.cells.length-1,first:$first}"
class="td mainTd usky-cell"
ng-repeat="cell in row.cells">
<!-- if cell is empty tools -->
<div
class="usky-control usky-control-placeholder"
ng-if="cell.controls.length == 0">
<div
ng-if="cell.model.allowed && cell.model.allowed.length === 1"
ng-init="singleEditor = getEditor(cell.model.allowed[0])"
class="cell-tools-add-single single-editor-{{singleEditor.alias}}">
<a href ng-click="addControl(singleEditor, cell)">
<i class="icon {{singleEditor.icon}}"></i>
<div class="help-text">{{singleEditor.name}}</div>
</a>
<p class="placeholder" ng-click="addControl(singleEditor, cell)">
Start writing here...
</p>
</div>
<div ng-if="!cell.model.allowed || cell.model.allowed.length > 1">
<p class="placeholder" ng-click="addTinyMce(cell)">
Start writing here...
</p>
<div class="cell-tools-add">
<div
class="iconBox"
ng-repeat="editor in availableEditors track by editor.alias"
ng-click="addControl(editor,cell)"
ng-show="allowedControl(editor, cell)">
<i class="icon {{editor.icon}}"></i>
</div>
</div>
</div>
</div>
<!-- Controls in cell -->
<div
ng-repeat="control in cell.controls"
ng-class="{last:$index==cell.controls.length-1,
removingControl:currentRemoveControl == control,
toolsControl:currentToolsControl == control,
selectedControl:currentControl == control}"
ng-mouseover="setCurrentControl(control)"
ng-mouseleave="disableCurrentControl(control)"
ng-init="setEditorPath(control); controlIndex = $index;"
class="usky-control">
<!-- Filled cell tools -->
<div class="cell-tools"
ng-animate="'fade'"
ng-if="control && ( currentControl == control)">
<div class="cell-tools-add"
ng-if="cell.model.max === undefined || cell.model.max < cell.controls.length"
ng-mouseover="setCurrentToolsControl(control)"
ng-mouseleave="disableCurrentToolsControl(control)">
<div
class="iconBox"
ng-repeat="editor in availableEditors"
ng-click="addControl(editor,cell, controlIndex)"
ng-show="(currentToolsControl == control) && allowedControl(editor, cell)">
<i class="icon {{editor.icon}}"></i>
</div>
<div class="iconBox"
ng-if="currentToolsControl != control">
<i class=" icon icon-add"></i>
</div>
</div>
<!-- delete cell -->
<div class="cell-tools-remove">
<div class="iconBox"
ng-click="deletePrompt = true"
ng-mouseover="setCurrentRemoveControl(control)"
ng-mouseleave="disableCurrentRemoveControl(control)">
<span ng-if="deletePrompt">
Are you sure?
<a href style="text-decoration: underline" ng-click="removeControl(cell, controlIndex)">Yes</a>
</span>
<i ng-if="!deletePrompt" class="icon icon-trash"></i>
</div>
</div>
</div>
<!-- Redering the editor for specific control -->
<div
ng-if="control && control.editorPath"
ng-include="control.editorPath"
class="usky-cell usky-cell-{{control.editor.view}}">
</div>
</div>
<!-- Controls repeat end -->
</div>
<!-- cells repeat end -->
</div>
</div>
</div>
</div>
<!-- row container end -->
</div>
<!-- row repeat -->
</div>
<!-- row sortable end -->
<!-- column tools -->
<div class="usky-templates-columns tb">
<div class="tr">
<div class="td middle">
<ul>
<li ng-repeat="cellModel in column.cellModels">
<div class="tb mainTbpt" ng-click="addRow(column, cellModel)">
<div class="tr">
<div style="width:{{ cell.percentage; }}%" class="middle mainTdpt td" ng-repeat="cell in cellModel.models">
<i class="icon {{cellPreview(cell)}}"></i>
</div>
</div>
</div>
<small>{{cellModel.name}}</small>
</li>
</ul>
</div>
</div>
<div class="tr">
<div class="td middle">
<br/>
<span class="help-text">Add rows to your layout</span>
</div>
</div>
</div>
<!-- column tools end -->
</div>
<!-- column repeat end -->
</div>
</div>
<!-- grid wrapper end -->
</div>
@@ -0,0 +1,41 @@
angular.module("umbraco")
.controller("Umbraco.PropertyEditors.GridPrevalueEditorController",
function ($scope, $http, assetsService, $rootScope, dialogService, mediaResource, gridService, imageHelper, $timeout) {
var emptyModel = {
enableGridWidth: true,
defaultGridWith: "",
enableBackground: true,
enableSkipTopMargin: true,
defaultSkipTopMargin: false,
enableSkipBottomMargin: true,
defaultSkipBottomMargin: false,
enableSkipLeftMargin: true,
defaultSkipLeftMargin: false,
enableSkipRightMargin: true,
defaultSkipRightMargin: false,
enableSkipControlMargin: true,
defaultSkipControlMargin: false,
enableFullScreen: true,
defaultFullScreen: false,
enableBoxed: true,
defaultBoxed: false,
enableRte: true,
enableMedia: true,
enableMacro: true,
enabledEditors: [ "rte", "media", "macro" ],
enableMultiCells: true,
approvedBackgroundCss: "",
gridConfigPath: "views/propertyeditors/grid/config/grid.default.config.js"
}
gridService.getGridEditors().then(function(response){
$scope.editors = response.data;
});
/* init grid data */
if (!$scope.model.value || $scope.model.value == "") {
$scope.model.value = emptyModel;
}
})
@@ -0,0 +1,109 @@
<div ng-controller="Umbraco.PropertyEditors.GridPrevalueEditorController">
<p><strong>Config file path:</strong></p>
<table cellpadding="20" style="margin-bottom:20px">
<tr>
<td>
<input ng-model="model.value.gridConfigPath" type="text" style="width:500px;">
</td>
</tr>
</table>
<!--<p><strong>Allowed row settings:</strong></p>
<table cellpadding="20" style="margin-bottom:20px">
<tr>
<td>
<p><i class="icon icon-paint-roller"></i>&nbsp;Enable/Disable row background</p>
<input ng-model="model.value.enableBackground" type="checkbox">
<label for="">Enable</label>
</td>
<td>
<p><i class="icon icon-navigation-top"></i>&nbsp;Enable/Disable top margin</p>
<input ng-model="model.value.enableSkipTopMargin" type="checkbox">
<label for="">Enable</label>
<br />
<input ng-model="model.value.defaultSkipTopMargin" type="checkbox">
<label for="">Default value</label>
</td>
<td>
<p><i class="icon icon-navigation-bottom"></i>&nbsp;Enable/Disable bottom margin</p>
<input ng-model="model.value.enableSkipBottomMargin" type="checkbox">
<label for="">Enable</label>
<br />
<input ng-model="model.value.defaultSkipBottomMargin" type="checkbox">
<label for="">Default value</label>
</td>
</tr>
<tr>
<td>
<p><i class="icon icon-navigation-first"></i>&nbsp;Enable/Disable left margin</p>
<input ng-model="model.value.enableSkipLeftMargin" type="checkbox">
<label for="">Enable</label>
<br />
<input ng-model="model.value.defaultSkipLeftMargin" type="checkbox">
<label for="">Default value</label>
</td>
<td>
<p><i class="icon icon-navigation-last"></i>&nbsp;Enable/Disable right margin</p>
<input ng-model="model.value.enableSkipRightMargin" type="checkbox">
<label for="">Enable</label>
<br />
<input ng-model="model.value.defaultSkipRightMargin" type="checkbox">
<label for="">Default value</label>
</td>
<td>
<p><i class="icon icon-fullscreen"></i>&nbsp;Enable/Disable control margin</p>
<input ng-model="model.value.enableSkipControlMargin" type="checkbox">
<label for="">Enable</label>
<br />
<input ng-model="model.value.defaultSkipControlMargin" type="checkbox">
<label for="">Default value</label>
</td>
</tr>
<tr>
<td>
<p><i class="icon icon-width"></i>&nbsp;Enable/Disable full screen</p>
<input ng-model="model.value.enableFullScreen" type="checkbox">
<label for="">Enable</label>
<br />
<input ng-model="model.value.defaultFullScreen" type="checkbox">
<label for="">Default value</label>
</td>
<td>
<p><i class="icon icon-frame"></i>&nbsp;Enable/Disable boxed</p>
<input ng-model="model.value.enableBoxed" type="checkbox">
<label for="">Enable</label>
<br />
<input ng-model="model.value.defaultBoxed" type="checkbox">
<label for="">Default value</label>
</td>
</tr>
</table>-->
<!--<p><strong>Allowed editors:</strong></p>
<table cellpadding="20" style="margin-bottom:20px">
<tr>
<td>
<p ng-repeat="editor in editors">
<input type="checkbox"
checklist-model="model.value.enabledEditors"
checklist-value="editor.alias">
&nbsp;<i class="icon {{editor.icon}}"></i>&nbsp; {{editor.name}}
</p>
</td>
</tr>
</table>-->
<p><strong>Others settings:</strong></p>
<table cellpadding="20" style="margin-bottom:20px">
<tr>
<td>
<p><input ng-model="model.value.enableMultiCells" type="checkbox">&nbsp;<i class="icon icon-edit"></i>&nbsp;Multiple controls in cell</p>
<!--<p>Approved background stylesheet path:&nbsp;<input ng-model="model.value.approvedBackgroundCss" type="text" style="width:300px;"></p>-->
</td>
</tr>
</table>
</div>
@@ -1,69 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../../../assets/css/umbraco.css" />
</head>
<body>
<div class="row-fluid">
<div class="span12" data-editor="rte">
<i class="icon-move"></i>
<i class="icon-edit"></i>
<i class="icon-trash"></i>
Headline
</div>
</div>
<div class="row-fluid">
<div class="span6" data-editor="rte">
Rich Text editor
</div>
<div class="span6" data-editor="macro">
<i class="icon-move"></i>
<i class="icon-edit"></i>
<i class="icon-trash"></i>
Insert macro
</div>
</div>
<div class="row-fluid">
<div class="span4" data-editor="macro">
<i class="icon-move"></i>
<i class="icon-edit"></i>
<i class="icon-trash"></i>
Insert Macro
</div>
<div class="span4" data-editor="image">
<i class="icon-move"></i>
<i class="icon-edit"></i>
<i class="icon-trash"></i>
Insert image
</div>
<div class="span4" data-editor="image">
<i class="icon-move"></i>
<i class="icon-edit"></i>
<i class="icon-trash"></i>
Insert image
</div>
</div>
<div class="row-fluid">
<div class="span12" data-editor="rte">
<i class="icon-move"></i>
<i class="icon-edit"></i>
<i class="icon-trash"></i>
Rich Text editor
</div>
</div>
<div class="row-fluid">
<div class="span12" data-editor="rte">
<i class="icon-move"></i>
<i class="icon-edit"></i>
<i class="icon-trash"></i>
Footer
</div>
</div>
<script src="../../../lib/jquery/jquery-2.0.3.min.js"></script>
<script src="js/iframe.js"></script>
</body>
</html>
@@ -1,19 +0,0 @@
$(function(){
var editors = $('[data-editor]');
var p = parent.$(parent.document);
editors.addClass("editor");
editors.on("click", function (event) {
event.preventDefault();
// parent.document.fire("umbraco.grid.click");
var el = this;
var e = jQuery.Event("umbraco.grid.click", {editor: $(el).data("editor"), element: el});
p.trigger( e );
});
});
@@ -0,0 +1,53 @@
/**
* @ngdoc controller
* @name Umbraco.Editors.Template.EditController
* @function
*
* @description
* The controller for editing templates
*/
function TemplateEditController($scope, $http, assetsService, templateResource, $routeParams, $log) {
$scope.pageId = 1069;
$scope.rendering = false;
$scope.aceLoaded = function(_editor) {
// Options
};
var render = _.throttle(function(){
templateResource.saveAndRender($scope.template.content, $scope.template.id, $scope.pageId).then(
function(response){
var iframe = document.getElementById('mockingbird');
iframe = (iframe.contentWindow) ? iframe.contentWindow : (iframe.contentDocument.document) ? iframe.contentDocument.document : iframe.contentDocument;
iframe.document.open();
iframe.document.write(response);
iframe.document.close();
$scope.rendering = false;
},
function(response){
$log.log(response);
$scope.rendering = false;
}
);
}, 1000);
$scope.aceChanged = function(e) {
if(!$scope.rendering){
$scope.rendering = true;
render();
}
};
templateResource.getById($routeParams.id).then(function(template){
$scope.template = template;
if(!$scope.rendering){
$scope.rendering = true;
render();
}
});
}
angular.module("umbraco").controller("Umbraco.Editors.Template.EditController", TemplateEditController);
@@ -0,0 +1,26 @@
<div ng-form name="contentForm" val-form-manager>
<umb-panel ng-controller="Umbraco.Editors.Template.EditController">
<umb-header>
<h1>{{template.name}}</h1>
</umb-header>
<div class="umb-panel-body umb-scrollable row-fluid">
<div class="span6" style="position: relative; height: 100%">
<div style="width: 100%; height: 100%; top: 0; bottom: 0; position: absolute" ng-model="template.content" ui-ace="{
useWrapMode : true,
showGutter: true,
theme:'twilight',
mode: 'xml',
onLoad: aceLoaded,
onChange: aceChanged
}"></div>
</div>
<div class="span6" style="position: relative; height: 100%">
<iframe id="mockingbird" style="width: 100%; height: 100%; top: 0; bottom: 0; position: absolute" id="preview" />
</div>
</div>
</umb-panel>
</div>
@@ -1,13 +0,0 @@
/**
* @ngdoc controller
* @name Umbraco.Editors.Settings.Template.EditController
* @function
*
* @description
* The controller for editing templates
*/
function TemplateEditController($scope, navigationService) {
$scope.template = "<html><body><h1>Hej</h1></body></html>";
}
angular.module("umbraco").controller("Umbraco.Editors.Settings.Template.EditController", TemplateEditController);
@@ -1,19 +0,0 @@
<div ng-form name="contentForm" val-form-manager>
<umb-panel ng-controller="Umbraco.Editors.Settings.Template.EditController">
<umb-header>
<h1>woop</h1>
</umb-header>
<div class="umb-pane row-fluid">
<div class="span6">
<div ace="html" ng-model="template">
<textarea></textarea>
</div>
</div>
<div class="span6">
<iframe style="width: 100%; height: 100%" id="preview" />
</div>
</div>
</umb-panel>
</div>
+4 -3
View File
@@ -2075,6 +2075,7 @@
<Content Include="Web.config">
<SubType>Designer</SubType>
</Content>
<Content Include="Views\Partials\Grid\bootstrap3.cshtml" />
<None Include="Web.Debug.config.transformed" />
<None Include="web.Template.Debug.config">
<DependentUpon>Web.Template.config</DependentUpon>
@@ -2505,11 +2506,11 @@
<Folder Include="Media\" />
<Folder Include="Scripts\" />
<Folder Include="Umbraco\PartialViews\Templates\" />
<Folder Include="Umbraco\preview\" />
<Folder Include="Umbraco_Client\FolderBrowser\Images\" />
<Folder Include="Umbraco_Client\Tags\images\" />
<Folder Include="UserControls\" />
<Folder Include="Views\MacroPartials\" />
<Folder Include="Views\Partials\" />
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
@@ -2530,9 +2531,9 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.0\x86\*.* "$(TargetDir)x86\"
<WebProjectProperties>
<UseIIS>True</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>7150</DevelopmentServerPort>
<DevelopmentServerPort>7200</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:7150</IISUrl>
<IISUrl>http://localhost:7200</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl>
@@ -0,0 +1,238 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link href="/Umbraco/lib/bootstrap/css/bootstrap.min.3.0.1.css" type="text/css" rel="stylesheet" />
<link href="/Umbraco/assets/css/tuning.panelStyles.css" type="text/css" rel="stylesheet" />
<link href="/Umbraco/lib/spectrum/spectrum.css" type="text/css" rel="stylesheet" />
<link href="/Umbraco/lib/jquery/jquery-ui-1.10.4.custom.min.css" type="text/css" rel="stylesheet" />
</head>
<body id="tuningPanel" ng-class="{open:isOpen}" ng-controller="Umbraco.tuningController">
<div class="wait" ng-show="!frameLoaded">Loading ...</div>
<div id="demo-iframe-wrapper" ng-show="frameLoaded" class="{{previewDevice.css}}">
<iframe id="resultFrame" ng-src="{{pageId}}" frameborder="0"></iframe>
</div>
<div class="tuning">
<div id="TuningBg" ng-class="{open:isOpen}" style="display: block;">
<div class="tuning-tools">
<div ng-click="togglePanel()">
<i ng-if="isOpen" class="icon icon-left-double-arrow"></i>
<i ng-if="!isOpen" class="icon icon-right-double-arrow"></i>
</div>
<div ng-if="isOpen" ng-click="settingOpen('previewDevice')" ng-class="{selected:settingIsOpen=='previewDevice', show:frameLoaded}" style="display:none">
<i class="icon icon-display"></i>
</div>
<div ng-if="isOpen" ng-click="settingOpen('setting')" ng-class="{selected:settingIsOpen=='setting', show:enableTuning > 0}" style="display:none">
<i class="icon icon-paint-roller"></i>
</div>
<div ng-if="isOpen" ng-click="settingOpen('presetColors')" ng-class="{selected:settingIsOpen=='presetColors', show:enableTuning > 0}" style="display:none">
<i class="icon icon-palette"></i>
</div>
</div>
<div class="tuning-wrapper">
<div ng-show="settingIsOpen == 'previewDevice'">
<h2>DEVICES</h2>
<ul class="preview-device">
<li ng-repeat="device in devices" ng-class=" { selected:previewDevice==device }" ng-click="updatePreviewDevice(device)">
<div><i class="icon {{device.icon}}"></i><span>{{device.name}}</span></div>
</li>
</ul>
</div>
<div ng-show="settingIsOpen == 'setting'">
<tabset vertical="false">
<tab ng-repeat="category in tuningModel.categories" active="category.active">
<tab-heading>
<span>{{category.name}}</span>
</tab-heading>
<div ng-repeat="section in category.sections">
<accordion close-others="true">
<accordion-group ng-repeat="subSection in section.subSections"
ng-class="{true:'selected', false:''}[isOpen]"
is-open="(schemaFocus != '' && schemaFocus == subSection.name.toLowerCase())">
<accordion-heading>
<span ng-click="accordionOpened(subSection.name)" ng-mouseover="accordionWillBeOpened(subSection.schema)">
{{subSection.name}}
</span>
</accordion-heading>
<div ng-mouseover="accordionWillBeOpened(subSection.schema)">
<div class="div-field" ng-repeat="field in subSection.fields">
<span class="field-title">{{field.name}}:</span>
<div ng-if="field.type == 'colorPicker'">
<spectrum colorselected="field.value"></spectrum>
<i ng-if="field.value != ''" ng-click="removeField(field)" class="icon icon-delete colorPickerDelete"></i>
</div>
<div ng-if="field.type == 'bgImagePicker'" class="imagePickerPreviewContainer">
<div class="imagePickerPreview" ng-click="open(field)" style="background-image:{{ field.value }}">
<i class="icon icon-add"></i>
</div>
<i ng-if="field.value != ''" ng-click="removeField(field)" class="icon icon-delete colorPickerDelete"></i>
</div>
<div ng-if="field.type == 'bgPositionPicker'">
<select ng-options="BackgroundPosition for BackgroundPosition in BackgroundPositions" ng-model="field.value"></select>
</div>
<div ng-if="field.type == 'bgRepeatPicker'">
<select ng-options="BackgroundRepeat for BackgroundRepeat in BackgroundRepeats" ng-model="field.value"></select>
</div>
<div ng-if="field.type == 'bgAttachmentPicker'">
<select ng-options="BackgroundAttachment for BackgroundAttachment in BackgroundAttachments" ng-model="field.value"></select>
</div>
<div ng-if="field.type == 'layoutPicker'">
<select ng-options="Layout for Layout in Layouts" ng-model="field.value"></select>
</div>
<div class="box-slider" ng-if="field.type == 'slider'">
<div ui-slider min="{{field.min}}" max="{{field.max}}" step="1" ng-model="field.value"></div>
</div>
<div ng-if="field.type == 'checkbox'">
<input type="checkbox" ng-checked="field.value == 'true'" ng-model="field.value">
</div>
<div ng-if="field.type == 'displayPicker'">
<select ng-options="display for display in displays" ng-model="field.value"></select>
</div>
<div ng-if="field.type == 'optionHomePicker'">
<select ng-options="optionHome for optionHome in optionHomes" ng-model="field.value"></select>
</div>
<div ng-if="field.type == 'fontFamilyPicker'">
<div class="fontFamilyPickerPreview" ng-click="openFontFamilyPickerModal(field)" style="font-family:{{ field.value }}">
<span>Aa</span>
{{ field.value }}
</div>
<i ng-if="field.value != ''" ng-click="removeField(field)" class="fa fa-times colorPickerDelete"></i>
</div>
</div>
</div>
</accordion-group>
</accordion>
</div>
</tab>
</tabset>
<button class="btn btn-default btn-default-save" ng-click="saveLessParameters()">save</button>
<button class="btn btn-default btn-default-delete" ng-click="deleteTuning()">reset</button>
</div>
<div ng-show="settingIsOpen == 'presetColors'">
<h2>PALETTE COLORS</h2>
<ul class="palette">
<li ng-click="refreshtuningByPalette(palette)" ng-repeat="palette in tuningPalette">
<div>
<span style="background-color: {{ palette.mainColor }}"></span>
</div>
<p>{{ palette.name }}</p>
</li>
</ul>
<button class="btn btn-default btn-default-save" style="display:none" ng-click="saveLessParameters()">save</button>
<button class="btn btn-default btn-default-save" ng-click="makePreset()">make it !</button>
</div>
</div>
<script type="text/ng-template" id="myModalContent.html">
<div class="modal-header bodyTuningImagePicker ng-scope">
<ul class="breadcrumb">
<li ng-if="startNodeId == -1">
<a href="" ng-click="gotoFolder()">Media</a>
</li>
<!-- ngRepeat: item in path -->
<li ng-repeat="item in currentPath" class="ng-scope">
<a ng-if="currentFolder.id == item.id" href="" ng-class="{disabled:currentFolder.id == item.id}">{{ item.name }}</a>
<a ng-if="currentFolder.id != item.id" ng-click="gotoFolder(item)">{{ item.name }}</a>
</li>
</ul>
</div>
<div class="modal-body bodyTuningImagePicker">
<ul class="tuningImagePicker">
<li>
<ul class="media-items">
<li ng-repeat="child in currentFolder.children | orderBy:'isFolder':true">
<div ng-if="!child.isFolder" class=" media-preview" ng-class="{selected:selectedMedia.id == child.id}" ng-click="selectMedia(child)" style="background-image: url({{ child.thumbnail }})"></div>
<div ng-if="child.isFolder" class=" media-preview" ng-click="selectMedia(child)">
<i class="icon icon-folder folder"><p class="folder-name">{{child.name}}</p></i>
</div>
</li>
</ul>
</li>
</ul>
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="ok()">OK</button>
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
</div>
</script>
<script type="text/ng-template" id="fontFamilyPickerModel.html">
<div class="modal-header tuning-fontfamilypicker">
<select ng-model="selectedFont" ng-change="showFontPreview(selectedFont)" ng-options="font as font.fontFamily group by font.fontType for font in fonts"></select>
<select ng-model="selectedFont.variant" ng-change="showFontPreview(selectedFont)" ng-options="variant for variant in selectedFont.variants" />
</div>
<div class="modal-body tuning-fontfamilypicker">
<span class="show" ng-style="setStyleVariant()">Aa Bb Cc 1 2 3 4&hellip; <br />The quick brown fox jumps over the lazy dog&hellip;</span>
</div>
<div class="modal-footer tuning-fontfamilypicker">
<button class="btn btn-primary" ng-click="ok()">OK</button>
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
</div>
</script>
</div>
</div>
<div id="speechbubble">
<p>Styles saved and visible at the website</p>
</div>
<script src="/umbraco/lib/lazyload/lazyload.min.js"></script>
<script src="/umbraco/js/tuning.loader.js"></script>
</body>
</html>
@@ -0,0 +1,111 @@
@inherits UmbracoViewPage<dynamic>
@using Umbraco.Web.Templates
@if (Model != null && !string.IsNullOrEmpty(Model.ToString()))
{
var onlyOneColumn = Model.columns != null ? ((System.Collections.ICollection)Model.columns).Count : 0;
<div class="uSky-grid @(onlyOneColumn > 1 ? "container" : "")">
<div class="row clearfix">
@foreach (var column in Model.columns)
{
<div class="col-md-@column.grid column">
@foreach (var row in column.rows)
{
<div class="gridrow_@row.uniqueId" >
<div class="container">
<div class="row clearfix">
@foreach (var cell in row.cells)
{
<div class="col-md-@cell.model.grid column">
@foreach (var control in cell.controls)
{
if (control != null && control.editor != null && control.editor.view != null)
{
string editor = control.editor.view.ToString();
switch (editor)
{
case "rte":
<text>
@Html.Raw(TemplateUtilities.ParseInternalLinks(control.value.ToString()))
</text>
break;
case "macro":
if (control.value != null)
{
string macroAlias = control.value.macroAlias.ToString();
ViewDataDictionary parameters = new ViewDataDictionary();
foreach (dynamic mpd in control.value.marcoParamsDictionary)
{
parameters.Add(mpd.Name, mpd.Value);
}
<text>
@Umbraco.RenderMacro(macroAlias, parameters)
</text>
}
break;
case "textstring":
if (control.editor.config.markup != null)
{
string markup = control.editor.config.markup.ToString();
markup = markup.Replace("#value#", control.value.ToString());
markup = markup.Replace("#style#", control.editor.config.style.ToString());
<text>
@Html.Raw(markup)
</text>
}
else
{
<text>
<div style="@control.editor.config.style">@control.value</div>
</text>
}
break;
case "media":
<text>
@if (control.value != null)
{
<div class="thumbnail">
<img src="@control.value.image">
</div>
if (control.value.caption != null)
{
<p class="caption">@control.value.caption</p>
}
}
</text>
break;
case "embed":
<text>
@Html.Raw(control.value)
</text>
break;
default:
break;
}
}
}
</div>
}
</div>
</div>
</div>
}
</div>
}
</div>
</div>
}
@@ -10,7 +10,7 @@ NOTES:
* Compression/Combination/Minification is not enabled unless debug="false" is specified on the 'compiliation' element in the web.config
* A new version will invalidate both client and server cache and create new persisted files
-->
<clientDependency version="1469612626" fileDependencyExtensions=".js,.css" loggerType="Umbraco.Web.UI.CdfLogger, umbraco">
<clientDependency version="912222045" fileDependencyExtensions=".js,.css" loggerType="Umbraco.Web.UI.CdfLogger, umbraco">
<!--
This section is used for Web Forms only, the enableCompositeFiles="true" is optional and by default is set to true.
+1
View File
@@ -39,4 +39,5 @@
<!-- Custom -->
<!--<add application="myApplication" alias="myTree" title="Me Tree" type="MyNamespace.myTree, MyAssembly"
iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="10" />-->
<add initialize="true" sortOrder="0" alias="template" application="settings" title="Templates" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.TemplateTreeController, umbraco" />
</trees>
@@ -29,7 +29,8 @@
<PropertyContextHelpOption>text</PropertyContextHelpOption>
<!-- The html injected into a (x)html page if Umbraco is running in preview mode -->
<PreviewBadge><![CDATA[<a id="umbracoPreviewBadge" style="position: absolute; top: 0; right: 0; border: 0; width: 149px; height: 149px; background: url('{1}/preview/previewModeBadge.png') no-repeat;" href="{0}/endPreview.aspx?redir={2}"><span style="display:none;">In Preview Mode - click to end</span></a>]]></PreviewBadge>
<PreviewBadge>
<![CDATA[<a id="umbracoPreviewBadge" style="position: absolute; top: 0; right: 0; border: 0; width: 149px; height: 149px; background: url('{1}/preview/previewModeBadge.png') no-repeat;" href="#" OnClick="javascript:window.top.location.href = '{0}/endPreview.aspx?redir={2}'"><span style="display:none;">In Preview Mode - click to end</span></a>]]></PreviewBadge>
<!-- Cache cycle of Media and Member data fetched from the umbraco.library methods -->
<!-- In seconds. 0 will disable cache -->
@@ -76,7 +76,9 @@
<PropertyContextHelpOption>text</PropertyContextHelpOption>
<!-- The html injected into a (x)html page if Umbraco is running in preview mode -->
<PreviewBadge><![CDATA[<a id="umbracoPreviewBadge" style="position: absolute; top: 0; right: 0; border: 0; width: 149px; height: 149px; background: url('{1}/preview/previewModeBadge.png') no-repeat;" href="{0}/endPreview.aspx?redir={2}"><span style="display:none;">In Preview Mode - click to end</span></a>]]></PreviewBadge>
<PreviewBadge>
<![CDATA[<a id="umbracoPreviewBadge" style="position: absolute; top: 0; right: 0; border: 0; width: 149px; height: 149px; background: url('{1}/preview/previewModeBadge.png') no-repeat;" href="#" OnClick="javascript:window.top.location.href = '{0}/endPreview.aspx?redir={2}'"><span style="display:none;">In Preview Mode - click to end</span></a>
]]></PreviewBadge>
<!-- Cache cycle of Media and Member data fetched from the umbraco.library methods -->
<!-- In seconds. 0 will disable cache -->
@@ -245,6 +245,7 @@
</area>
<area alias="editcontenttype">
<key alias="allowedchildnodetypes">Allowed child node types</key>
<key alias="contenttypecompositions">Document Type Compositions</key>
<key alias="create">Create</key>
<key alias="deletetab">Delete tab</key>
<key alias="description">Description</key>
@@ -251,6 +251,7 @@
<area alias="editcontenttype">
<key alias="allowedchildnodetypes">Allowed child nodetypes</key>
<key alias="contenttypecompositions">Document Type Compositions</key>
<key alias="create">Create</key>
<key alias="deletetab">Delete tab</key>
<key alias="description">Description</key>
@@ -81,13 +81,20 @@
<asp:CheckBox runat="server" ID="cb_isContainer" Text="Yes" /><br />
</cc2:PropertyPanel>
</cc2:Pane>
<cc2:Pane ID="Pane5" runat="server">
<cc2:PropertyPanel ID="pp_allowedChildren" runat="server" Text="Allowed Child nodetypes">
<asp:CheckBoxList ID="lstAllowedContentTypes" runat="server" EnableViewState="True"/>
<asp:PlaceHolder ID="PlaceHolderAllowedContentTypes" runat="server"/>
</cc2:PropertyPanel>
</cc2:Pane>
<cc2:Pane ID="Pane5" runat="server">
<cc2:PropertyPanel ID="pp_allowedChildren" runat="server" Text="Allowed Child nodetypes">
<asp:CheckBoxList ID="lstAllowedContentTypes" runat="server" EnableViewState="True"/>
<asp:PlaceHolder ID="PlaceHolderAllowedContentTypes" runat="server"/>
</cc2:PropertyPanel>
</cc2:Pane>
<cc2:Pane ID="Pane9" runat="server">
<cc2:PropertyPanel ID="pp_compositions" runat="server" Text="ContentType Compositions">
<asp:CheckBoxList ID="lstContentTypeCompositions" runat="server" EnableViewState="True"/>
<asp:PlaceHolder ID="PlaceHolderContentTypeCompositions" runat="server"/>
</cc2:PropertyPanel>
</cc2:Pane>
</asp:Panel>
@@ -202,7 +202,12 @@ namespace Umbraco.Web.UI.Umbraco.Settings.Views
ClientCallbackOpenMacroModel = "function(alias) {editViewEditor.openMacroModal(alias);}"
};
editorSource.Menu.InsertNewControl(macroSplitButton, 40);
MenuIconI umbTemplateQueryBuilder = editorSource.Menu.NewIcon();
umbTemplateQueryBuilder.ImageURL = UmbracoPath + "/images/editor/inshtml.gif";
umbTemplateQueryBuilder.OnClickCommand = "editViewEditor.openQueryModal()";
umbTemplateQueryBuilder.AltText = "Open query builder";
if (_template == null)
{
InitializeEditorForPartialView();
@@ -157,6 +157,18 @@ Umbraco.Sys.registerNamespace("Umbraco.Application");
},
/** This is used to launch an angular based modal window instead of the legacy window */
rootScope: function () {
if (!this.mainWindow().UmbClientMgr) {
throw "An angular modal window can only be launched when the modal is running within the main Umbraco application";
}
else {
return this.mainWindow().UmbClientMgr.rootScope();
}
},
openModalWindow: function(url, name, showHeader, width, height, top, leftOffset, closeTriggers, onCloseCallback) {
//need to create the modal on the top window if the top window has a client manager, if not, create it on the current window
@@ -69,6 +69,33 @@
});
},
openQueryModal: function () {
/// <summary>callback used to display the modal dialog to insert a macro with parameters</summary>
var self = this;
UmbClientMgr.openAngularModalWindow({
template: "views/common/dialogs/template/queryBuilder.html",
callback: function (data) {
//var dataFormatted = data.replace(new RegExp('[' + "." + ']', 'g'), "\n\t\t\t\t\t.");
var code = "\n@{\n" + "\tvar selection = " + data + ";\n}\n";
code += "<ul>\n" +
"\t@foreach(var item in selection){\n" +
"\t\t<li>\n" +
"\t\t\t<a href=\"@item.Url\">@item.Name</a>\n" +
"\t\t</li>\n" +
"\t}\n" +
"</ul>\n\n";
UmbEditor.Insert(code, '', self._opts.codeEditorElementId);
}
});
},
doSubmit: function () {
/// <summary>Submits the data to the server for saving</summary>
var codeVal = UmbClientMgr.contentFrame().UmbEditor.GetCode();
+2
View File
@@ -195,6 +195,8 @@
<mimeMap fileExtension=".svg" mimeType="image/svg+xml" />
<remove fileExtension=".woff"/>
<mimeMap fileExtension=".woff" mimeType="application/x-font-woff" />
<remove fileExtension=".less" />
<mimeMap fileExtension=".less" mimeType="text/css" />
</staticContent>
<!-- Ensure the powered by header is not returned -->
@@ -185,6 +185,10 @@ namespace Umbraco.Web.Editors
{
"stylesheetApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<StylesheetController>(
controller => controller.GetAll())
},
{
"templateApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<TemplateController>(
controller => controller.GetById(0))
},
{
"memberTypeApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<MemberTypeController>(
@@ -0,0 +1,117 @@
using AutoMapper;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;
using Umbraco.Core.Models;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Mvc;
using Umbraco.Web.WebApi;
using Umbraco.Web.WebApi.Filters;
namespace Umbraco.Web.Editors
{
[PluginController("UmbracoApi")]
[UmbracoApplicationAuthorize(Core.Constants.Applications.Settings)]
public class TemplateController : UmbracoAuthorizedJsonController
{
/// <summary>
/// Gets the content json for the content id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public TemplateDisplay GetById(int id)
{
var template = Services.FileService.GetTemplate(id);
if (template == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
TemplateDisplay t = new TemplateDisplay();
t.Alias = template.Alias;
t.Content = template.Content;
t.Id = template.Id;
t.Name = template.Name;
return t;
}
/// <summary>
/// Deletes a data type wth a given ID
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpDelete]
[HttpPost]
public HttpResponseMessage DeleteById(int id)
{
var foundTemplate = Services.FileService.GetTemplate(id);
if (foundTemplate == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
Services.FileService.DeleteTemplate(foundTemplate.Alias);
return Request.CreateResponse(HttpStatusCode.OK);
}
[HttpPost]
public HttpResponseMessage PostSaveAndRender(dynamic model)
{
var foundTemplate = Services.FileService.GetTemplate((int)model.templateId);
if (foundTemplate == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
foundTemplate.Content = model.html;
Services.FileService.SaveTemplate(foundTemplate);
string result = string.Empty;
try
{
var url = "http://" + Request.RequestUri.Host + "/" + model.pageId + ".aspx?altTemplate=" + foundTemplate.Alias;
result = url;
WebClient wc = new WebClient();
result = wc.DownloadString(new Uri(url));
}
catch (WebException exception)
{
if (exception.Response != null)
{
var responseStream = exception.Response.GetResponseStream();
if (responseStream != null)
{
using (var reader = new StreamReader(responseStream))
{
result = reader.ReadToEnd();
}
}
}
}
catch (Exception ex)
{
result += ex.ToString();
}
return new HttpResponseMessage()
{
Content = new StringContent(
result,
Encoding.UTF8,
"text/html"
)
};
}
}
}
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Umbraco.Web.Editors.TemplateQuery
{
public class ContentTypeModel
{
public string Alias { get; set; }
public string Name { get; set; }
}
}
@@ -0,0 +1,14 @@
namespace Umbraco.Web.Editors
{
public enum Operathor
{
Equals = 1,
NotEquals = 2,
Contains = 3,
NotContains = 4,
LessThan = 5,
LessThanEqualTo = 6,
GreaterThan = 7,
GreaterThanEqualTo = 8
}
}
@@ -0,0 +1,25 @@
using System.Collections.Generic;
namespace Umbraco.Web.Editors
{
public class OperathorTerm
{
public OperathorTerm()
{
Name = "is";
Operathor = Operathor.Equals;
AppliesTo = new [] { "string" };
}
public OperathorTerm(string name, Operathor operathor, IEnumerable<string> appliesTo)
{
Name = name;
Operathor = operathor;
AppliesTo = appliesTo;
}
public string Name { get; set; }
public Operathor Operathor { get; set; }
public IEnumerable<string> AppliesTo { get; set; }
}
}
@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Umbraco.Web.Editors.TemplateQuery
{
public class PropertyModel
{
public string Name { get; set; }
public string Alias { get; set; }
public string Type { get; set; }
}
}
@@ -0,0 +1,62 @@
namespace Umbraco.Web.Editors
{
using Umbraco.Web.Editors.TemplateQuery;
public class QueryCondition
{
public PropertyModel Property { get; set; }
public OperathorTerm Term { get; set; }
public string ConstraintValue { get; set; }
}
internal static class QueryConditionExtensions
{
private static string MakeBinaryOperation(this QueryCondition condition, string operand, int token)
{
return string.Format("{0}{1}@{2}", condition.Property.Name, operand, token);
}
public static string BuildCondition(this QueryCondition condition, int token)
{
var operand = string.Empty;
var value = string.Empty;
switch (condition.Term.Operathor)
{
case Operathor.Equals:
operand = " == ";
break;
case Operathor.NotEquals:
operand = " != ";
break;
case Operathor.GreaterThan:
operand = " > ";
break;
case Operathor.GreaterThanEqualTo:
operand = " >= ";
break;
case Operathor.LessThan:
operand = " < ";
break;
case Operathor.LessThanEqualTo:
operand = " <= ";
break;
case Operathor.Contains:
value = string.Format("{0}.Contains(@{1})", condition.Property.Name, token);
break;
case Operathor.NotContains:
value = string.Format("!{0}.Contains(@{1})", condition.Property.Name, token);
break;
default :
operand = " == ";
break;
}
return string.IsNullOrEmpty(value) ? condition.MakeBinaryOperation(operand, token) : value;
}
}
}
@@ -0,0 +1,15 @@
using System.Collections.Generic;
namespace Umbraco.Web.Editors
{
using Umbraco.Web.Editors.TemplateQuery;
public class QueryModel
{
public ContentTypeModel ContentType { get; set; }
public SourceModel Source { get; set; }
public IEnumerable<QueryCondition> Filters { get; set; }
public SortExpression Sort { get; set; }
public int Take { get; set; }
}
}
@@ -0,0 +1,15 @@
using System.Collections.Generic;
namespace Umbraco.Web.Editors
{
public class QueryResultModel
{
public string QueryExpression { get; set; }
public IEnumerable<TemplateQueryResult> SampleResults { get; set; }
public int ResultCount { get; set; }
public double ExecutionTime { get; set; }
public int Take { get; set; }
}
}
@@ -0,0 +1,11 @@
namespace Umbraco.Web.Editors
{
using Umbraco.Web.Editors.TemplateQuery;
public class SortExpression
{
public PropertyModel Property { get; set; }
public string Direction { get; set; }
}
}

Some files were not shown because too many files have changed in this diff Show More