Files
Archetype/app/controllers/controller.js
T

443 lines
14 KiB
JavaScript

angular.module("umbraco").controller("Imulus.ArchetypeController", function ($scope, $http, assetsService, angularHelper, notificationsService, $timeout, entityResource) {
//$scope.model.value = "";
$scope.model.hideLabel = $scope.model.config.hideLabel == 1;
//get a reference to the current form
$scope.form = $scope.form || angularHelper.getCurrentForm($scope);
//set the config equal to our prevalue config
$scope.model.config = $scope.model.config.archetypeConfig;
//ini the model
$scope.model.value = $scope.model.value || getDefaultModel($scope.model.config);
// store the umbraco property alias to help generate unique IDs. Hopefully there's a better way to get this in the future :)
$scope.umbracoHostPropertyAlias = $scope.$parent.$parent.model.alias;
init();
//hold references to helper resources
$scope.resources = {
entityResource: entityResource
}
//helper to get $eval the labelTemplate
$scope.getFieldsetTitle = function(fieldsetConfigModel, fieldsetIndex) {
if(!fieldsetConfigModel)
return "";
var fieldset = $scope.model.value.fieldsets[fieldsetIndex];
var fieldsetConfig = $scope.getConfigFieldsetByAlias(fieldset.alias);
var template = fieldsetConfigModel.labelTemplate;
if (template.length < 1)
return fieldsetConfig.label;
var rgx = /{{([^)].*)}}/g;
var results;
var parsedTemplate = template;
while ((results = rgx.exec(template)) !== null) {
// split the template in case it consists of multiple property aliases and/or functions
var templates = results[0].replace("{{", '').replace("}}", '').split("|");
var templateLabelValue = "";
for(var i = 0; i < templates.length; i++) {
// stop looking for a template label value if a previous template part already yielded a value
if(templateLabelValue != "") {
break;
}
var template = templates[i];
//test for function
var beginParamsIndexOf = template.indexOf("(");
var endParamsIndexOf = template.indexOf(")");
if(beginParamsIndexOf != -1 && endParamsIndexOf != -1)
{
var functionName = template.substring(0, beginParamsIndexOf);
var propertyAlias = template.substring(beginParamsIndexOf + 1, endParamsIndexOf).split(',')[0];
var args = {};
var beginArgsIndexOf = template.indexOf(',');
if(beginArgsIndexOf != -1) {
var argsString = template.substring(beginArgsIndexOf + 1, endParamsIndexOf).trim();
var normalizedJsonString = argsString.replace(/(\w+)\s*:/g, '"$1":');
args = JSON.parse(normalizedJsonString);
}
templateLabelValue = executeFunctionByName(functionName, window, $scope.getPropertyValueByAlias(fieldset, propertyAlias), $scope, args);
}
else {
propertyAlias = template;
templateLabelValue = $scope.getPropertyValueByAlias(fieldset, propertyAlias);
}
}
parsedTemplate = parsedTemplate.replace(results[0], templateLabelValue);
}
return parsedTemplate;
};
function executeFunctionByName(functionName, context) {
var args = Array.prototype.slice.call(arguments).splice(2);
var namespaces = functionName.split(".");
var func = namespaces.pop();
for(var i = 0; i < namespaces.length; i++) {
context = context[namespaces[i]];
}
if(context && context[func]) {
return context[func].apply(this, args);
}
return "";
}
var draggedRteSettings;
//sort config
$scope.sortableOptions = {
axis: 'y',
cursor: "move",
handle: ".handle",
start: function(ev, ui) {
draggedRteSettings = {};
ui.item.parent().find('.mceNoEditor').each(function () {
// remove all RTEs in the dragged row and save their settings
var id = $(this).attr('id');
draggedRteSettings[id] = _.findWhere(tinyMCE.editors, { id: id }).settings;
tinyMCE.execCommand('mceRemoveEditor', false, id);
});
},
update: function (ev, ui) {
$scope.setDirty();
},
stop: function (ev, ui) {
ui.item.parent().find('.mceNoEditor').each(function () {
var id = $(this).attr('id');
draggedRteSettings[id] = draggedRteSettings[id] || _.findWhere(tinyMCE.editors, { id: id }).settings;
tinyMCE.execCommand('mceRemoveEditor', false, id);
tinyMCE.init(draggedRteSettings[id]);
});
}
};
//handles a fieldset add
$scope.addRow = function (fieldsetAlias, $index) {
if ($scope.canAdd()) {
if ($scope.model.config.fieldsets) {
var newFieldset = getEmptyRenderFieldset($scope.getConfigFieldsetByAlias(fieldsetAlias));
if (typeof $index != 'undefined')
{
$scope.model.value.fieldsets.splice($index + 1, 0, newFieldset);
}
else
{
$scope.model.value.fieldsets.push(newFieldset);
}
}
$scope.setDirty();
newFieldset.collapse = $scope.model.config.enableCollapsing ? true : false;
$scope.focusFieldset(newFieldset);
}
}
$scope.removeRow = function ($index) {
if ($scope.canRemove()) {
if (confirm('Are you sure you want to remove this?')) {
$scope.setDirty();
$scope.model.value.fieldsets.splice($index, 1);
$scope.$broadcast("archetypeRemoveFieldset", {index: $index});
}
}
}
$scope.cloneRow = function ($index) {
if ($scope.canClone() && typeof $index != 'undefined') {
var newFieldset = angular.copy($scope.model.value.fieldsets[$index]);
if(newFieldset) {
$scope.model.value.fieldsets.splice($index + 1, 0, newFieldset);
$scope.setDirty();
newFieldset.collapse = $scope.model.config.enableCollapsing ? true : false;
$scope.focusFieldset(newFieldset);
}
}
}
$scope.enableDisable = function (fieldset) {
fieldset.disabled = !fieldset.disabled;
// explicitly set the form as dirty when manipulating the enabled/disabled state of a fieldset
$scope.setDirty();
}
//helpers for determining if a user can do something
$scope.canAdd = function () {
if ($scope.model.config.maxFieldsets)
{
return countVisible() < $scope.model.config.maxFieldsets;
}
return true;
}
//helper that returns if an item can be removed
$scope.canRemove = function () {
return countVisible() > 1
|| ($scope.model.config.maxFieldsets == 1 && $scope.model.config.fieldsets.length > 1)
|| $scope.model.config.startWithAddButton;
}
$scope.canClone = function () {
if (!$scope.model.config.enableCloning) {
return false;
}
if ($scope.model.config.maxFieldsets)
{
return countVisible() < $scope.model.config.maxFieldsets;
}
return true;
}
//helper that returns if an item can be sorted
$scope.canSort = function ()
{
return countVisible() > 1;
}
//helper that returns if an item can be disabled
$scope.canDisable = function () {
return $scope.model.config.enableDisabling;
}
//helpers for determining if the add button should be shown
$scope.showAddButton = function () {
return $scope.model.config.startWithAddButton
&& countVisible() === 0;
///&& $scope.model.config.fieldsets.length == 1;
}
//helper, ini the render model from the server (model.value)
function init() {
$scope.model.value = removeNulls($scope.model.value);
addDefaultProperties($scope.model.value.fieldsets);
}
function addDefaultProperties(fieldsets)
{
_.each(fieldsets, function (fieldset)
{
fieldset.collapse = false;
fieldset.isValid = true;
});
}
//helper to get the correct fieldset from config
$scope.getConfigFieldsetByAlias = function(alias) {
return _.find($scope.model.config.fieldsets, function(fieldset){
return fieldset.alias == alias;
});
}
//helper to get a property by alias from a fieldset
$scope.getPropertyValueByAlias = function(fieldset, propertyAlias) {
var property = _.find(fieldset.properties, function(p) {
return p.alias == propertyAlias;
});
return (typeof property !== 'undefined') ? property.value : '';
};
$scope.isCollapsed = function(fieldset)
{
if(typeof fieldset.collapse === "undefined")
{
fieldset.collapse = true;
}
return fieldset.collapse;
}
//helper for expanding/collapsing fieldsets
$scope.focusFieldset = function(fieldset){
fixDisableSelection();
if (!$scope.model.config.enableCollapsing) {
return;
}
var iniState;
if(fieldset)
{
iniState = fieldset.collapse;
}
_.each($scope.model.value.fieldsets, function(fieldset){
fieldset.collapse = true;
});
if(!fieldset && $scope.model.value.fieldsets.length == 1)
{
$scope.model.value.fieldsets[0].collapse = false;
return;
}
if(iniState && fieldset)
{
fieldset.collapse = !iniState;
}
}
//ini the fieldset expand/collapse
$scope.focusFieldset();
//developerMode helpers
$scope.model.value.toString = stringify;
//encapsulate stringify (should be built into browsers, not sure of IE support)
function stringify() {
return JSON.stringify(this);
}
//watch for changes
$scope.$watch('model.value', function (v) {
if ($scope.model.config.developerMode) {
console.log(v);
if (typeof v === 'string') {
$scope.model.value = JSON.parse(v);
$scope.model.value.toString = stringify;
}
}
// reset submit watcher counter on save
$scope.activeSubmitWatcher = 0;
});
//helper to count what is visible
function countVisible()
{
return $scope.model.value.fieldsets.length;
}
// helper to get initial model if none was provided
function getDefaultModel(config) {
if (config.startWithAddButton)
return { fieldsets: [] };
return { fieldsets: [getEmptyRenderFieldset(config.fieldsets[0])] };
}
//helper to add an empty fieldset to the render model
function getEmptyRenderFieldset (fieldsetModel) {
return {alias: fieldsetModel.alias, collapse: false, isValid: true, properties: []};
}
//helper to ensure no nulls make it into the model
function removeNulls(model){
if(model.fieldsets){
_.each(model.fieldsets, function(fieldset, index){
if(!fieldset){
model.fieldsets.splice(index, 1);
removeNulls(model);
}
});
return model;
}
}
// Hack for U4-4281 / #61
function fixDisableSelection() {
$timeout(function() {
$('.archetypeEditor .controls')
.bind('mousedown.ui-disableSelection selectstart.ui-disableSelection', function(e) {
e.stopImmediatePropagation();
});
}, 1000);
}
//helper to lookup validity when given a fieldsetIndex and property alias
$scope.getPropertyValidity = function(fieldsetIndex, alias)
{
if($scope.model.value.fieldsets[fieldsetIndex])
{
var property = _.find($scope.model.value.fieldsets[fieldsetIndex].properties, function(property){
return property.alias == alias;
});
}
return (typeof property == 'undefined') ? true : property.isValid;
}
//helper to lookup validity when given a fieldset
$scope.getFieldsetValidity = function (fieldset) {
if (fieldset.isValid == false) {
return false;
}
// recursive validation of nested fieldsets
var nestedFieldsetsValid = true;
_.each(fieldset.properties, function (property) {
if (property != null && property.value != null && property.propertyEditorAlias == "Imulus.Archetype") {
_.each(property.value.fieldsets, function (inner) {
if ($scope.getFieldsetValidity(inner) == false) {
nestedFieldsetsValid = false;
}
});
}
});
return nestedFieldsetsValid;
}
// helper to force the current form into the dirty state
$scope.setDirty = function () {
if($scope.form) {
$scope.form.$setDirty();
}
}
//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);
}
// submit watcher handling:
// because some property editors use the "formSubmitting" event to set/clean up their model.value,
// we need to monitor the "formSubmitting" event from a custom property and broadcast our own event
// to forcefully update the appropriate model.value's
$scope.activeSubmitWatcher = 0;
$scope.submitWatcherOnLoad = function () {
$scope.activeSubmitWatcher++;
return $scope.activeSubmitWatcher;
}
$scope.submitWatcherOnSubmit = function () {
$scope.$broadcast("archetypeFormSubmitting");
}
});