You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
422 lines
15 KiB
422 lines
15 KiB
8 months ago
|
/*
|
||
|
* Activiti Modeler component part of the Activiti project
|
||
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
|
||
|
*
|
||
|
* This library is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU Lesser General Public
|
||
|
* License as published by the Free Software Foundation; either
|
||
|
* version 2.1 of the License, or (at your option) any later version.
|
||
|
*
|
||
|
* This library is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
* Lesser General Public License for more details.
|
||
|
|
||
|
* You should have received a copy of the GNU Lesser General Public
|
||
|
* License along with this library; if not, write to the Free Software
|
||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||
|
*/
|
||
|
'use strict'
|
||
|
|
||
|
var activitiModeler = angular.module('activitiModeler', [
|
||
|
'ngCookies',
|
||
|
'ngResource',
|
||
|
'ngSanitize',
|
||
|
'ngRoute',
|
||
|
'ngDragDrop',
|
||
|
'mgcrea.ngStrap',
|
||
|
'ngGrid',
|
||
|
'ngAnimate',
|
||
|
'pascalprecht.translate',
|
||
|
'duScroll'
|
||
|
])
|
||
|
|
||
|
var activitiModule = activitiModeler
|
||
|
|
||
|
activitiModeler
|
||
|
// Initialize routes
|
||
|
.config(['$selectProvider', '$translateProvider', function ($selectProvider, $translateProvider) {
|
||
|
// Override caret for bs-select directive
|
||
|
angular.extend($selectProvider.defaults, {
|
||
|
caretHtml: ' <i class="icon icon-caret-down"></i>'
|
||
|
})
|
||
|
|
||
|
// Initialize angular-translate
|
||
|
$translateProvider.useStaticFilesLoader({
|
||
|
prefix: './editor-app/i18n/',
|
||
|
suffix: '.json'
|
||
|
})
|
||
|
|
||
|
$translateProvider.preferredLanguage('zh-CN')
|
||
|
|
||
|
// remember language
|
||
|
$translateProvider.useCookieStorage()
|
||
|
}])
|
||
|
.run(['$rootScope', '$timeout', '$modal', '$translate', '$location', '$window', '$http', '$q',
|
||
|
function ($rootScope, $timeout, $modal, $translate, $location, $window, $http, $q) {
|
||
|
$rootScope.config = ACTIVITI.CONFIG
|
||
|
|
||
|
$rootScope.editorInitialized = false
|
||
|
|
||
|
$rootScope.editorFactory = $q.defer()
|
||
|
|
||
|
$rootScope.forceSelectionRefresh = false
|
||
|
|
||
|
$rootScope.ignoreChanges = false // by default never ignore changes
|
||
|
|
||
|
$rootScope.validationErrors = []
|
||
|
|
||
|
$rootScope.staticIncludeVersion = Date.now()
|
||
|
|
||
|
/**
|
||
|
* A 'safer' apply that avoids concurrent updates (which $apply allows).
|
||
|
*/
|
||
|
$rootScope.safeApply = function (fn) {
|
||
|
var phase = this.$root.$$phase
|
||
|
if (phase == '$apply' || phase == '$digest') {
|
||
|
if (fn && (typeof (fn) === 'function')) {
|
||
|
fn()
|
||
|
}
|
||
|
} else {
|
||
|
this.$apply(fn)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initialize the event bus: couple all Oryx events with a dispatch of the
|
||
|
* event of the event bus. This way, it gets much easier to attach custom logic
|
||
|
* to any event.
|
||
|
*/
|
||
|
|
||
|
/* Helper method to fetch model from server (always needed) */
|
||
|
function fetchModel (modelId) {
|
||
|
var modelUrl = KISBPM.URL.getModel(modelId)
|
||
|
var headers = {}
|
||
|
var token = EDITOR.UTIL.getParameterByName('token')
|
||
|
token = token && token.replace(/^"|"$/g, '')
|
||
|
headers[$rootScope.config.token] = token
|
||
|
$http({
|
||
|
method: 'GET',
|
||
|
url: modelUrl,
|
||
|
headers: headers
|
||
|
})
|
||
|
.success(function ({ data }, status, headers, config) {
|
||
|
$rootScope.editor = new ORYX.Editor(data)
|
||
|
$rootScope.modelData = angular.fromJson(data)
|
||
|
$rootScope.editorFactory.resolve()
|
||
|
})
|
||
|
.error(function (data, status, headers, config) {
|
||
|
console.log('Error loading model with id ' + modelId + ' ' + data)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
function initScrollHandling () {
|
||
|
var canvasSection = jQuery('#canvasSection')
|
||
|
canvasSection.scroll(function () {
|
||
|
// Hides the resizer and quick menu items during scrolling
|
||
|
|
||
|
var selectedElements = $rootScope.editor.selection
|
||
|
var subSelectionElements = $rootScope.editor._subSelection
|
||
|
|
||
|
$rootScope.selectedElements = selectedElements
|
||
|
$rootScope.subSelectionElements = subSelectionElements
|
||
|
if (selectedElements && selectedElements.length > 0) {
|
||
|
$rootScope.selectedElementBeforeScrolling = selectedElements[0]
|
||
|
}
|
||
|
|
||
|
jQuery('.Oryx_button').each(function (i, obj) {
|
||
|
$rootScope.orginalOryxButtonStyle = obj.style.display
|
||
|
obj.style.display = 'none'
|
||
|
})
|
||
|
|
||
|
jQuery('.resizer_southeast').each(function (i, obj) {
|
||
|
$rootScope.orginalResizerSEStyle = obj.style.display
|
||
|
obj.style.display = 'none'
|
||
|
})
|
||
|
jQuery('.resizer_northwest').each(function (i, obj) {
|
||
|
$rootScope.orginalResizerNWStyle = obj.style.display
|
||
|
obj.style.display = 'none'
|
||
|
})
|
||
|
$rootScope.editor.handleEvents({ type: ORYX.CONFIG.EVENT_CANVAS_SCROLL })
|
||
|
})
|
||
|
|
||
|
canvasSection.scrollStopped(function () {
|
||
|
// Puts the quick menu items and resizer back when scroll is stopped.
|
||
|
|
||
|
$rootScope.editor.setSelection([]) // needed cause it checks for element changes and does nothing if the elements are the same
|
||
|
$rootScope.editor.setSelection($rootScope.selectedElements, $rootScope.subSelectionElements)
|
||
|
$rootScope.selectedElements = undefined
|
||
|
$rootScope.subSelectionElements = undefined
|
||
|
|
||
|
function handleDisplayProperty (obj) {
|
||
|
if (jQuery(obj).position().top > 0) {
|
||
|
obj.style.display = 'block'
|
||
|
} else {
|
||
|
obj.style.display = 'none'
|
||
|
}
|
||
|
}
|
||
|
|
||
|
jQuery('.Oryx_button').each(function (i, obj) {
|
||
|
handleDisplayProperty(obj)
|
||
|
})
|
||
|
|
||
|
jQuery('.resizer_southeast').each(function (i, obj) {
|
||
|
handleDisplayProperty(obj)
|
||
|
})
|
||
|
jQuery('.resizer_northwest').each(function (i, obj) {
|
||
|
handleDisplayProperty(obj)
|
||
|
})
|
||
|
})
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initialize the Oryx Editor when the content has been loaded
|
||
|
*/
|
||
|
$rootScope.$on('$includeContentLoaded', function (event) {
|
||
|
if (!$rootScope.editorInitialized) {
|
||
|
ORYX._loadPlugins()
|
||
|
|
||
|
var modelId = EDITOR.UTIL.getParameterByName('modelId')
|
||
|
fetchModel(modelId)
|
||
|
|
||
|
$rootScope.window = {}
|
||
|
var updateWindowSize = function () {
|
||
|
$rootScope.window.width = $window.innerWidth
|
||
|
$rootScope.window.height = $window.innerHeight
|
||
|
}
|
||
|
|
||
|
// Window resize hook
|
||
|
angular.element($window).bind('resize', function () {
|
||
|
$rootScope.safeApply(updateWindowSize())
|
||
|
})
|
||
|
|
||
|
$rootScope.$watch('window.forceRefresh', function (newValue) {
|
||
|
if (newValue) {
|
||
|
$timeout(function () {
|
||
|
updateWindowSize()
|
||
|
$rootScope.window.forceRefresh = false
|
||
|
})
|
||
|
}
|
||
|
})
|
||
|
|
||
|
updateWindowSize()
|
||
|
|
||
|
// Hook in resizing of main panels when window resizes
|
||
|
// TODO: perhaps move to a separate JS-file?
|
||
|
jQuery(window).resize(function () {
|
||
|
// Calculate the offset based on the bottom of the module header
|
||
|
var offset = jQuery('#editor-header').offset()
|
||
|
var propSectionHeight = jQuery('#propertySection').height()
|
||
|
var canvas = jQuery('#canvasSection')
|
||
|
|
||
|
if (offset == undefined || offset === null ||
|
||
|
propSectionHeight === undefined || propSectionHeight === null ||
|
||
|
canvas === undefined || canvas === null) {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if ($rootScope.editor) {
|
||
|
var selectedElements = $rootScope.editor.selection
|
||
|
var subSelectionElements = $rootScope.editor._subSelection
|
||
|
|
||
|
$rootScope.selectedElements = selectedElements
|
||
|
$rootScope.subSelectionElements = subSelectionElements
|
||
|
if (selectedElements && selectedElements.length > 0) {
|
||
|
$rootScope.selectedElementBeforeScrolling = selectedElements[0]
|
||
|
|
||
|
$rootScope.editor.setSelection([]) // needed cause it checks for element changes and does nothing if the elements are the same
|
||
|
$rootScope.editor.setSelection($rootScope.selectedElements, $rootScope.subSelectionElements)
|
||
|
$rootScope.selectedElements = undefined
|
||
|
$rootScope.subSelectionElements = undefined
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var totalAvailable = jQuery(window).height() - offset.top - 40 - 21
|
||
|
canvas.height(totalAvailable - propSectionHeight)
|
||
|
jQuery('#paletteSection').height(totalAvailable)
|
||
|
|
||
|
// Update positions of the resize-markers, according to the canvas
|
||
|
|
||
|
var actualCanvas = null
|
||
|
if (canvas && canvas[0].children[1]) {
|
||
|
actualCanvas = canvas[0].children[1]
|
||
|
}
|
||
|
|
||
|
var canvasTop = canvas.position().top
|
||
|
var canvasLeft = canvas.position().left
|
||
|
var canvasHeight = canvas[0].clientHeight
|
||
|
var canvasWidth = canvas[0].clientWidth
|
||
|
var iconCenterOffset = 8
|
||
|
var widthDiff = 0
|
||
|
|
||
|
var actualWidth = 0
|
||
|
if (actualCanvas) {
|
||
|
// In some browsers, the SVG-element clientwidth isn't available, so we revert to the parent
|
||
|
actualWidth = actualCanvas.clientWidth || actualCanvas.parentNode.clientWidth
|
||
|
}
|
||
|
|
||
|
if (actualWidth < canvas[0].clientWidth) {
|
||
|
widthDiff = actualWidth - canvas[0].clientWidth
|
||
|
// In case the canvas is smaller than the actual viewport, the resizers should be moved
|
||
|
canvasLeft -= widthDiff / 2
|
||
|
canvasWidth += widthDiff
|
||
|
}
|
||
|
|
||
|
var iconWidth = 17
|
||
|
var iconOffset = 20
|
||
|
|
||
|
var north = jQuery('#canvas-grow-N')
|
||
|
north.css('top', canvasTop + iconOffset + 'px')
|
||
|
north.css('left', canvasLeft - 10 + (canvasWidth - iconWidth) / 2 + 'px')
|
||
|
|
||
|
var south = jQuery('#canvas-grow-S')
|
||
|
south.css('top', (canvasTop + canvasHeight - iconOffset - iconCenterOffset) + 'px')
|
||
|
south.css('left', canvasLeft - 10 + (canvasWidth - iconWidth) / 2 + 'px')
|
||
|
|
||
|
var east = jQuery('#canvas-grow-E')
|
||
|
east.css('top', canvasTop - 10 + (canvasHeight - iconWidth) / 2 + 'px')
|
||
|
east.css('left', (canvasLeft + canvasWidth - iconOffset - iconCenterOffset) + 'px')
|
||
|
|
||
|
var west = jQuery('#canvas-grow-W')
|
||
|
west.css('top', canvasTop - 10 + (canvasHeight - iconWidth) / 2 + 'px')
|
||
|
west.css('left', canvasLeft + iconOffset + 'px')
|
||
|
|
||
|
north = jQuery('#canvas-shrink-N')
|
||
|
north.css('top', canvasTop + iconOffset + 'px')
|
||
|
north.css('left', canvasLeft + 10 + (canvasWidth - iconWidth) / 2 + 'px')
|
||
|
|
||
|
south = jQuery('#canvas-shrink-S')
|
||
|
south.css('top', (canvasTop + canvasHeight - iconOffset - iconCenterOffset) + 'px')
|
||
|
south.css('left', canvasLeft + 10 + (canvasWidth - iconWidth) / 2 + 'px')
|
||
|
|
||
|
east = jQuery('#canvas-shrink-E')
|
||
|
east.css('top', canvasTop + 10 + (canvasHeight - iconWidth) / 2 + 'px')
|
||
|
east.css('left', (canvasLeft + canvasWidth - iconOffset - iconCenterOffset) + 'px')
|
||
|
|
||
|
west = jQuery('#canvas-shrink-W')
|
||
|
west.css('top', canvasTop + 10 + (canvasHeight - iconWidth) / 2 + 'px')
|
||
|
west.css('left', canvasLeft + iconOffset + 'px')
|
||
|
})
|
||
|
|
||
|
jQuery(window).trigger('resize')
|
||
|
|
||
|
jQuery.fn.scrollStopped = function (callback) {
|
||
|
jQuery(this).scroll(function () {
|
||
|
var self = this
|
||
|
var $this = jQuery(self)
|
||
|
if ($this.data('scrollTimeout')) {
|
||
|
clearTimeout($this.data('scrollTimeout'))
|
||
|
}
|
||
|
$this.data('scrollTimeout', setTimeout(callback, 50, self))
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// Always needed, cause the DOM element on which the scroll event listeners are attached are changed for every new model
|
||
|
initScrollHandling()
|
||
|
|
||
|
$rootScope.editorInitialized = true
|
||
|
}
|
||
|
})
|
||
|
|
||
|
/**
|
||
|
* Initialize the event bus: couple all Oryx events with a dispatch of the
|
||
|
* event of the event bus. This way, it gets much easier to attach custom logic
|
||
|
* to any event.
|
||
|
*/
|
||
|
|
||
|
$rootScope.editorFactory.promise.then(function () {
|
||
|
KISBPM.eventBus.editor = $rootScope.editor
|
||
|
|
||
|
var eventMappings = [
|
||
|
{ oryxType: ORYX.CONFIG.EVENT_SELECTION_CHANGED, kisBpmType: KISBPM.eventBus.EVENT_TYPE_SELECTION_CHANGE },
|
||
|
{ oryxType: ORYX.CONFIG.EVENT_DBLCLICK, kisBpmType: KISBPM.eventBus.EVENT_TYPE_DOUBLE_CLICK },
|
||
|
{ oryxType: ORYX.CONFIG.EVENT_MOUSEOUT, kisBpmType: KISBPM.eventBus.EVENT_TYPE_MOUSE_OUT },
|
||
|
{ oryxType: ORYX.CONFIG.EVENT_MOUSEOVER, kisBpmType: KISBPM.eventBus.EVENT_TYPE_MOUSE_OVER }
|
||
|
|
||
|
]
|
||
|
|
||
|
eventMappings.forEach(function (eventMapping) {
|
||
|
$rootScope.editor.registerOnEvent(eventMapping.oryxType, function (event) {
|
||
|
KISBPM.eventBus.dispatch(eventMapping.kisBpmType, event)
|
||
|
})
|
||
|
})
|
||
|
|
||
|
$rootScope.editor.registerOnEvent(ORYX.CONFIG.EVENT_SHAPEREMOVED, function (event) {
|
||
|
var validateButton = document.getElementById(event.shape.resourceId + '-validate-button')
|
||
|
if (validateButton) {
|
||
|
validateButton.style.display = 'none'
|
||
|
}
|
||
|
})
|
||
|
|
||
|
// The Oryx canvas is ready (we know since we're in this promise callback) and the
|
||
|
// event bus is ready. The editor is now ready for use
|
||
|
KISBPM.eventBus.dispatch(KISBPM.eventBus.EVENT_TYPE_EDITOR_READY, { type: KISBPM.eventBus.EVENT_TYPE_EDITOR_READY })
|
||
|
})
|
||
|
|
||
|
// Alerts
|
||
|
$rootScope.alerts = {
|
||
|
queue: []
|
||
|
}
|
||
|
|
||
|
$rootScope.showAlert = function (alert) {
|
||
|
if (alert.queue.length > 0) {
|
||
|
alert.current = alert.queue.shift()
|
||
|
// Start timout for message-pruning
|
||
|
alert.timeout = $timeout(function () {
|
||
|
if (alert.queue.length == 0) {
|
||
|
alert.current = undefined
|
||
|
alert.timeout = undefined
|
||
|
} else {
|
||
|
$rootScope.showAlert(alert)
|
||
|
}
|
||
|
}, (alert.current.type == 'error' ? 5000 : 1000))
|
||
|
} else {
|
||
|
$rootScope.alerts.current = undefined
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$rootScope.addAlert = function (message, type) {
|
||
|
var newAlert = { message: message, type: type }
|
||
|
if (!$rootScope.alerts.timeout) {
|
||
|
// Timeout for message queue is not running, start one
|
||
|
$rootScope.alerts.queue.push(newAlert)
|
||
|
$rootScope.showAlert($rootScope.alerts)
|
||
|
} else {
|
||
|
$rootScope.alerts.queue.push(newAlert)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$rootScope.dismissAlert = function () {
|
||
|
if (!$rootScope.alerts.timeout) {
|
||
|
$rootScope.alerts.current = undefined
|
||
|
} else {
|
||
|
$timeout.cancel($rootScope.alerts.timeout)
|
||
|
$rootScope.alerts.timeout = undefined
|
||
|
$rootScope.showAlert($rootScope.alerts)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$rootScope.addAlertPromise = function (promise, type) {
|
||
|
if (promise) {
|
||
|
promise.then(function (data) {
|
||
|
$rootScope.addAlert(data, type)
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
])
|
||
|
|
||
|
// Moment-JS date-formatting filter
|
||
|
.filter('dateformat', function () {
|
||
|
return function (date, format) {
|
||
|
if (date) {
|
||
|
if (format) {
|
||
|
return moment(date).format(format)
|
||
|
} else {
|
||
|
return moment(date).calendar()
|
||
|
}
|
||
|
}
|
||
|
return ''
|
||
|
}
|
||
|
})
|