Initial commit
This commit is contained in:
+14
@@ -0,0 +1,14 @@
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
Icon
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
Desktop.ini
|
||||
$RECYCLE.BIN/
|
||||
|
||||
dist/
|
||||
node_modules/
|
||||
@@ -0,0 +1,84 @@
|
||||
module.exports = function(grunt) {
|
||||
grunt.initConfig({
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
dest: 'dist',
|
||||
|
||||
watch: {
|
||||
less: {
|
||||
files: ['app/less/*.less', 'lib/**/*.less'],
|
||||
tasks: ['less:build'],
|
||||
options: {
|
||||
spawn: false,
|
||||
}
|
||||
},
|
||||
|
||||
js: {
|
||||
files: ['app/**/*.js', 'lib/**/*.js'],
|
||||
tasks: ['concat', 'jshint'],
|
||||
options: {
|
||||
spawn: false,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
jshint: {
|
||||
options: {
|
||||
jshintrc: '.jshintrc'
|
||||
},
|
||||
src: {
|
||||
src: ['app/**/*.js', 'lib/**/*.js']
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
copy: {
|
||||
main: {
|
||||
files: [
|
||||
{expand: true, src: ['app/package.manifest'], dest: '<%= dest %>', flatten: true},
|
||||
{expand: true, src: ['app/views/archetype.html'], dest: '<%= dest %>/views', flatten: true}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
less: {
|
||||
build: {
|
||||
options: {
|
||||
paths: ["app/less", "lib/less", "vendor"],
|
||||
},
|
||||
files: {
|
||||
'<%= dest %>/css/archetype.css': 'app/less/archetype.less',
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
concat: {
|
||||
options: {
|
||||
stripBanners: false
|
||||
},
|
||||
application: {
|
||||
src: [
|
||||
'app/controllers/archetype_controller.js',
|
||||
'app/directives/content_item.js'
|
||||
],
|
||||
dest: '<%= dest %>/js/archetype.js'
|
||||
}
|
||||
},
|
||||
|
||||
clean: ['<%= dest %>']
|
||||
|
||||
});
|
||||
|
||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||
grunt.loadNpmTasks('grunt-contrib-less');
|
||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||
grunt.loadNpmTasks('grunt-contrib-concat');
|
||||
grunt.loadNpmTasks('grunt-contrib-copy');
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||
|
||||
|
||||
grunt.registerTask('css:build', ['less']);
|
||||
grunt.registerTask('js:build', ['concat']);
|
||||
grunt.registerTask('default', ['clean', 'css:build', 'js:build', 'copy']);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
angular.module("umbraco").controller("Imulus.ArchetypeController", function ($scope, $http, assetsService) {
|
||||
|
||||
//$scope.model.value = "";
|
||||
|
||||
//test config/model
|
||||
//$scope.model.config.emptyFieldSetModel = '{ remove: false, properties:[{ type: "textbox", options: { label: "Name" }, data: "" }, { type: "contentPicker", options: { label: "Pick some content" }, data: "" }]}';
|
||||
|
||||
//set default value of the model
|
||||
//this works by checking to see if there is a model; then cascades to the default model then to an empty fieldset
|
||||
var validDefaultModel = getValidJson("$scope.model.config.defaultModel", $scope.model.config.defaultModel);
|
||||
var validEmptyFieldsetModel = getValidJson("$scope.model.config.emptyFieldsetModel", $scope.model.config.emptyFieldsetModel);
|
||||
|
||||
$scope.model.value = $scope.model.value || (validDefaultModel || { fieldsets: [validEmptyFieldsetModel] });
|
||||
|
||||
//ini
|
||||
$scope.archetypeRenderModel = {};
|
||||
initArchetypeRenderModel();
|
||||
|
||||
/* add/remove/sort */
|
||||
|
||||
//defines the options for the jquery sortable
|
||||
//i used an ng-model="archetypeRenderModel" so the sort updates the right model
|
||||
$scope.sortableOptions = {
|
||||
axis: 'y',
|
||||
cursor: "move",
|
||||
handle: ".handle",
|
||||
update: function (ev, ui) {
|
||||
|
||||
},
|
||||
stop: function (ev, ui) {
|
||||
console.log($scope.archetypeRenderModel);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.addRow = function ($index) {
|
||||
if (true)
|
||||
{
|
||||
var validJson = getValidJson("$scope.model.config.emptyFieldsetModel", $scope.model.config.emptyFieldsetModel);
|
||||
|
||||
if (validJson)
|
||||
{
|
||||
$scope.archetypeRenderModel.fieldsets.splice($index + 1, 0, validJson);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//rather than splice the archetypeRenderModel, we're hiding this and cleaning onFormSubmitting
|
||||
$scope.removeRow = function ($index) {
|
||||
if ($scope.canRemove()) {
|
||||
if (confirm('Are you sure you want to remove this?')) {
|
||||
$scope.archetypeRenderModel.fieldsets[$index].remove = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//helpers for determining if a user can do something
|
||||
$scope.canAdd = function ()
|
||||
{
|
||||
if ($scope.model.config.maxProperties)
|
||||
{
|
||||
return countVisible() < $scope.model.config.maxProperties;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$scope.canRemove = function ()
|
||||
{
|
||||
return countVisible() > 1;
|
||||
}
|
||||
|
||||
$scope.canSort = function ()
|
||||
{
|
||||
return countVisible() > 1;
|
||||
}
|
||||
|
||||
//helper, ini the render model from the server (model.value)
|
||||
function initArchetypeRenderModel() {
|
||||
$scope.archetypeRenderModel = $scope.model.value;
|
||||
}
|
||||
|
||||
//helper returns valid JS or null
|
||||
function getValidJson(variable, json)
|
||||
{
|
||||
if(!json) return null;
|
||||
|
||||
try {
|
||||
return eval("(" + json + ")");
|
||||
}
|
||||
catch (e) {
|
||||
console.log("There was an error while using 'eval' on " + variable);
|
||||
console.log(json);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
//developerMode helpers
|
||||
$scope.archetypeRenderModel.toString = stringify;
|
||||
|
||||
function stringify() {
|
||||
return JSON.stringify(this);
|
||||
}
|
||||
|
||||
//watch for changes
|
||||
$scope.$watch('archetypeRenderModel', function (v) {
|
||||
if ($scope.model.config.developerMode) {
|
||||
console.log(v);
|
||||
if (typeof v === 'string') {
|
||||
$scope.archetypeRenderModel = JSON.parse(v);
|
||||
$scope.archetypeRenderModel.toString = stringify;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//helper to count what is visible
|
||||
function countVisible()
|
||||
{
|
||||
var count = 0;
|
||||
|
||||
for (var i in $scope.archetypeRenderModel.fieldsets) {
|
||||
if ($scope.archetypeRenderModel.fieldsets[i].remove == false) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
//helper to sync the model to the renderModel
|
||||
function syncModelToRenderModel()
|
||||
{
|
||||
$scope.model.value = { fieldsets: [] };
|
||||
|
||||
for (var i in $scope.archetypeRenderModel.fieldsets) {
|
||||
if (!$scope.archetypeRenderModel.fieldsets[i].remove) {
|
||||
$scope.model.value.fieldsets.push($scope.archetypeRenderModel.fieldsets[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//sync things up on save
|
||||
$scope.$on("formSubmitting", function (ev, args) {
|
||||
syncModelToRenderModel();
|
||||
});
|
||||
|
||||
//custom js
|
||||
if ($scope.model.config.customJsPath) {
|
||||
assetsService.loadJs($scope.model.config.customJsPath);
|
||||
}
|
||||
|
||||
//archetype css
|
||||
assetsService.loadCss("/App_Plugins/Archetype/css/archetype.css");
|
||||
|
||||
//custom css
|
||||
if($scope.model.config.customCssPath)
|
||||
{
|
||||
assetsService.loadCss($scope.model.config.customCssPath);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
angular.module("umbraco").directive('contentItem', function ($compile, $http) {
|
||||
|
||||
var linker = function (scope, element, attrs) {
|
||||
|
||||
if (scope.content.view)
|
||||
{
|
||||
$http.get(scope.content.view).success(function (data) {
|
||||
if (data) {
|
||||
var rawTemplate = data;
|
||||
|
||||
//define the initial model and config
|
||||
scope.model = {};
|
||||
scope.model.config = {};
|
||||
|
||||
//pull these from the content
|
||||
scope.model.value = scope.content.value;
|
||||
scope.model.config = scope.content.config;
|
||||
|
||||
//some items need an alias
|
||||
scope.model.alias = "scope-" + scope.$id;
|
||||
|
||||
//watch for changes since there is no two-way binding with the child values
|
||||
scope.$watch('model.value', function (newValue, oldValue) {
|
||||
scope.content.value = newValue;
|
||||
});
|
||||
|
||||
//add label
|
||||
if (true) {
|
||||
rawTemplate = '<label>{{content.options.label}}</label>' + rawTemplate;
|
||||
}
|
||||
|
||||
element.html(rawTemplate).show();
|
||||
$compile(element.contents())(scope);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
restrict: "E",
|
||||
rep1ace: true,
|
||||
link: linker,
|
||||
scope: {
|
||||
content: '='
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
.archetypeEditor fieldset{
|
||||
border: 1px solid #666;
|
||||
padding: 5px;
|
||||
margin-bottom: 5px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.archetypeEditor label{
|
||||
width: 75px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.archetypeEditor .multiPropertyTextbox{
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.archetypeEditor ul{
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.archetypeEditor .archetypeEditorControls{
|
||||
float: right;
|
||||
}
|
||||
|
||||
.archetypeEditor .archetypeProperty{
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.archetypeEditor .umb-contentpicker{
|
||||
margin-left: 60px;
|
||||
padding-left: 25px;
|
||||
}
|
||||
|
||||
.archetypeEditor .archetypeDeveloperModel
|
||||
{
|
||||
width: 98%;
|
||||
min-height: 100px;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid red;
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
{
|
||||
propertyEditors: [
|
||||
{
|
||||
alias: "Archetype",
|
||||
name: "Archetype",
|
||||
editor: {
|
||||
view: "~/App_Plugins/Archetype/views/archetype.html",
|
||||
valueType: "JSON"
|
||||
},
|
||||
prevalues: {
|
||||
fields: [
|
||||
{
|
||||
label: "Empty Fieldset Model",
|
||||
description: "(Required) What would you like a new fieldset model to be?",
|
||||
key: "emptyFieldsetModel",
|
||||
view: "textarea",
|
||||
validation: [
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Hide editor controls?",
|
||||
description: "Hide the add/remove/sort controls",
|
||||
key: "hideControls",
|
||||
view: "boolean",
|
||||
validation: [
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Default Model",
|
||||
description: "(Optional) What would you the default model to be? Otherwise the model will defer to the Empty Fieldset Model for new items.",
|
||||
key: "defaultModel",
|
||||
view: "textarea",
|
||||
validation: [
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Max Fieldsets",
|
||||
description: "(Optional) How many Fieldsets are allowed? Entering '1' will disable the controls. Default is unlimited.",
|
||||
key: "maxProperties",
|
||||
view: "number",
|
||||
validation: [
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Custom Class",
|
||||
description: "(Optional) Enter a custom CSS class.",
|
||||
key: "customCssClass",
|
||||
view: "textstring",
|
||||
validation: [
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "CSS File",
|
||||
description: "(Optional) Enter a path for a custom CSS file to be included on the page.",
|
||||
key: "customCssPath",
|
||||
view: "textstring",
|
||||
validation: [
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "JS File",
|
||||
description: "(Optional) Enter a path for a custom JS file to be included on the page.",
|
||||
key: "customJsPath",
|
||||
view: "textstring",
|
||||
validation: [
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Developer Mode",
|
||||
description: "Turn on for developer options.",
|
||||
key: "developerMode",
|
||||
view: "boolean",
|
||||
validation: [
|
||||
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
,
|
||||
javascript: [
|
||||
'~/App_Plugins/Archetype/js/archetype.js'
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<div class="archetypeEditor ng-class:model.config.customCssClass" ng-controller="Imulus.ArchetypeController">
|
||||
<textarea class="archetypeDeveloperModel" ng-show="model.config.developerMode" ng-model="archetypeRenderModel"></textarea>
|
||||
<ul ui-sortable="sortableOptions" ng-model="archetypeRenderModel.fieldsets">
|
||||
<li ng-repeat="fieldset in archetypeRenderModel.fieldsets" ng-hide="fieldset.remove">
|
||||
<fieldset>
|
||||
<div class="archetypeEditorControls" ng-hide="model.config.hideControls || model.config.maxProperties == '1'">
|
||||
<i class="icon icon-add" ng-click="addRow($index)" ng-show="canAdd()"></i>
|
||||
<i class="icon icon-delete" ng-click="removeRow($index)" ng-show="canRemove()"></i>
|
||||
<i class="icon icon-navigation handle" ng-show="canSort()"></i>
|
||||
</div>
|
||||
<div class="archetypeProperty" ng-repeat="property in fieldset.properties">
|
||||
<content-item content="property"></content-item>
|
||||
</div>
|
||||
</fieldset>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "archetype",
|
||||
"version": "0.0.0",
|
||||
"devDependencies": {
|
||||
"grunt": "~0.4.2",
|
||||
"bower": "~1.2.8",
|
||||
"grunt-contrib-copy": "~0.5.0",
|
||||
"grunt-contrib-less": "~0.8.3",
|
||||
"grunt-contrib-concat": "~0.3.0",
|
||||
"grunt-contrib-uglify": "~0.2.7",
|
||||
"grunt-contrib-watch": "~0.5.3",
|
||||
"grunt-contrib-jshint": "~0.7.2",
|
||||
"grunt-cli": "~0.1.11",
|
||||
"grunt-contrib-clean": "~0.5.0"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user