Initial commit

This commit is contained in:
Tom Fulton
2014-01-10 17:20:35 -07:00
commit b524121f8a
8 changed files with 469 additions and 0 deletions
+14
View File
@@ -0,0 +1,14 @@
.DS_Store
.AppleDouble
.LSOverride
Icon
._*
.Spotlight-V100
.Trashes
Thumbs.db
ehthumbs.db
Desktop.ini
$RECYCLE.BIN/
dist/
node_modules/
+84
View File
@@ -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']);
};
+159
View File
@@ -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);
}
});
+47
View File
@@ -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: '='
}
}
});
+40
View File
@@ -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;
}
+92
View File
@@ -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'
]
}
+17
View File
@@ -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>
+16
View File
@@ -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"
}
}