Reduce taiga weight
- improve uglify - remove jquery ui - add dragula - new autocompletestable
parent
0e3e30d48e
commit
dd0db6cd78
|
@ -601,6 +601,9 @@ i18nInit = (lang, $translate) ->
|
||||||
# i18n - moment.js
|
# i18n - moment.js
|
||||||
moment.locale(lang)
|
moment.locale(lang)
|
||||||
|
|
||||||
|
if (lang != 'en') # en is the default, the file doesn't exist
|
||||||
|
ljs.load "/#{window._version}/locales/moment-locales/" + lang + ".js"
|
||||||
|
|
||||||
# i18n - checksley.js
|
# i18n - checksley.js
|
||||||
messages = {
|
messages = {
|
||||||
defaultMessage: $translate.instant("COMMON.FORM_ERRORS.DEFAULT_MESSAGE")
|
defaultMessage: $translate.instant("COMMON.FORM_ERRORS.DEFAULT_MESSAGE")
|
||||||
|
|
|
@ -137,22 +137,31 @@ ProjectValuesDirective = ($log, $repo, $confirm, $location, animationFrame, $tra
|
||||||
itemEl = null
|
itemEl = null
|
||||||
tdom = $el.find(".sortable")
|
tdom = $el.find(".sortable")
|
||||||
|
|
||||||
tdom.sortable({
|
drake = dragula([tdom[0]], {
|
||||||
handle: ".row.table-main.visualization",
|
direction: 'vertical',
|
||||||
dropOnEmpty: true
|
copySortSource: false,
|
||||||
connectWith: ".project-values-body"
|
copy: false,
|
||||||
revert: 400
|
mirrorContainer: tdom[0],
|
||||||
axis: "y"
|
moves: (item) -> return $(item).is('div[tg-bind-scope]')
|
||||||
})
|
})
|
||||||
|
|
||||||
tdom.on "sortstop", (event, ui) ->
|
drake.on 'dragend', (item) ->
|
||||||
itemEl = ui.item
|
itemEl = $(item)
|
||||||
itemValue = itemEl.scope().value
|
itemValue = itemEl.scope().value
|
||||||
itemIndex = itemEl.index()
|
itemIndex = itemEl.index()
|
||||||
$scope.$broadcast("admin:project-values:move", itemValue, itemIndex)
|
$scope.$broadcast("admin:project-values:move", itemValue, itemIndex)
|
||||||
|
|
||||||
|
scroll = autoScroll(window, {
|
||||||
|
margin: 20,
|
||||||
|
pixels: 30,
|
||||||
|
scrollWhenOutside: true,
|
||||||
|
autoScroll: () ->
|
||||||
|
return this.down && drake.dragging;
|
||||||
|
})
|
||||||
|
|
||||||
$scope.$on "$destroy", ->
|
$scope.$on "$destroy", ->
|
||||||
$el.off()
|
$el.off()
|
||||||
|
drake.destroy()
|
||||||
|
|
||||||
## Value Link
|
## Value Link
|
||||||
|
|
||||||
|
|
|
@ -139,8 +139,9 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
||||||
@rootscope.$broadcast("filters:update")
|
@rootscope.$broadcast("filters:update")
|
||||||
|
|
||||||
@scope.$on("sprint:us:move", @.moveUs)
|
@scope.$on("sprint:us:move", @.moveUs)
|
||||||
@scope.$on("sprint:us:moved", @.loadSprints)
|
@scope.$on "sprint:us:moved", () =>
|
||||||
@scope.$on("sprint:us:moved", @.loadProjectStats)
|
@.loadSprints()
|
||||||
|
@.loadProjectStats()
|
||||||
|
|
||||||
@scope.$on("backlog:load-closed-sprints", @.loadClosedSprints)
|
@scope.$on("backlog:load-closed-sprints", @.loadClosedSprints)
|
||||||
@scope.$on("backlog:unload-closed-sprints", @.unloadClosedSprints)
|
@scope.$on("backlog:unload-closed-sprints", @.unloadClosedSprints)
|
||||||
|
@ -390,8 +391,7 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
||||||
# Persist in bulk all affected
|
# Persist in bulk all affected
|
||||||
# userstories with order change
|
# userstories with order change
|
||||||
@rs.userstories.bulkUpdateBacklogOrder(project, data).then =>
|
@rs.userstories.bulkUpdateBacklogOrder(project, data).then =>
|
||||||
for us in usList
|
@rootscope.$broadcast("sprint:us:moved")
|
||||||
@rootscope.$broadcast("sprint:us:moved", us, oldSprintId, newSprintId)
|
|
||||||
|
|
||||||
# For sprint
|
# For sprint
|
||||||
else
|
else
|
||||||
|
@ -402,8 +402,7 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
||||||
# Persist in bulk all affected
|
# Persist in bulk all affected
|
||||||
# userstories with order change
|
# userstories with order change
|
||||||
@rs.userstories.bulkUpdateSprintOrder(project, data).then =>
|
@rs.userstories.bulkUpdateSprintOrder(project, data).then =>
|
||||||
for us in usList
|
@rootscope.$broadcast("sprint:us:moved")
|
||||||
@rootscope.$broadcast("sprint:us:moved", us, oldSprintId, newSprintId)
|
|
||||||
|
|
||||||
return promise
|
return promise
|
||||||
|
|
||||||
|
@ -430,7 +429,7 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
||||||
items = @.resortUserStories(@scope.userstories, "backlog_order")
|
items = @.resortUserStories(@scope.userstories, "backlog_order")
|
||||||
data = @.prepareBulkUpdateData(items, "backlog_order")
|
data = @.prepareBulkUpdateData(items, "backlog_order")
|
||||||
return @rs.userstories.bulkUpdateBacklogOrder(us.project, data).then =>
|
return @rs.userstories.bulkUpdateBacklogOrder(us.project, data).then =>
|
||||||
@rootscope.$broadcast("sprint:us:moved", us, oldSprintId, newSprintId)
|
@rootscope.$broadcast("sprint:us:moved")
|
||||||
|
|
||||||
if movedFromClosedSprint
|
if movedFromClosedSprint
|
||||||
@rootscope.$broadcast("backlog:load-closed-sprints")
|
@rootscope.$broadcast("backlog:load-closed-sprints")
|
||||||
|
@ -443,8 +442,6 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
||||||
# From backlog to sprint
|
# From backlog to sprint
|
||||||
if oldSprintId == null
|
if oldSprintId == null
|
||||||
us.milestone = newSprintId for us in usList
|
us.milestone = newSprintId for us in usList
|
||||||
|
|
||||||
@scope.$apply =>
|
|
||||||
args = [newUsIndex, 0].concat(usList)
|
args = [newUsIndex, 0].concat(usList)
|
||||||
|
|
||||||
# Add moving us to sprint user stories list
|
# Add moving us to sprint user stories list
|
||||||
|
@ -470,21 +467,20 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
||||||
r = sprint.user_stories.indexOf(us)
|
r = sprint.user_stories.indexOf(us)
|
||||||
sprint.user_stories.splice(r, 1)
|
sprint.user_stories.splice(r, 1)
|
||||||
|
|
||||||
# Persist the milestone change of userstory
|
#Persist the milestone change of userstory
|
||||||
promises = _.map usList, (us) => @repo.save(us)
|
promises = _.map usList, (us) => @repo.save(us)
|
||||||
|
|
||||||
# Rehash userstories order field
|
#Rehash userstories order field
|
||||||
# and persist in bulk all changes.
|
#and persist in bulk all changes.
|
||||||
promise = @q.all(promises).then =>
|
promise = @q.all(promises).then =>
|
||||||
items = @.resortUserStories(newSprint.user_stories, "sprint_order")
|
items = @.resortUserStories(newSprint.user_stories, "sprint_order")
|
||||||
data = @.prepareBulkUpdateData(items, "sprint_order")
|
data = @.prepareBulkUpdateData(items, "sprint_order")
|
||||||
|
|
||||||
@rs.userstories.bulkUpdateSprintOrder(project, data).then (result) =>
|
@rs.userstories.bulkUpdateSprintOrder(project, data).then (result) =>
|
||||||
@rootscope.$broadcast("sprint:us:moved", us, oldSprintId, newSprintId)
|
@rootscope.$broadcast("sprint:us:moved")
|
||||||
|
|
||||||
@rs.userstories.bulkUpdateBacklogOrder(project, data).then =>
|
@rs.userstories.bulkUpdateBacklogOrder(project, data).then =>
|
||||||
for us in usList
|
@rootscope.$broadcast("sprint:us:moved")
|
||||||
@rootscope.$broadcast("sprint:us:moved", us, oldSprintId, newSprintId)
|
|
||||||
|
|
||||||
if movedToClosedSprint || movedFromClosedSprint
|
if movedToClosedSprint || movedFromClosedSprint
|
||||||
@scope.$broadcast("backlog:load-closed-sprints")
|
@scope.$broadcast("backlog:load-closed-sprints")
|
||||||
|
@ -680,7 +676,7 @@ BacklogDirective = ($repo, $rootscope, $translate) ->
|
||||||
return _.map(rowElements, (x) -> angular.element(x))
|
return _.map(rowElements, (x) -> angular.element(x))
|
||||||
|
|
||||||
$scope.$on("userstories:loaded", reloadDoomLine)
|
$scope.$on("userstories:loaded", reloadDoomLine)
|
||||||
$scope.$watch "stats", reloadDoomLine
|
$scope.$watch("stats", reloadDoomLine)
|
||||||
|
|
||||||
## Move to current sprint link
|
## Move to current sprint link
|
||||||
|
|
||||||
|
@ -835,8 +831,6 @@ BacklogDirective = ($repo, $rootscope, $translate) ->
|
||||||
linkFilters($scope, $el, $attrs, $ctrl)
|
linkFilters($scope, $el, $attrs, $ctrl)
|
||||||
linkDoomLine($scope, $el, $attrs, $ctrl)
|
linkDoomLine($scope, $el, $attrs, $ctrl)
|
||||||
|
|
||||||
$el.find(".backlog-table-body").disableSelection()
|
|
||||||
|
|
||||||
filters = $ctrl.getUrlFilters()
|
filters = $ctrl.getUrlFilters()
|
||||||
if filters.status ||
|
if filters.status ||
|
||||||
filters.tags ||
|
filters.tags ||
|
||||||
|
|
|
@ -38,187 +38,117 @@ module = angular.module("taigaBacklog")
|
||||||
#############################################################################
|
#############################################################################
|
||||||
|
|
||||||
deleteElement = (el) ->
|
deleteElement = (el) ->
|
||||||
el.scope().$destroy()
|
$(el).scope().$destroy()
|
||||||
el.off()
|
$(el).off()
|
||||||
el.remove()
|
$(el).remove()
|
||||||
|
|
||||||
BacklogSortableDirective = ($repo, $rs, $rootscope, $tgConfirm, $translate) ->
|
BacklogSortableDirective = ($repo, $rs, $rootscope, $tgConfirm, $translate) ->
|
||||||
# Notes about jquery bug:
|
|
||||||
# http://stackoverflow.com/questions/5791886/jquery-draggable-shows-
|
|
||||||
# helper-in-wrong-place-when-scrolled-down-page
|
|
||||||
|
|
||||||
link = ($scope, $el, $attrs) ->
|
link = ($scope, $el, $attrs) ->
|
||||||
getUsIndex = (us) =>
|
|
||||||
return $(us).index(".backlog-table-body .row")
|
|
||||||
|
|
||||||
bindOnce $scope, "project", (project) ->
|
bindOnce $scope, "project", (project) ->
|
||||||
# If the user has not enough permissions we don't enable the sortable
|
# If the user has not enough permissions we don't enable the sortable
|
||||||
if not (project.my_permissions.indexOf("modify_us") > -1)
|
if not (project.my_permissions.indexOf("modify_us") > -1)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
initIsBacklog = false
|
||||||
|
|
||||||
filterError = ->
|
filterError = ->
|
||||||
text = $translate.instant("BACKLOG.SORTABLE_FILTER_ERROR")
|
text = $translate.instant("BACKLOG.SORTABLE_FILTER_ERROR")
|
||||||
$tgConfirm.notify("error", text)
|
$tgConfirm.notify("error", text)
|
||||||
|
|
||||||
$el.sortable({
|
drake = dragula([$el[0], $('.empty-backlog')[0]], {
|
||||||
items: ".us-item-row",
|
copySortSource: false,
|
||||||
cancel: ".popover"
|
copy: false,
|
||||||
connectWith: ".sprint"
|
isContainer: (el) -> return el.classList.contains('sprint-table'),
|
||||||
dropOnEmpty: true
|
moves: (item) ->
|
||||||
placeholder: "row us-item-row us-item-drag sortable-placeholder"
|
if !$(item).hasClass('row')
|
||||||
scroll: true
|
return false
|
||||||
disableHorizontalScroll: true
|
|
||||||
# A consequence of length of backlog user story item
|
# it doesn't move is the filter is open
|
||||||
# the default tolerance ("intersection") not works properly.
|
parent = $(item).parent()
|
||||||
tolerance: "pointer"
|
initIsBacklog = parent.hasClass('backlog-table-body')
|
||||||
# Revert on backlog is disabled bacause it works bad. Something
|
|
||||||
# on the current taiga backlog structure or style makes jquery ui
|
if initIsBacklog && $el.hasClass("active-filters")
|
||||||
# works unexpectly (in some circumstances calculates wrong
|
filterError()
|
||||||
# position for revert).
|
return false
|
||||||
revert: false
|
|
||||||
start: () ->
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
drake.on 'drag', (item, container) ->
|
||||||
|
parent = $(item).parent()
|
||||||
|
initIsBacklog = parent.hasClass('backlog-table-body')
|
||||||
|
|
||||||
$(document.body).addClass("drag-active")
|
$(document.body).addClass("drag-active")
|
||||||
stop: () ->
|
|
||||||
|
isChecked = $(item).find("input[type='checkbox']").is(":checked")
|
||||||
|
|
||||||
|
window.dragMultiple.start(item, container)
|
||||||
|
|
||||||
|
drake.on 'cloned', (item) ->
|
||||||
|
$(item).addClass('backlog-us-mirror')
|
||||||
|
|
||||||
|
drake.on 'dragend', (item) ->
|
||||||
|
$('.doom-line').remove()
|
||||||
|
|
||||||
|
parent = $(item).parent()
|
||||||
|
isBacklog = parent.hasClass('backlog-table-body') || parent.hasClass('empty-backlog')
|
||||||
|
|
||||||
|
sameContainer = (initIsBacklog == isBacklog)
|
||||||
|
|
||||||
|
dragMultipleItems = window.dragMultiple.stop()
|
||||||
|
|
||||||
$(document.body).removeClass("drag-active")
|
$(document.body).removeClass("drag-active")
|
||||||
|
|
||||||
if $el.hasClass("active-filters")
|
items = $(item).parent().find('.row')
|
||||||
$el.sortable("cancel")
|
|
||||||
filterError()
|
|
||||||
})
|
|
||||||
|
|
||||||
$el.on "multiplesortreceive", (event, ui) ->
|
sprint = null
|
||||||
if $el.hasClass("active-filters")
|
|
||||||
ui.source.sortable("cancel")
|
|
||||||
filterError()
|
|
||||||
|
|
||||||
return
|
firstElement = if dragMultipleItems.length then dragMultipleItems[0] else item
|
||||||
|
|
||||||
itemUs = ui.item.scope().us
|
if isBacklog
|
||||||
itemIndex = getUsIndex(ui.item)
|
index = $(firstElement).index(".backlog-table-body .row")
|
||||||
|
else
|
||||||
|
index = $(firstElement).index()
|
||||||
|
sprint = parent.scope().sprint.id
|
||||||
|
|
||||||
deleteElement(ui.item)
|
if !sameContainer
|
||||||
|
if dragMultipleItems.length
|
||||||
$scope.$emit("sprint:us:move", [itemUs], itemIndex, null)
|
usList = _.map dragMultipleItems, (item) ->
|
||||||
ui.item.find('a').removeClass('noclick')
|
return item = $(item).scope().us
|
||||||
|
else
|
||||||
$el.on "multiplesortstop", (event, ui) ->
|
usList = [$(item).scope().us]
|
||||||
# When parent not exists, do nothing
|
|
||||||
if $(ui.items[0]).parent().length == 0
|
|
||||||
return
|
|
||||||
|
|
||||||
if $el.hasClass("active-filters")
|
|
||||||
return
|
|
||||||
|
|
||||||
items = _.sortBy ui.items, (item) ->
|
|
||||||
return $(item).index()
|
|
||||||
|
|
||||||
index = _.min _.map items, (item) ->
|
|
||||||
return getUsIndex(item)
|
|
||||||
|
|
||||||
us = _.map items, (item) ->
|
|
||||||
item = $(item)
|
|
||||||
itemUs = item.scope().us
|
|
||||||
|
|
||||||
# HACK: setTimeout prevents that firefox click
|
|
||||||
# event fires just after drag ends
|
|
||||||
setTimeout ( =>
|
|
||||||
item.find('a').removeClass('noclick')
|
|
||||||
), 300
|
|
||||||
|
|
||||||
return itemUs
|
|
||||||
|
|
||||||
$scope.$emit("sprint:us:move", us, index, null)
|
|
||||||
|
|
||||||
$el.on "sortstart", (event, ui) ->
|
|
||||||
ui.item.find('a').addClass('noclick')
|
|
||||||
|
|
||||||
$scope.$on "$destroy", ->
|
|
||||||
$el.off()
|
|
||||||
|
|
||||||
return {link: link}
|
|
||||||
|
|
||||||
BacklogEmptySortableDirective = ($repo, $rs, $rootscope) ->
|
|
||||||
# Notes about jquery bug:
|
|
||||||
# http://stackoverflow.com/questions/5791886/jquery-draggable-shows-
|
|
||||||
# helper-in-wrong-place-when-scrolled-down-page
|
|
||||||
|
|
||||||
link = ($scope, $el, $attrs) ->
|
|
||||||
bindOnce $scope, "project", (project) ->
|
|
||||||
# If the user has not enough permissions we don't enable the sortable
|
|
||||||
if project.my_permissions.indexOf("modify_us") > -1
|
|
||||||
$el.sortable({
|
|
||||||
items: ".us-item-row",
|
|
||||||
dropOnEmpty: true
|
|
||||||
})
|
|
||||||
|
|
||||||
$el.on "sortreceive", (event, ui) ->
|
|
||||||
itemUs = ui.item.scope().us
|
|
||||||
itemIndex = ui.item.index()
|
|
||||||
|
|
||||||
deleteElement(ui.item)
|
|
||||||
$scope.$emit("sprint:us:move", [itemUs], itemIndex, null)
|
|
||||||
|
|
||||||
ui.item.find('a').removeClass('noclick')
|
|
||||||
|
|
||||||
$scope.$on "$destroy", ->
|
|
||||||
$el.off()
|
|
||||||
|
|
||||||
return {link: link}
|
|
||||||
|
|
||||||
|
|
||||||
SprintSortableDirective = ($repo, $rs, $rootscope) ->
|
|
||||||
link = ($scope, $el, $attrs) ->
|
|
||||||
bindOnce $scope, "project", (project) ->
|
|
||||||
# If the user has not enough permissions we don't enable the sortable
|
|
||||||
if project.my_permissions.indexOf("modify_us") > -1
|
|
||||||
$el.sortable({
|
|
||||||
scroll: true
|
|
||||||
dropOnEmpty: true
|
|
||||||
items: ".sprint-table .milestone-us-item-row"
|
|
||||||
disableHorizontalScroll: true
|
|
||||||
connectWith: ".sprint,.backlog-table-body,.empty-backlog"
|
|
||||||
placeholder: "row us-item-row sortable-placeholder"
|
|
||||||
forcePlaceholderSize:true
|
|
||||||
})
|
|
||||||
|
|
||||||
$el.on "multiplesortreceive", (event, ui) ->
|
|
||||||
items = _.sortBy ui.items, (item) ->
|
|
||||||
return $(item).index()
|
|
||||||
|
|
||||||
index = _.min _.map items, (item) ->
|
|
||||||
return $(item).index()
|
|
||||||
|
|
||||||
us = _.map items, (item) ->
|
|
||||||
item = $(item)
|
|
||||||
itemUs = item.scope().us
|
|
||||||
|
|
||||||
|
if (dragMultipleItems.length)
|
||||||
|
_.each dragMultipleItems, (item) ->
|
||||||
deleteElement(item)
|
deleteElement(item)
|
||||||
|
else
|
||||||
|
deleteElement(item)
|
||||||
|
else
|
||||||
|
if dragMultipleItems.length
|
||||||
|
usList = _.map dragMultipleItems, (item) ->
|
||||||
|
return item = $(item).scope().us
|
||||||
|
else
|
||||||
|
usList = _.map items, (item) ->
|
||||||
|
item = $(item)
|
||||||
|
itemUs = item.scope().us
|
||||||
|
|
||||||
return itemUs
|
return itemUs
|
||||||
|
|
||||||
$scope.$emit("sprint:us:move", us, index, $scope.sprint.id)
|
$scope.$emit("sprint:us:move", usList, index, sprint)
|
||||||
|
|
||||||
$el.on "multiplesortstop", (event, ui) ->
|
scroll = autoScroll([window], {
|
||||||
# When parent not exists, do nothing
|
margin: 20,
|
||||||
if ui.item.parent().length == 0
|
pixels: 30,
|
||||||
return
|
scrollWhenOutside: true,
|
||||||
|
autoScroll: () ->
|
||||||
|
return this.down && drake.dragging;
|
||||||
|
})
|
||||||
|
|
||||||
itemUs = ui.item.scope().us
|
$scope.$on "$destroy", ->
|
||||||
itemIndex = ui.item.index()
|
$el.off()
|
||||||
|
drake.destroy()
|
||||||
# HACK: setTimeout prevents that firefox click
|
|
||||||
# event fires just after drag ends
|
|
||||||
setTimeout ( =>
|
|
||||||
ui.item.find('a').removeClass('noclick')
|
|
||||||
), 300
|
|
||||||
|
|
||||||
$scope.$emit("sprint:us:move", [itemUs], itemIndex, $scope.sprint.id)
|
|
||||||
|
|
||||||
$el.on "sortstart", (event, ui) ->
|
|
||||||
ui.item.find('a').addClass('noclick')
|
|
||||||
|
|
||||||
return {link:link}
|
|
||||||
|
|
||||||
|
return {link: link}
|
||||||
|
|
||||||
module.directive("tgBacklogSortable", [
|
module.directive("tgBacklogSortable", [
|
||||||
"$tgRepo",
|
"$tgRepo",
|
||||||
|
@ -228,17 +158,3 @@ module.directive("tgBacklogSortable", [
|
||||||
"$translate",
|
"$translate",
|
||||||
BacklogSortableDirective
|
BacklogSortableDirective
|
||||||
])
|
])
|
||||||
|
|
||||||
module.directive("tgBacklogEmptySortable", [
|
|
||||||
"$tgRepo",
|
|
||||||
"$tgResources",
|
|
||||||
"$rootScope",
|
|
||||||
BacklogEmptySortableDirective
|
|
||||||
])
|
|
||||||
|
|
||||||
module.directive("tgSprintSortable", [
|
|
||||||
"$tgRepo",
|
|
||||||
"$tgResources",
|
|
||||||
"$rootScope",
|
|
||||||
SprintSortableDirective
|
|
||||||
])
|
|
||||||
|
|
|
@ -108,6 +108,8 @@ LbTagLineDirective = ($rs, $template, $compile) ->
|
||||||
|
|
||||||
templateTags = $template.get("common/tag/lb-tag-line-tags.html", true)
|
templateTags = $template.get("common/tag/lb-tag-line-tags.html", true)
|
||||||
|
|
||||||
|
autocomplete = null
|
||||||
|
|
||||||
link = ($scope, $el, $attrs, $model) ->
|
link = ($scope, $el, $attrs, $model) ->
|
||||||
## Render
|
## Render
|
||||||
renderTags = (tags, tagsColors) ->
|
renderTags = (tags, tagsColors) ->
|
||||||
|
@ -130,7 +132,7 @@ LbTagLineDirective = ($rs, $template, $compile) ->
|
||||||
|
|
||||||
resetInput = ->
|
resetInput = ->
|
||||||
$el.find("input").val("")
|
$el.find("input").val("")
|
||||||
$el.find("input").autocomplete("close")
|
autocomplete.close()
|
||||||
|
|
||||||
## Aux methods
|
## Aux methods
|
||||||
addValue = (value) ->
|
addValue = (value) ->
|
||||||
|
@ -190,22 +192,15 @@ LbTagLineDirective = ($rs, $template, $compile) ->
|
||||||
deleteValue(value)
|
deleteValue(value)
|
||||||
|
|
||||||
bindOnce $scope, "project", (project) ->
|
bindOnce $scope, "project", (project) ->
|
||||||
positioningFunction = (position, elements) ->
|
input = $el.find("input")
|
||||||
menu = elements.element.element
|
|
||||||
menu.css("width", elements.target.width)
|
|
||||||
menu.css("top", position.top)
|
|
||||||
menu.css("left", position.left)
|
|
||||||
|
|
||||||
$el.find("input").autocomplete({
|
autocomplete = new Awesomplete(input[0], {
|
||||||
source: _.keys(project.tags_colors)
|
list: _.keys(project.tags_colors)
|
||||||
position: {
|
});
|
||||||
my: "left top",
|
|
||||||
using: positioningFunction
|
input.on "awesomplete-selectcomplete", () ->
|
||||||
}
|
addValue(input.val())
|
||||||
select: (event, ui) ->
|
input.val("")
|
||||||
addValue(ui.item.value)
|
|
||||||
ui.item.value = ""
|
|
||||||
})
|
|
||||||
|
|
||||||
$scope.$watch $attrs.ngModel, (tags) ->
|
$scope.$watch $attrs.ngModel, (tags) ->
|
||||||
tagsColors = $scope.project?.tags_colors or []
|
tagsColors = $scope.project?.tags_colors or []
|
||||||
|
@ -235,6 +230,8 @@ TagLineDirective = ($rootScope, $repo, $rs, $confirm, $qqueue, $template, $compi
|
||||||
templateTags = $template.get("common/tag/tags-line-tags.html", true)
|
templateTags = $template.get("common/tag/tags-line-tags.html", true)
|
||||||
|
|
||||||
link = ($scope, $el, $attrs, $model) ->
|
link = ($scope, $el, $attrs, $model) ->
|
||||||
|
autocomplete = null
|
||||||
|
|
||||||
isEditable = ->
|
isEditable = ->
|
||||||
if $attrs.requiredPerm?
|
if $attrs.requiredPerm?
|
||||||
return $scope.project.my_permissions.indexOf($attrs.requiredPerm) != -1
|
return $scope.project.my_permissions.indexOf($attrs.requiredPerm) != -1
|
||||||
|
@ -268,7 +265,8 @@ TagLineDirective = ($rootScope, $repo, $rs, $confirm, $qqueue, $template, $compi
|
||||||
hideInput = -> $el.find("input").addClass("hidden").blur()
|
hideInput = -> $el.find("input").addClass("hidden").blur()
|
||||||
resetInput = ->
|
resetInput = ->
|
||||||
$el.find("input").val("")
|
$el.find("input").val("")
|
||||||
$el.find("input").autocomplete("close")
|
|
||||||
|
autocomplete.close()
|
||||||
|
|
||||||
## Aux methods
|
## Aux methods
|
||||||
addValue = $qqueue.bindAdd (value) ->
|
addValue = $qqueue.bindAdd (value) ->
|
||||||
|
@ -366,22 +364,15 @@ TagLineDirective = ($rootScope, $repo, $rs, $confirm, $qqueue, $template, $compi
|
||||||
|
|
||||||
showAddTagButton()
|
showAddTagButton()
|
||||||
|
|
||||||
positioningFunction = (position, elements) ->
|
input = $el.find("input")
|
||||||
menu = elements.element.element
|
|
||||||
menu.css("width", elements.target.width)
|
|
||||||
menu.css("top", position.top)
|
|
||||||
menu.css("left", position.left)
|
|
||||||
|
|
||||||
$el.find("input").autocomplete({
|
autocomplete = new Awesomplete(input[0], {
|
||||||
source: _.keys(tags_colors)
|
list: _.keys(tags_colors)
|
||||||
position: {
|
});
|
||||||
my: "left top",
|
|
||||||
using: positioningFunction
|
input.on "awesomplete-selectcomplete", () ->
|
||||||
}
|
addValue(input.val())
|
||||||
select: (event, ui) ->
|
input.val("")
|
||||||
addValue(ui.item.value)
|
|
||||||
ui.item.value = ""
|
|
||||||
})
|
|
||||||
|
|
||||||
$scope.$watch $attrs.ngModel, (model) ->
|
$scope.$watch $attrs.ngModel, (model) ->
|
||||||
return if not model
|
return if not model
|
||||||
|
|
|
@ -418,8 +418,6 @@ module.directive("tgKanbanArchivedStatusIntro", ["$translate", KanbanArchivedSta
|
||||||
|
|
||||||
KanbanUserstoryDirective = ($rootscope, $loading, $rs, $rs2) ->
|
KanbanUserstoryDirective = ($rootscope, $loading, $rs, $rs2) ->
|
||||||
link = ($scope, $el, $attrs, $model) ->
|
link = ($scope, $el, $attrs, $model) ->
|
||||||
$el.disableSelection()
|
|
||||||
|
|
||||||
$scope.$watch "us", (us) ->
|
$scope.$watch "us", (us) ->
|
||||||
if us.is_blocked and not $el.hasClass("blocked")
|
if us.is_blocked and not $el.hasClass("blocked")
|
||||||
$el.addClass("blocked")
|
$el.addClass("blocked")
|
||||||
|
@ -498,8 +496,6 @@ module.directive("tgKanbanSquishColumn", ["$tgResources", KanbanSquishColumnDire
|
||||||
|
|
||||||
KanbanWipLimitDirective = ->
|
KanbanWipLimitDirective = ->
|
||||||
link = ($scope, $el, $attrs) ->
|
link = ($scope, $el, $attrs) ->
|
||||||
$el.disableSelection()
|
|
||||||
|
|
||||||
status = $scope.$eval($attrs.tgKanbanWipLimit)
|
status = $scope.$eval($attrs.tgKanbanWipLimit)
|
||||||
|
|
||||||
redrawWipLimit = =>
|
redrawWipLimit = =>
|
||||||
|
|
|
@ -55,16 +55,23 @@ KanbanSortableDirective = ($repo, $rs, $rootscope) ->
|
||||||
itemEl.off()
|
itemEl.off()
|
||||||
itemEl.remove()
|
itemEl.remove()
|
||||||
|
|
||||||
tdom.sortable({
|
containers = _.map $el.find('.task-column'), (item) ->
|
||||||
handle: ".kanban-task-inner"
|
return item
|
||||||
dropOnEmpty: true
|
|
||||||
connectWith: ".kanban-uses-box"
|
drake = dragula(containers, {
|
||||||
revert: 400
|
copySortSource: false,
|
||||||
|
copy: false,
|
||||||
|
mirrorContainer: tdom[0],
|
||||||
|
moves: (item) ->
|
||||||
|
return $(item).hasClass('kanban-task')
|
||||||
})
|
})
|
||||||
|
|
||||||
tdom.on "sortstop", (event, ui) ->
|
drake.on 'drag', (item) ->
|
||||||
parentEl = ui.item.parent()
|
oldParentScope = $(item).parent().scope()
|
||||||
itemEl = ui.item
|
|
||||||
|
drake.on 'dragend', (item) ->
|
||||||
|
parentEl = $(item).parent()
|
||||||
|
itemEl = $(item)
|
||||||
itemUs = itemEl.scope().us
|
itemUs = itemEl.scope().us
|
||||||
itemIndex = itemEl.index()
|
itemIndex = itemEl.index()
|
||||||
newParentScope = parentEl.scope()
|
newParentScope = parentEl.scope()
|
||||||
|
@ -78,14 +85,17 @@ KanbanSortableDirective = ($repo, $rs, $rootscope) ->
|
||||||
$scope.$apply ->
|
$scope.$apply ->
|
||||||
$rootscope.$broadcast("kanban:us:move", itemUs, itemUs.status, newStatusId, itemIndex)
|
$rootscope.$broadcast("kanban:us:move", itemUs, itemUs.status, newStatusId, itemIndex)
|
||||||
|
|
||||||
ui.item.find('a').removeClass('noclick')
|
scroll = autoScroll(containers, {
|
||||||
|
margin: 20,
|
||||||
tdom.on "sortstart", (event, ui) ->
|
pixels: 30,
|
||||||
oldParentScope = ui.item.parent().scope()
|
scrollWhenOutside: true,
|
||||||
ui.item.find('a').addClass('noclick')
|
autoScroll: () ->
|
||||||
|
return this.down && drake.dragging;
|
||||||
|
})
|
||||||
|
|
||||||
$scope.$on "$destroy", ->
|
$scope.$on "$destroy", ->
|
||||||
$el.off()
|
$el.off()
|
||||||
|
drake.destroy()
|
||||||
|
|
||||||
return {link: link}
|
return {link: link}
|
||||||
|
|
||||||
|
|
|
@ -309,8 +309,6 @@ module.directive("tgTaskboard", ["$rootScope", TaskboardDirective])
|
||||||
|
|
||||||
TaskboardTaskDirective = ($rootscope, $loading, $rs, $rs2) ->
|
TaskboardTaskDirective = ($rootscope, $loading, $rs, $rs2) ->
|
||||||
link = ($scope, $el, $attrs, $model) ->
|
link = ($scope, $el, $attrs, $model) ->
|
||||||
$el.disableSelection()
|
|
||||||
|
|
||||||
$scope.$watch "task", (task) ->
|
$scope.$watch "task", (task) ->
|
||||||
if task.is_blocked and not $el.hasClass("blocked")
|
if task.is_blocked and not $el.hasClass("blocked")
|
||||||
$el.addClass("blocked")
|
$el.addClass("blocked")
|
||||||
|
|
|
@ -39,9 +39,9 @@ module = angular.module("taigaBacklog")
|
||||||
|
|
||||||
TaskboardSortableDirective = ($repo, $rs, $rootscope) ->
|
TaskboardSortableDirective = ($repo, $rs, $rootscope) ->
|
||||||
link = ($scope, $el, $attrs) ->
|
link = ($scope, $el, $attrs) ->
|
||||||
bindOnce $scope, "project", (project) ->
|
bindOnce $scope, "tasks", (xx) ->
|
||||||
# If the user has not enough permissions we don't enable the sortable
|
# If the user has not enough permissions we don't enable the sortable
|
||||||
if not (project.my_permissions.indexOf("modify_us") > -1)
|
if not ($scope.project.my_permissions.indexOf("modify_us") > -1)
|
||||||
return
|
return
|
||||||
|
|
||||||
oldParentScope = null
|
oldParentScope = null
|
||||||
|
@ -55,16 +55,22 @@ TaskboardSortableDirective = ($repo, $rs, $rootscope) ->
|
||||||
itemEl.off()
|
itemEl.off()
|
||||||
itemEl.remove()
|
itemEl.remove()
|
||||||
|
|
||||||
tdom.sortable({
|
containers = _.map $el.find('.task-column'), (item) ->
|
||||||
handle: ".taskboard-task-inner",
|
return item
|
||||||
dropOnEmpty: true
|
|
||||||
connectWith: ".taskboard-tasks-box"
|
drake = dragula(containers, {
|
||||||
revert: 400
|
copySortSource: false,
|
||||||
|
copy: false,
|
||||||
|
mirrorContainer: $el[0],
|
||||||
|
moves: (item) -> return $(item).hasClass('taskboard-task')
|
||||||
})
|
})
|
||||||
|
|
||||||
tdom.on "sortstop", (event, ui) ->
|
drake.on 'drag', (item) ->
|
||||||
parentEl = ui.item.parent()
|
oldParentScope = $(item).parent().scope()
|
||||||
itemEl = ui.item
|
|
||||||
|
drake.on 'dragend', (item) ->
|
||||||
|
parentEl = $(item).parent()
|
||||||
|
itemEl = $(item)
|
||||||
itemTask = itemEl.scope().task
|
itemTask = itemEl.scope().task
|
||||||
itemIndex = itemEl.index()
|
itemIndex = itemEl.index()
|
||||||
newParentScope = parentEl.scope()
|
newParentScope = parentEl.scope()
|
||||||
|
@ -80,14 +86,17 @@ TaskboardSortableDirective = ($repo, $rs, $rootscope) ->
|
||||||
$scope.$apply ->
|
$scope.$apply ->
|
||||||
$rootscope.$broadcast("taskboard:task:move", itemTask, newUsId, newStatusId, itemIndex)
|
$rootscope.$broadcast("taskboard:task:move", itemTask, newUsId, newStatusId, itemIndex)
|
||||||
|
|
||||||
ui.item.find('a').removeClass('noclick')
|
scroll = autoScroll(containers, {
|
||||||
|
margin: 20,
|
||||||
tdom.on "sortstart", (event, ui) ->
|
pixels: 30,
|
||||||
oldParentScope = ui.item.parent().scope()
|
scrollWhenOutside: true,
|
||||||
ui.item.find('a').addClass('noclick')
|
autoScroll: () ->
|
||||||
|
return this.down && drake.dragging;
|
||||||
|
})
|
||||||
|
|
||||||
$scope.$on "$destroy", ->
|
$scope.$on "$destroy", ->
|
||||||
$el.off()
|
$el.off()
|
||||||
|
drake.destroy()
|
||||||
|
|
||||||
return {link: link}
|
return {link: link}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,573 @@
|
||||||
|
// The MIT License (MIT)
|
||||||
|
|
||||||
|
// Copyright (c) 2016 Quentin Engles
|
||||||
|
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
// more_events
|
||||||
|
|
||||||
|
function MoreEvents(context){
|
||||||
|
this.listeners = {};
|
||||||
|
this.__context = context || this;
|
||||||
|
}
|
||||||
|
|
||||||
|
MoreEvents.prototype = {
|
||||||
|
constructor: MoreEvents,
|
||||||
|
on: function(event, listener){
|
||||||
|
this.listeners[event] = this.listeners[event] || [];
|
||||||
|
this.listeners[event].push(listener);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
one: function(event, listener){
|
||||||
|
function onceListener(){
|
||||||
|
listener.apply(this, arguments);
|
||||||
|
this.off(event, onceListener);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return this.on(event, onceListener);
|
||||||
|
},
|
||||||
|
emit: function(event){
|
||||||
|
if(typeof this.listeners[event] === 'undefined' || !this.listeners[event].length)
|
||||||
|
return this;
|
||||||
|
|
||||||
|
var args = Array.prototype.slice.call(arguments, 1),
|
||||||
|
canRun = this.listeners[event].length;
|
||||||
|
|
||||||
|
do{
|
||||||
|
this.listeners[event][--canRun].apply(this.__context, args);
|
||||||
|
}while(canRun);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
off: function(event, listener){
|
||||||
|
if(this.listeners[event] === undefined || !this.listeners[event].length)
|
||||||
|
return this;
|
||||||
|
this.listeners[event] = this.listeners[event].filter(function(item){
|
||||||
|
return item !== listener;
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
dispose: function(){
|
||||||
|
for(var n in this){
|
||||||
|
this[n] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// pointer_point
|
||||||
|
|
||||||
|
var Emitter = MoreEvents;
|
||||||
|
|
||||||
|
if(!Date.now){ Date.now = function(){ return new Date().getTime() } }
|
||||||
|
|
||||||
|
function LocalDimensions(point, rect){
|
||||||
|
for(var n in rect)
|
||||||
|
setProp(this, n, rect[n]);
|
||||||
|
|
||||||
|
setProp(this, 'x', point.x - rect.left+1);
|
||||||
|
setProp(this, 'y', point.y - rect.top+1);
|
||||||
|
|
||||||
|
setProp(this, 'north', (((rect.bottom - rect.top) / 2)-this.y));
|
||||||
|
setProp(this, 'south', ((-(rect.bottom - rect.top) / 2)+this.y));
|
||||||
|
setProp(this, 'east', (((rect.right - rect.left) / 2)-this.x));
|
||||||
|
setProp(this, 'west', ((-(rect.right - rect.left) / 2)+this.x));
|
||||||
|
|
||||||
|
|
||||||
|
function setProp(self, name, value){
|
||||||
|
Object.defineProperty(self, name, {
|
||||||
|
value: value,
|
||||||
|
configurable: true,
|
||||||
|
writable: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function Point(elements){
|
||||||
|
var self = this, el = [];
|
||||||
|
|
||||||
|
if(typeof elements.length === 'undefined'){
|
||||||
|
elements = [elements];
|
||||||
|
}
|
||||||
|
|
||||||
|
for(var i=0; i<elements.length; i++){
|
||||||
|
if(elements[i] !== undefined){
|
||||||
|
if(typeof elements[i] === 'string'){
|
||||||
|
try{
|
||||||
|
el.push(document.querySelector(e));
|
||||||
|
}catch(err){
|
||||||
|
throw new Error(e + ' is not a valid selector used by pointer.');
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
el.push(elements[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var pos = {}, direction = {}, rect, local,
|
||||||
|
lastmousex=-1, lastmousey=-1, timestamp, mousetravel = 0,
|
||||||
|
startX=-1, startY=-1, scrolling = false, buf = 10, timeOut = false,
|
||||||
|
downTime;
|
||||||
|
|
||||||
|
var special = {
|
||||||
|
hold: []
|
||||||
|
};
|
||||||
|
|
||||||
|
this.emitter = new Emitter(this);
|
||||||
|
|
||||||
|
this.origin = null;
|
||||||
|
this.current = null;
|
||||||
|
this.previous = null;
|
||||||
|
|
||||||
|
window.addEventListener('mousedown', onDown, false);
|
||||||
|
window.addEventListener('mousemove', onMove, false);
|
||||||
|
window.addEventListener("mouseup", onUp, false);
|
||||||
|
|
||||||
|
window.addEventListener('touchstart', onDown, false);
|
||||||
|
window.addEventListener('touchmove', onMove, false);
|
||||||
|
window.addEventListener('touchend', onUp, false);
|
||||||
|
|
||||||
|
window.addEventListener('scroll', function(e){
|
||||||
|
scrolling = true;
|
||||||
|
clearTimeout(timeOut)
|
||||||
|
timeOut = setTimeout(function(){
|
||||||
|
scrolling = false;
|
||||||
|
}, 100)
|
||||||
|
});
|
||||||
|
|
||||||
|
function onDown(e){
|
||||||
|
|
||||||
|
downTime = Date.now();
|
||||||
|
|
||||||
|
toPoint(e);
|
||||||
|
self.down = true;
|
||||||
|
self.up = false;
|
||||||
|
if(self.current){
|
||||||
|
self.origin = self.current;
|
||||||
|
self.emitter.emit('down', self.current, local);
|
||||||
|
}
|
||||||
|
|
||||||
|
startX = self.x;
|
||||||
|
startY = self.y;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMove(e){
|
||||||
|
toPoint(e);
|
||||||
|
self.emitter.emit('move', self.current, local);
|
||||||
|
if(self.down && self.current){
|
||||||
|
self.emitter.emit('stroke', self.current, local);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onUp(e){
|
||||||
|
self.down = false;
|
||||||
|
self.up = true;
|
||||||
|
|
||||||
|
if(self.current){
|
||||||
|
self.emitter.emit('up', self.current, local);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(e.targetTouches){
|
||||||
|
//Allow click within buf. A 20x20 square.
|
||||||
|
if(!(self.y > (startY - buf) && self.y < (startY + buf) &&
|
||||||
|
self.x > (startX - buf) && self.x < (startX + buf))){
|
||||||
|
//If there is scrolling there was a touch flick.
|
||||||
|
if(!scrolling){
|
||||||
|
//No touch flick so
|
||||||
|
self.previous = null;
|
||||||
|
self.origin = null;
|
||||||
|
e.preventDefault();
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scrolling = false;
|
||||||
|
self.previous = null;
|
||||||
|
self.origin = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toPoint(event){
|
||||||
|
var dot, eventDoc, doc, body, pageX, pageY;
|
||||||
|
var target, newTarget = null, leaving = null;
|
||||||
|
|
||||||
|
event = event || window.event; // IE-ism
|
||||||
|
target = event.target || event.srcElement;
|
||||||
|
|
||||||
|
//Supporting touch
|
||||||
|
//http://www.creativebloq.com/javascript/make-your-site-work-touch-devices-51411644
|
||||||
|
if(event.targetTouches) {
|
||||||
|
event.pageX = event.targetTouches[0].clientX;
|
||||||
|
event.pageY = event.targetTouches[0].clientY;
|
||||||
|
event.clientX = event.targetTouches[0].clientX;
|
||||||
|
event.clientY = event.targetTouches[0].clientY;
|
||||||
|
}else
|
||||||
|
|
||||||
|
// If pageX/Y aren't available and clientX/Y are,
|
||||||
|
// calculate pageX/Y - logic taken from jQuery.
|
||||||
|
// (This is to support old IE)
|
||||||
|
if (event.pageX === null && event.clientX !== null) {
|
||||||
|
eventDoc = (event.target && event.target.ownerDocument) || document;
|
||||||
|
doc = eventDoc.documentElement;
|
||||||
|
body = eventDoc.body;
|
||||||
|
|
||||||
|
event.pageX = event.clientX +
|
||||||
|
(doc && doc.scrollLeft || body && body.scrollLeft || 0) -
|
||||||
|
(doc && doc.clientLeft || body && body.clientLeft || 0);
|
||||||
|
event.pageY = event.clientY +
|
||||||
|
(doc && doc.scrollTop || body && body.scrollTop || 0) -
|
||||||
|
(doc && doc.clientTop || body && body.clientTop || 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if(self.x && self.y){
|
||||||
|
if(event.pageX < self.x)
|
||||||
|
direction.h = 'left';
|
||||||
|
else if(event.pageX > self.x)
|
||||||
|
direction.h = 'right';
|
||||||
|
if(event.pageY < self.y)
|
||||||
|
direction.v = 'up';
|
||||||
|
else if(event.pageY > self.y)
|
||||||
|
direction.v = 'down';
|
||||||
|
|
||||||
|
lastmousex = self.x;
|
||||||
|
lastmousey = self.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = {};
|
||||||
|
//Prefer the viewport with clientX, and clientY.
|
||||||
|
//pageX, and pageY change too often.
|
||||||
|
pos.x = event.clientX;//event.pageX;
|
||||||
|
pos.y = event.clientY;//event.pageY;
|
||||||
|
|
||||||
|
if(self.current === null || self.outside(self.current)){
|
||||||
|
for(var i=0; i<el.length; i++){
|
||||||
|
//console.log('inside el['+i+'] '+self.inside(el[i]));
|
||||||
|
if(el[i] === target || self.inside(el[i])){
|
||||||
|
//if(el[i] === target){
|
||||||
|
newTarget = el[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
leaving = self.current;
|
||||||
|
if(newTarget){
|
||||||
|
self.previous = self.current;
|
||||||
|
self.current = newTarget;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rect = self.current ? getRect(self.current) : null;
|
||||||
|
local = rect ? new LocalDimensions(self, rect) : null;
|
||||||
|
|
||||||
|
if(leaving){
|
||||||
|
if(!newTarget)
|
||||||
|
self.current = null;
|
||||||
|
self.emitter.emit('leave', leaving, local);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(newTarget){
|
||||||
|
self.emitter.emit('enter', self.current, local);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get speed
|
||||||
|
//http://stackoverflow.com/questions/6417036/track-mouse-speed-with-js
|
||||||
|
Object.defineProperty(this, 'speedX', {
|
||||||
|
get: function(){
|
||||||
|
var now = Date.now() / 1000;
|
||||||
|
var dt = now - timestamp;
|
||||||
|
var dx = self.x - lastmousex;
|
||||||
|
timestamp = now;
|
||||||
|
return Math.round(dx / dt);// * 1000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'speedY', {
|
||||||
|
get: function(){
|
||||||
|
var now = Date.now() / 1000;
|
||||||
|
var dt = now - timestamp;
|
||||||
|
var dy = self.y - lastmousey;
|
||||||
|
timestamp = now;
|
||||||
|
return Math.round(dy / dt);// * 1000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'x', {
|
||||||
|
get: function(){
|
||||||
|
return pos.x;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'y', {
|
||||||
|
get: function(){
|
||||||
|
return pos.y;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'h', {
|
||||||
|
get: function(){
|
||||||
|
return direction.h;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'v', {
|
||||||
|
get: function(){
|
||||||
|
return direction.v;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.emitter.on('up', function(el, rect){
|
||||||
|
if(downTime){
|
||||||
|
for(var i=0; i<special.hold.length; i++){
|
||||||
|
if(Date.now() > downTime + (special.hold[i].data || 2000)){
|
||||||
|
special.hold[i].callback.call(this, el, rect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
downTime = 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
function removeSpecial(event, cb){
|
||||||
|
for(var i=0; i<special[event].length; i++){
|
||||||
|
if(special[event][i].callback === cb){
|
||||||
|
special[event].splice(i, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addSpecial(event, data, cb){
|
||||||
|
if(typeof cb === 'undefined'){
|
||||||
|
cb = data;
|
||||||
|
data = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
special[event].push({
|
||||||
|
data: data,
|
||||||
|
callback: cb
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.on = function(event, cb){
|
||||||
|
if(special[event]){
|
||||||
|
addSpecial(event, cb, arguments[2]);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
this.emitter.on(event, cb);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.off = function(event, cb){
|
||||||
|
if(special[event]){
|
||||||
|
removeSpecial(event, cb);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
this.emitter.off(event, cb);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.add = function(element){
|
||||||
|
if(typeof element === 'string'){
|
||||||
|
try{
|
||||||
|
el.push(document.querySelector(e));
|
||||||
|
}catch(err){
|
||||||
|
throw new Error(e + ' is not a valid selector, and can\'t be used add to pointer.');
|
||||||
|
}
|
||||||
|
}else if(!element){
|
||||||
|
throw new Error(e + ' can not be added to pointer.');
|
||||||
|
}
|
||||||
|
|
||||||
|
el.push(element);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.destroy = function(){
|
||||||
|
window.removeEventListener('mousedown', onDown, false);
|
||||||
|
window.removeEventListener('mousemove', onMove, false);
|
||||||
|
window.removeEventListener('mouseup', onUp, false);
|
||||||
|
|
||||||
|
window.removeEventListener('touchstart', onDown, false);
|
||||||
|
window.removeEventListener('touchmove', onMove, false);
|
||||||
|
window.removeEventListener('touchend', onUp, false);
|
||||||
|
el = null;
|
||||||
|
self = null;
|
||||||
|
pos = null;
|
||||||
|
direction = null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Point.prototype = {
|
||||||
|
constructor: Point,
|
||||||
|
inside: function(el){
|
||||||
|
if(!el) throw new TypeError('Cannot be inside '+el);
|
||||||
|
var rect = getRect(el);
|
||||||
|
return (this.y > rect.top && this.y < rect.bottom &&
|
||||||
|
this.x > rect.left && this.x < rect.right);
|
||||||
|
},
|
||||||
|
outside: function(el){
|
||||||
|
if(!el) throw new TypeError('Cannot be outside '+el);
|
||||||
|
return !this.inside(el);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function elementFromPoint(x, y){
|
||||||
|
if(document.getElementFromPoint)
|
||||||
|
return document.getElementFromPoint(x, y);
|
||||||
|
else
|
||||||
|
return document.elementFromPoint(x, y);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function safeObject(src){
|
||||||
|
var obj = {};
|
||||||
|
for(var n in src)
|
||||||
|
obj[n] = src[n];
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRect(el){
|
||||||
|
if(el === window){
|
||||||
|
return {
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: window.innerWidth,
|
||||||
|
bottom: window.innerHeight,
|
||||||
|
width: window.innerWidth,
|
||||||
|
height: window.innerHeight
|
||||||
|
};
|
||||||
|
|
||||||
|
}else{
|
||||||
|
return el.getBoundingClientRect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var pointer = function(element){
|
||||||
|
return new Point(element);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Autscroller
|
||||||
|
|
||||||
|
function AutoScrollerFactory(element, options){
|
||||||
|
return new AutoScroller(element, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
function AutoScroller(elements, options){
|
||||||
|
var self = this, pixels = 2;
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
this.margin = options.margin || -1;
|
||||||
|
this.scrolling = false;
|
||||||
|
this.scrollWhenOutside = options.scrollWhenOutside || false;
|
||||||
|
|
||||||
|
this.point = pointer(elements);
|
||||||
|
|
||||||
|
if(!isNaN(options.pixels)){
|
||||||
|
pixels = options.pixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(typeof options.autoScroll === 'boolean'){
|
||||||
|
this.autoScroll = options.autoScroll ? function(){return true;} : function(){return false;};
|
||||||
|
}else if(typeof options.autoScroll === 'undefined'){
|
||||||
|
this.autoScroll = function(){return false;};
|
||||||
|
}else if(typeof options.autoScroll === 'function'){
|
||||||
|
this.autoScroll = options.autoScroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.destroy = function() {
|
||||||
|
this.point.destroy();
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.defineProperties(this, {
|
||||||
|
down: {
|
||||||
|
get: function(){ return self.point.down; }
|
||||||
|
},
|
||||||
|
interval: {
|
||||||
|
get: function(){ return 1/pixels * 1000; }
|
||||||
|
},
|
||||||
|
pixels: {
|
||||||
|
set: function(i){ pixels = i; },
|
||||||
|
get: function(){ return pixels; }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.point.on('move', function(el, rect){
|
||||||
|
|
||||||
|
if(!el) return;
|
||||||
|
if(!self.autoScroll()) return;
|
||||||
|
if(!self.scrollWhenOutside && this.outside(el)) return;
|
||||||
|
|
||||||
|
if(self.point.y < rect.top + self.margin){
|
||||||
|
autoScrollV(el, -1, rect);
|
||||||
|
}else if(self.point.y > rect.bottom - self.margin){
|
||||||
|
autoScrollV(el, 1, rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(self.point.x < rect.left + self.margin){
|
||||||
|
autoScrollH(el, -1, rect);
|
||||||
|
}else if(self.point.x > rect.right - self.margin){
|
||||||
|
autoScrollH(el, 1, rect);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function autoScrollV(el, amount, rect){
|
||||||
|
//if(!self.down) return;
|
||||||
|
if(!self.autoScroll()) return;
|
||||||
|
if(!self.scrollWhenOutside && self.point.outside(el)) return;
|
||||||
|
if(el === window){
|
||||||
|
window.scrollTo(el.pageXOffset, el.pageYOffset + amount);
|
||||||
|
}else{
|
||||||
|
el.scrollTop = el.scrollTop + amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(function(){
|
||||||
|
if(self.point.y < rect.top + self.margin){
|
||||||
|
autoScrollV(el, amount, rect);
|
||||||
|
}else if(self.point.y > rect.bottom - self.margin){
|
||||||
|
autoScrollV(el, amount, rect);
|
||||||
|
}
|
||||||
|
}, self.interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
function autoScrollH(el, amount, rect){
|
||||||
|
//if(!self.down) return;
|
||||||
|
if(!self.autoScroll()) return;
|
||||||
|
if(!self.scrollWhenOutside && self.point.outside(el)) return;
|
||||||
|
if(el === window){
|
||||||
|
window.scrollTo(el.pageXOffset + amount, el.pageYOffset);
|
||||||
|
}else{
|
||||||
|
el.scrollLeft = el.scrollLeft + amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(function(){
|
||||||
|
if(self.point.x < rect.left + self.margin){
|
||||||
|
autoScrollH(el, amount, rect);
|
||||||
|
}else if(self.point.x > rect.right - self.margin){
|
||||||
|
autoScrollH(el, amount, rect);
|
||||||
|
}
|
||||||
|
}, self.interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
window.autoScroll = AutoScrollerFactory;
|
||||||
|
}());
|
|
@ -0,0 +1,221 @@
|
||||||
|
(function() {
|
||||||
|
var multipleSortableClass = 'ui-multisortable-multiple';
|
||||||
|
var mainClass = 'main-drag-item';
|
||||||
|
var inProgress = false;
|
||||||
|
|
||||||
|
var reset = function(elm) {
|
||||||
|
$(elm)
|
||||||
|
.removeAttr('style')
|
||||||
|
.removeClass('tg-backlog-us-mirror')
|
||||||
|
.removeClass('backlog-us-mirror')
|
||||||
|
.data('dragMultipleIndex', null)
|
||||||
|
.data('dragMultipleActive', false);
|
||||||
|
};
|
||||||
|
|
||||||
|
var sort = function(positions) {
|
||||||
|
var current = dragMultiple.items.elm;
|
||||||
|
|
||||||
|
positions.after.reverse();
|
||||||
|
|
||||||
|
$.each(positions.after, function () {
|
||||||
|
reset(this);
|
||||||
|
current.after(this);
|
||||||
|
});
|
||||||
|
|
||||||
|
$.each(positions.before, function () {
|
||||||
|
reset(this);
|
||||||
|
current.before(this);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var drag = function() {
|
||||||
|
var current = dragMultiple.items.elm;
|
||||||
|
var container = dragMultiple.items.container;
|
||||||
|
|
||||||
|
var shadow = dragMultiple.items.shadow;
|
||||||
|
|
||||||
|
// following the drag element
|
||||||
|
var currentLeft = shadow.position().left;
|
||||||
|
var currentTop = shadow.position().top;
|
||||||
|
var height = shadow.outerHeight();
|
||||||
|
|
||||||
|
_.forEach(dragMultiple.items.draggingItems, function(elm, index) {
|
||||||
|
var elmIndex = parseInt(elm.data('dragMultipleIndex'), 10);
|
||||||
|
var top = currentTop + (elmIndex * height);
|
||||||
|
|
||||||
|
elm
|
||||||
|
.css({
|
||||||
|
top: top,
|
||||||
|
left: currentLeft
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var stop = function() {
|
||||||
|
inProgress = false;
|
||||||
|
|
||||||
|
refreshOriginal();
|
||||||
|
|
||||||
|
var current = dragMultiple.items.elm;
|
||||||
|
var container = dragMultiple.items.container;
|
||||||
|
|
||||||
|
$(window).off('mousemove.dragmultiple');
|
||||||
|
|
||||||
|
// reset
|
||||||
|
dragMultiple.items = {};
|
||||||
|
|
||||||
|
$('.' + mainClass).removeClass(mainClass);
|
||||||
|
$('.tg-backlog-us-mirror').remove();
|
||||||
|
$('.backlog-us-mirror').removeClass('backlog-us-mirror');
|
||||||
|
|
||||||
|
$('.tg-backlog-us-dragging')
|
||||||
|
.removeClass('tg-backlog-us-dragging')
|
||||||
|
.show();
|
||||||
|
|
||||||
|
return $('.' + multipleSortableClass);
|
||||||
|
};
|
||||||
|
|
||||||
|
var refreshOriginal = function() {
|
||||||
|
var index = parseInt(dragMultiple.items.elm.data('dragMultipleIndex'), 10);
|
||||||
|
|
||||||
|
var after = [];
|
||||||
|
var before = [];
|
||||||
|
|
||||||
|
_.forEach(dragMultiple.items.draggedItemsOriginal, function(item) {
|
||||||
|
if (parseInt($(item).data('dragMultipleIndex'), 10) > index) {
|
||||||
|
after.push(item);
|
||||||
|
} else {
|
||||||
|
before.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
after.reverse();
|
||||||
|
|
||||||
|
_.forEach(after, function(item) {
|
||||||
|
$(item).insertAfter(dragMultiple.items.elm);
|
||||||
|
});
|
||||||
|
|
||||||
|
_.forEach(before, function(item) {
|
||||||
|
$(item).insertBefore(dragMultiple.items.elm);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var isMultiple = function(elm, container) {
|
||||||
|
var items = $(container).find('.' + multipleSortableClass);
|
||||||
|
|
||||||
|
if (!$(elm).hasClass(multipleSortableClass) || !(items.length > 1)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
var setIndex = function(items) {
|
||||||
|
var before = [];
|
||||||
|
var after = [];
|
||||||
|
var mainFound = false;
|
||||||
|
_.forEach(items, function(item, index) {
|
||||||
|
if ($(item).data('dragMultipleIndex') === 0) {
|
||||||
|
mainFound = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mainFound) {
|
||||||
|
after.push(item);
|
||||||
|
} else {
|
||||||
|
before.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
before.reverse();
|
||||||
|
|
||||||
|
_.forEach(after, function(item, index) {
|
||||||
|
$(item).data('dragMultipleIndex', index + 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
_.forEach(before, function(item, index) {
|
||||||
|
$(item).data('dragMultipleIndex', -index - 1);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var dragMultiple = {};
|
||||||
|
|
||||||
|
dragMultiple.prepare = function(elm, container) {
|
||||||
|
inProgress = true;
|
||||||
|
|
||||||
|
var items = $(container).find('.' + multipleSortableClass);
|
||||||
|
|
||||||
|
$(elm)
|
||||||
|
.data('dragmultiple:originalPosition', $(elm).position())
|
||||||
|
.data('dragMultipleActive', true);
|
||||||
|
|
||||||
|
dragMultiple.items = {};
|
||||||
|
|
||||||
|
dragMultiple.items.elm = $(elm);
|
||||||
|
dragMultiple.items.container = $(container);
|
||||||
|
|
||||||
|
dragMultiple.items.elm.data('dragMultipleIndex', 0);
|
||||||
|
|
||||||
|
setIndex(items);
|
||||||
|
|
||||||
|
dragMultiple.items.shadow = $('.gu-mirror');
|
||||||
|
|
||||||
|
dragMultiple.items.elm.addClass(mainClass);
|
||||||
|
|
||||||
|
items = _.filter(items, function(item) {
|
||||||
|
return !$(item).hasClass(mainClass);
|
||||||
|
});
|
||||||
|
|
||||||
|
dragMultiple.items.draggedItemsOriginal = items;
|
||||||
|
|
||||||
|
var itemsCloned = _.map(items, function (item) {
|
||||||
|
clone = $(item).clone(true);
|
||||||
|
|
||||||
|
clone
|
||||||
|
.addClass('backlog-us-mirror')
|
||||||
|
.addClass('tg-backlog-us-mirror')
|
||||||
|
.data('dragmultiple:originalPosition', $(item).position())
|
||||||
|
.data('dragMultipleActive', true)
|
||||||
|
.css({
|
||||||
|
zIndex: '9999',
|
||||||
|
opacity: '0.8',
|
||||||
|
position: 'fixed',
|
||||||
|
width: dragMultiple.items.elm.outerWidth(),
|
||||||
|
height: dragMultiple.items.elm.outerHeight()
|
||||||
|
});
|
||||||
|
|
||||||
|
$(item)
|
||||||
|
.hide()
|
||||||
|
.addClass('tg-backlog-us-dragging');
|
||||||
|
|
||||||
|
return clone;
|
||||||
|
});
|
||||||
|
|
||||||
|
dragMultiple.items.draggingItems = itemsCloned;
|
||||||
|
|
||||||
|
$(document.body).append(itemsCloned);
|
||||||
|
};
|
||||||
|
|
||||||
|
dragMultiple.start = function(item, container) {
|
||||||
|
if (isMultiple(item, container)) {
|
||||||
|
$(window).on('mousemove.dragmultiple', function() {
|
||||||
|
if (!inProgress) {
|
||||||
|
dragMultiple.prepare(item, container);
|
||||||
|
}
|
||||||
|
|
||||||
|
drag();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
dragMultiple.stop = function() {
|
||||||
|
if (inProgress) {
|
||||||
|
return stop();
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.dragMultiple = dragMultiple;
|
||||||
|
}());
|
|
@ -1,161 +0,0 @@
|
||||||
(function($) {
|
|
||||||
var multipleSortableClass = 'ui-multisortable-multiple';
|
|
||||||
var dragStarted = false;
|
|
||||||
|
|
||||||
var multiSort = {};
|
|
||||||
|
|
||||||
multiSort.isBelow = function(elm, compare) {
|
|
||||||
var elmOriginalPosition = elm.data('dragmultiple:originalPosition');
|
|
||||||
var compareOriginalPosition = compare.data('dragmultiple:originalPosition');
|
|
||||||
|
|
||||||
return elmOriginalPosition.top > compareOriginalPosition.top;
|
|
||||||
};
|
|
||||||
|
|
||||||
multiSort.reset = function(elm) {
|
|
||||||
$(elm)
|
|
||||||
.removeClass("ui-sortable-helper")
|
|
||||||
.removeAttr('style')
|
|
||||||
.data('dragMultipleActive', false);
|
|
||||||
};
|
|
||||||
|
|
||||||
multiSort.sort = function(current, positions) {
|
|
||||||
positions.after.reverse();
|
|
||||||
|
|
||||||
$.each(positions.after, function () {
|
|
||||||
multiSort.reset(this);
|
|
||||||
current.after(this);
|
|
||||||
});
|
|
||||||
|
|
||||||
$.each(positions.before, function () {
|
|
||||||
multiSort.reset(this);
|
|
||||||
current.before(this);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
multiSort.sortPositions = function(elm, current) {
|
|
||||||
//saved if the elements are after or before the current
|
|
||||||
var insertAfter = [];
|
|
||||||
var insertBefore = [];
|
|
||||||
|
|
||||||
$(elm).find('.' + multipleSortableClass).each(function () {
|
|
||||||
var elm = $(this);
|
|
||||||
|
|
||||||
if (elm[0] === current[0] || !current.hasClass(multipleSortableClass)) return;
|
|
||||||
|
|
||||||
if (multiSort.isBelow(elm, current)) {
|
|
||||||
insertAfter.push(elm);
|
|
||||||
} else {
|
|
||||||
insertBefore.push(elm);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {'after': insertAfter, 'before': insertBefore};
|
|
||||||
};
|
|
||||||
|
|
||||||
$.widget( "ui.sortable", $.ui.sortable, {
|
|
||||||
_mouseStart: function() {
|
|
||||||
dragStarted = false;
|
|
||||||
|
|
||||||
this._superApply( arguments );
|
|
||||||
},
|
|
||||||
_createHelper: function () {
|
|
||||||
var helper = this._superApply( arguments );
|
|
||||||
|
|
||||||
if ($(helper).hasClass(multipleSortableClass)) {
|
|
||||||
$(this.element).find('.' + multipleSortableClass).each(function () {
|
|
||||||
$(this)
|
|
||||||
.data('dragmultiple:originalPosition', $(this).position())
|
|
||||||
.data('dragMultipleActive', true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return helper;
|
|
||||||
},
|
|
||||||
_mouseStop: function (event, ui) {
|
|
||||||
var current = this.helper;
|
|
||||||
var elms = [];
|
|
||||||
|
|
||||||
if (current.hasClass(multipleSortableClass)) {
|
|
||||||
elms = $(this.element).find('.' + multipleSortableClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!elms.length) {
|
|
||||||
elms = [current];
|
|
||||||
}
|
|
||||||
|
|
||||||
//save the order of the elements relative to the main
|
|
||||||
var positions = multiSort.sortPositions(this.element, current);
|
|
||||||
|
|
||||||
this._superApply( arguments );
|
|
||||||
|
|
||||||
if (this.element !== this.currentContainer.element) {
|
|
||||||
// change to another sortable list
|
|
||||||
multiSort.sort(current, positions);
|
|
||||||
|
|
||||||
$(this.currentContainer.element).trigger('multiplesortreceive', {
|
|
||||||
'item': current,
|
|
||||||
'items': elms,
|
|
||||||
'source': this.element
|
|
||||||
});
|
|
||||||
} else if (current.hasClass(multipleSortableClass)) {
|
|
||||||
// sort in the same list
|
|
||||||
multiSort.sort(current, positions);
|
|
||||||
}
|
|
||||||
|
|
||||||
$(this.element).trigger('multiplesortstop', {
|
|
||||||
'item': current,
|
|
||||||
'items': elms
|
|
||||||
});
|
|
||||||
},
|
|
||||||
_mouseDrag: function(key, value) {
|
|
||||||
this._super(key, value);
|
|
||||||
|
|
||||||
var current = this.helper;
|
|
||||||
|
|
||||||
if (!current.hasClass(multipleSortableClass)) return;
|
|
||||||
|
|
||||||
// following the drag element
|
|
||||||
var currentLeft = current.position().left;
|
|
||||||
var currentTop = current.position().top;
|
|
||||||
var currentZIndex = current.css('z-index');
|
|
||||||
var currentPosition = current.css('position');
|
|
||||||
|
|
||||||
var positions = multiSort.sortPositions(this.element, current);
|
|
||||||
|
|
||||||
positions.before.reverse();
|
|
||||||
|
|
||||||
[{'positions': positions.after, type: 'after'},
|
|
||||||
{'positions': positions.before, type: 'before'}]
|
|
||||||
.forEach(function (item) {
|
|
||||||
$.each(item.positions, function (index, elm) {
|
|
||||||
var top;
|
|
||||||
|
|
||||||
if (item.type === 'after') {
|
|
||||||
top = currentTop + ((index + 1) * current.outerHeight());
|
|
||||||
} else {
|
|
||||||
top = currentTop - ((index + 1) * current.outerHeight());
|
|
||||||
}
|
|
||||||
|
|
||||||
elm
|
|
||||||
.addClass("ui-sortable-helper")
|
|
||||||
.css({
|
|
||||||
width: elm.outerWidth(),
|
|
||||||
height: elm.outerHeight(),
|
|
||||||
position: currentPosition,
|
|
||||||
zIndex: currentZIndex,
|
|
||||||
top: top,
|
|
||||||
left: currentLeft
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// it only refresh position the first time because
|
|
||||||
// jquery-ui has saved the old positions of the draggable elements
|
|
||||||
// and with this will remove all elements with dragMultipleActive
|
|
||||||
if (!dragStarted) {
|
|
||||||
dragStarted = true;
|
|
||||||
this.refreshPositions();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}(jQuery))
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,11 +0,0 @@
|
||||||
/*!
|
|
||||||
* jQuery UI Touch Punch 0.2.3
|
|
||||||
*
|
|
||||||
* Copyright 2011–2014, Dave Furfero
|
|
||||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
|
||||||
*
|
|
||||||
* Depends:
|
|
||||||
* jquery.ui.widget.js
|
|
||||||
* jquery.ui.mouse.js
|
|
||||||
*/
|
|
||||||
!function(a){function f(a,b){if(!(a.originalEvent.touches.length>1)){a.preventDefault();var c=a.originalEvent.changedTouches[0],d=document.createEvent("MouseEvents");d.initMouseEvent(b,!0,!0,window,1,c.screenX,c.screenY,c.clientX,c.clientY,!1,!1,!1,!1,0,null),a.target.dispatchEvent(d)}}if(a.support.touch="ontouchend"in document,a.support.touch){var e,b=a.ui.mouse.prototype,c=b._mouseInit,d=b._mouseDestroy;b._touchStart=function(a){var b=this;!e&&b._mouseCapture(a.originalEvent.changedTouches[0])&&(e=!0,b._touchMoved=!1,f(a,"mouseover"),f(a,"mousemove"),f(a,"mousedown"))},b._touchMove=function(a){e&&(this._touchMoved=!0,f(a,"mousemove"))},b._touchEnd=function(a){e&&(f(a,"mouseup"),f(a,"mouseout"),this._touchMoved||f(a,"click"),e=!1)},b._mouseInit=function(){var b=this;b.element.bind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),c.call(b)},b._mouseDestroy=function(){var b=this;b.element.unbind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),d.call(b)}}}(jQuery);
|
|
|
@ -1,5 +1,20 @@
|
||||||
.attachments {
|
.attachments {
|
||||||
margin-bottom: 4rem;
|
margin-bottom: 4rem;
|
||||||
|
.gu-transit {
|
||||||
|
background: $whitish;
|
||||||
|
height: 40px;
|
||||||
|
* {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.gu-mirror {
|
||||||
|
opacity: 1;
|
||||||
|
form {
|
||||||
|
background: lighten($primary, 60%);
|
||||||
|
box-shadow: 1px 1px 10px rgba($black, .1);
|
||||||
|
transition: background .2s ease-in;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.attachments-header {
|
.attachments-header {
|
||||||
|
@ -78,15 +93,6 @@
|
||||||
.single-attachment {
|
.single-attachment {
|
||||||
@extend %small;
|
@extend %small;
|
||||||
background: rgba($white, .9);
|
background: rgba($white, .9);
|
||||||
&.ui-sortable-helper {
|
|
||||||
background: lighten($primary, 60%);
|
|
||||||
box-shadow: 1px 1px 10px rgba($black, .1);
|
|
||||||
transition: background .2s ease-in;
|
|
||||||
}
|
|
||||||
&.sortable-placeholder {
|
|
||||||
background: $whitish;
|
|
||||||
height: 40px;
|
|
||||||
}
|
|
||||||
.attachment-name {
|
.attachment-name {
|
||||||
@extend %bold;
|
@extend %bold;
|
||||||
padding-right: 1rem;
|
padding-right: 1rem;
|
||||||
|
|
|
@ -21,25 +21,34 @@ AttachmentSortableDirective = ($parse) ->
|
||||||
link = (scope, el, attrs) ->
|
link = (scope, el, attrs) ->
|
||||||
callback = $parse(attrs.tgAttachmentsSortable)
|
callback = $parse(attrs.tgAttachmentsSortable)
|
||||||
|
|
||||||
el.sortable({
|
drake = dragula([el[0]], {
|
||||||
items: "div[tg-bind-scope]"
|
copySortSource: false,
|
||||||
handle: ".settings .icon"
|
copy: false,
|
||||||
containment: ".attachments"
|
mirrorContainer: el[0],
|
||||||
dropOnEmpty: true
|
moves: (item) -> return $(item).is('div[tg-bind-scope]')
|
||||||
helper: 'clone'
|
|
||||||
scroll: false
|
|
||||||
tolerance: "pointer"
|
|
||||||
placeholder: "sortable-placeholder single-attachment"
|
|
||||||
})
|
})
|
||||||
|
|
||||||
el.on "sortstop", (event, ui) ->
|
drake.on 'dragend', (item) ->
|
||||||
attachment = ui.item.scope().attachment
|
item = $(item)
|
||||||
newIndex = ui.item.index()
|
|
||||||
|
attachment = item.scope().attachment
|
||||||
|
newIndex = item.index()
|
||||||
|
|
||||||
scope.$apply () ->
|
scope.$apply () ->
|
||||||
callback(scope, {attachment: attachment, index: newIndex})
|
callback(scope, {attachment: attachment, index: newIndex})
|
||||||
|
|
||||||
scope.$on "$destroy", -> el.off()
|
scroll = autoScroll(window, {
|
||||||
|
margin: 20,
|
||||||
|
pixels: 30,
|
||||||
|
scrollWhenOutside: true,
|
||||||
|
autoScroll: () ->
|
||||||
|
return this.down && drake.dragging;
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
scope.$on "$destroy", ->
|
||||||
|
el.off()
|
||||||
|
drake.destroy()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
link: link
|
link: link
|
||||||
|
|
|
@ -21,17 +21,15 @@ SortProjectsDirective = (currentUserService) ->
|
||||||
link = (scope, el, attrs, ctrl) ->
|
link = (scope, el, attrs, ctrl) ->
|
||||||
itemEl = null
|
itemEl = null
|
||||||
|
|
||||||
el.sortable({
|
drake = dragula([el[0]], {
|
||||||
dropOnEmpty: true
|
copySortSource: false,
|
||||||
revert: 200
|
copy: false,
|
||||||
axis: "y"
|
mirrorContainer: el[0],
|
||||||
opacity: .95
|
moves: (item) -> return $(item).hasClass('list-itemtype-project')
|
||||||
placeholder: 'placeholder'
|
|
||||||
cancel: '.project-name'
|
|
||||||
})
|
})
|
||||||
|
|
||||||
el.on "sortstop", (event, ui) ->
|
drake.on 'dragend', (item) ->
|
||||||
itemEl = ui.item
|
itemEl = $(item)
|
||||||
project = itemEl.scope().project
|
project = itemEl.scope().project
|
||||||
index = itemEl.index()
|
index = itemEl.index()
|
||||||
|
|
||||||
|
@ -46,6 +44,18 @@ SortProjectsDirective = (currentUserService) ->
|
||||||
|
|
||||||
currentUserService.bulkUpdateProjectsOrder(sortData)
|
currentUserService.bulkUpdateProjectsOrder(sortData)
|
||||||
|
|
||||||
|
scroll = autoScroll(window, {
|
||||||
|
margin: 20,
|
||||||
|
pixels: 30,
|
||||||
|
scrollWhenOutside: true,
|
||||||
|
autoScroll: () ->
|
||||||
|
return this.down && drake.dragging;
|
||||||
|
})
|
||||||
|
|
||||||
|
scope.$on "$destroy", ->
|
||||||
|
el.off()
|
||||||
|
drake.destroy()
|
||||||
|
|
||||||
directive = {
|
directive = {
|
||||||
scope: {
|
scope: {
|
||||||
projects: "=tgSortProjects"
|
projects: "=tgSortProjects"
|
||||||
|
|
|
@ -49,10 +49,6 @@
|
||||||
color: $gray-light;
|
color: $gray-light;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
}
|
}
|
||||||
.placeholder {
|
|
||||||
background-color: lighten($whitish, 3%);
|
|
||||||
height: 5rem;
|
|
||||||
}
|
|
||||||
.list-itemtype-project {
|
.list-itemtype-project {
|
||||||
background: rgba($white, .6);
|
background: rgba($white, .6);
|
||||||
&:hover {
|
&:hover {
|
||||||
|
@ -96,4 +92,16 @@
|
||||||
width: 1.1rem;
|
width: 1.1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.gu-transit {
|
||||||
|
background-color: lighten($whitish, 3%);
|
||||||
|
height: 5rem;
|
||||||
|
opacity: 1;
|
||||||
|
* {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.gu-mirror {
|
||||||
|
background: lighten($primary, 63%);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
# File: app-meta.service.spec.coffee
|
# File: app-meta.service.spec.coffee
|
||||||
###
|
###
|
||||||
|
|
||||||
|
angular.module("taigaCommon").provider("$exceptionHandler", angular.mock.$ExceptionHandlerProvider)
|
||||||
|
|
||||||
describe "AppMetaService", ->
|
describe "AppMetaService", ->
|
||||||
appMetaService = null
|
appMetaService = null
|
||||||
$rootScope = null
|
$rootScope = null
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
###
|
|
||||||
# Copyright (C) 2014-2016 Taiga Agile LLC <taiga@taiga.io>
|
|
||||||
#
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU Affero General Public License as
|
|
||||||
# published by the Free Software Foundation, either version 3 of the
|
|
||||||
# License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program 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 Affero General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
# File: scope-event.service.coffee
|
|
||||||
###
|
|
||||||
|
|
||||||
class ScopeEvent
|
|
||||||
scopes: {},
|
|
||||||
_searchDuplicatedScopes: (id) ->
|
|
||||||
return _.find Object.keys(@scopes), (key) =>
|
|
||||||
return @scopes[key].$id == id
|
|
||||||
|
|
||||||
_create: (name, scope) ->
|
|
||||||
duplicatedScopeName = @._searchDuplicatedScopes(scope.$id)
|
|
||||||
|
|
||||||
if duplicatedScopeName
|
|
||||||
throw new Error("scopeEvent: this scope is already
|
|
||||||
register with the name \"" + duplicatedScopeName + "\"")
|
|
||||||
|
|
||||||
if @scopes[name]
|
|
||||||
throw new Error("scopeEvent: \"" + name + "\" already in use")
|
|
||||||
else
|
|
||||||
scope._tgEmitter = new EventEmitter2()
|
|
||||||
|
|
||||||
scope.$on "$destroy", () =>
|
|
||||||
scope._tgEmitter.removeAllListeners()
|
|
||||||
delete @scopes[name]
|
|
||||||
|
|
||||||
@scopes[name] = scope
|
|
||||||
|
|
||||||
emitter: (name, scope) ->
|
|
||||||
if scope
|
|
||||||
scope = @._create(name, scope)
|
|
||||||
else if @scopes[name]
|
|
||||||
scope = @scopes[name]
|
|
||||||
else
|
|
||||||
throw new Error("scopeEvent: \"" + name + "\" scope doesn't exist'")
|
|
||||||
|
|
||||||
return scope._tgEmitter
|
|
||||||
|
|
||||||
angular.module("taigaCommon").service("tgScopeEvent", ScopeEvent)
|
|
|
@ -1,102 +0,0 @@
|
||||||
###
|
|
||||||
# Copyright (C) 2014-2016 Taiga Agile LLC <taiga@taiga.io>
|
|
||||||
#
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU Affero General Public License as
|
|
||||||
# published by the Free Software Foundation, either version 3 of the
|
|
||||||
# License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program 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 Affero General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
# File: scope-event.service.spec.coffee
|
|
||||||
###
|
|
||||||
|
|
||||||
angular.module("taigaCommon").provider("$exceptionHandler", angular.mock.$ExceptionHandlerProvider)
|
|
||||||
|
|
||||||
describe "tgScopeEvent", ->
|
|
||||||
scopeEvent = null
|
|
||||||
$rootScope = null
|
|
||||||
|
|
||||||
_inject = ->
|
|
||||||
inject (_tgScopeEvent_, _$rootScope_) ->
|
|
||||||
scopeEvent = _tgScopeEvent_
|
|
||||||
scopeEvent.scopes = {}
|
|
||||||
|
|
||||||
$rootScope = _$rootScope_
|
|
||||||
|
|
||||||
_setup = ->
|
|
||||||
_inject()
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
module "taigaCommon"
|
|
||||||
_setup()
|
|
||||||
|
|
||||||
it "get non-existent emitter", () ->
|
|
||||||
fn = () -> scopeEvent.emitter("test")
|
|
||||||
|
|
||||||
expect(fn).to.throw(Error, "scopeEvent: \"test\" scope doesn't exist'")
|
|
||||||
|
|
||||||
it "create emitter", () ->
|
|
||||||
scope = $rootScope.$new()
|
|
||||||
|
|
||||||
emitter = scopeEvent.emitter("test", scope)
|
|
||||||
|
|
||||||
expect(emitter).to.be.an.instanceof(EventEmitter2)
|
|
||||||
|
|
||||||
it "get emitter", () ->
|
|
||||||
scope = $rootScope.$new()
|
|
||||||
|
|
||||||
scopeEvent.emitter("test", scope)
|
|
||||||
|
|
||||||
emitter = scopeEvent.emitter("test")
|
|
||||||
|
|
||||||
expect(emitter).to.be.an.instanceof(EventEmitter2)
|
|
||||||
|
|
||||||
it "duplicate emitter name", () ->
|
|
||||||
scope = $rootScope.$new()
|
|
||||||
scope2 = $rootScope.$new()
|
|
||||||
|
|
||||||
scopeEvent.emitter("test", scope)
|
|
||||||
|
|
||||||
fn = () -> scopeEvent.emitter("test", scope2)
|
|
||||||
|
|
||||||
expect(fn).to.throw(Error, "scopeEvent: \"test\" already in use")
|
|
||||||
|
|
||||||
it "duplicate scope", () ->
|
|
||||||
scope = $rootScope.$new()
|
|
||||||
|
|
||||||
scopeEvent.emitter("test", scope)
|
|
||||||
|
|
||||||
fn = () -> scopeEvent.emitter("test2", scope)
|
|
||||||
|
|
||||||
expect(fn).to.throw(Error, "scopeEvent: this scope is already register with the name \"test\"")
|
|
||||||
|
|
||||||
|
|
||||||
it "create listener", () ->
|
|
||||||
scope = $rootScope.$new()
|
|
||||||
|
|
||||||
emitter = scopeEvent.emitter("test", scope)
|
|
||||||
emitter.on "test_listener", () -> return
|
|
||||||
|
|
||||||
expect(scope._tgEmitter.listeners("test_listener")[0]).to.be.ok
|
|
||||||
|
|
||||||
it "remove emitter and listeners after scope destroy", () ->
|
|
||||||
scope = $rootScope.$new()
|
|
||||||
|
|
||||||
emitter = scopeEvent.emitter("test", scope)
|
|
||||||
|
|
||||||
emitter.on "test_listener", () -> return
|
|
||||||
|
|
||||||
expect(scope._tgEmitter.listeners("test_listener")).to.have.length(1)
|
|
||||||
|
|
||||||
expect(scopeEvent.scopes['test']).to.be.ok
|
|
||||||
scope.$destroy()
|
|
||||||
|
|
||||||
expect(scopeEvent.scopes['test']).to.be.undefined
|
|
||||||
expect(scope._tgEmitter.listeners("test_listener")).to.have.length(0)
|
|
|
@ -54,5 +54,5 @@ div.row.us-item-row(
|
||||||
div.points(tg-backlog-us-points="us")
|
div.points(tg-backlog-us-points="us")
|
||||||
a.us-points(href="", title="{{'COMMON.FIELDS.POINTS' | translate}}")
|
a.us-points(href="", title="{{'COMMON.FIELDS.POINTS' | translate}}")
|
||||||
|
|
||||||
a(tg-check-permission="modify_us", href="", title="{{'COMMON.DRAG' | translate}}")
|
a.e2e-drag(tg-check-permission="modify_us", href="", title="{{'COMMON.DRAG' | translate}}")
|
||||||
tg-svg(svg-icon="icon-drag")
|
tg-svg(svg-icon="icon-drag")
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
div.kanban-table(tg-kanban-squish-column)
|
div.kanban-table(tg-kanban-squish-column, tg-kanban-sortable)
|
||||||
div.kanban-table-header
|
div.kanban-table-header
|
||||||
div.kanban-table-inner
|
div.kanban-table-inner
|
||||||
h2.task-colum-name(ng-repeat="s in usStatusList track by s.id",
|
h2.task-colum-name(ng-repeat="s in usStatusList track by s.id",
|
||||||
|
@ -66,7 +66,6 @@ div.kanban-table(tg-kanban-squish-column)
|
||||||
div.kanban-table-inner
|
div.kanban-table-inner
|
||||||
div.kanban-uses-box.task-column(ng-class='{vfold:folds[s.id]}',
|
div.kanban-uses-box.task-column(ng-class='{vfold:folds[s.id]}',
|
||||||
ng-repeat="s in usStatusList track by s.id",
|
ng-repeat="s in usStatusList track by s.id",
|
||||||
tg-kanban-sortable,
|
|
||||||
tg-kanban-wip-limit="s",
|
tg-kanban-wip-limit="s",
|
||||||
tg-kanban-column-height-fixer,
|
tg-kanban-column-height-fixer,
|
||||||
tg-bind-scope
|
tg-bind-scope
|
||||||
|
|
|
@ -2,7 +2,7 @@ header(tg-backlog-sprint-header, ng-model="sprint")
|
||||||
|
|
||||||
div.sprint-progress-bar(tg-progress-bar="100 * sprint.closed_points / sprint.total_points")
|
div.sprint-progress-bar(tg-progress-bar="100 * sprint.closed_points / sprint.total_points")
|
||||||
|
|
||||||
div.sprint-table
|
div.sprint-table(tg-bind-scope, ng-class="{'sprint-empty-wrapper': !sprint.user_stories.length}")
|
||||||
div.sprint-empty(ng-if="!sprint.user_stories.length")
|
div.sprint-empty(ng-if="!sprint.user_stories.length")
|
||||||
span(tg-class-permission="{'hidden': 'modify_us'}") {{ 'BACKLOG.SPRINTS.WARNING_EMPTY_SPRINT_ANONYMOUS' | translate }}
|
span(tg-class-permission="{'hidden': 'modify_us'}") {{ 'BACKLOG.SPRINTS.WARNING_EMPTY_SPRINT_ANONYMOUS' | translate }}
|
||||||
span(tg-class-permission="{'hidden': '!modify_us'}") {{ 'BACKLOG.SPRINTS.WARNING_EMPTY_SPRINT' | translate }}
|
span(tg-class-permission="{'hidden': '!modify_us'}") {{ 'BACKLOG.SPRINTS.WARNING_EMPTY_SPRINT' | translate }}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
div.taskboard-table(tg-taskboard-squish-column)
|
div.taskboard-table(tg-taskboard-squish-column, tg-taskboard-sortable)
|
||||||
div.taskboard-table-header
|
div.taskboard-table-header
|
||||||
div.taskboard-table-inner
|
div.taskboard-table-inner
|
||||||
h2.task-colum-name(translate="TASKBOARD.TABLE.COLUMN")
|
h2.task-colum-name(translate="TASKBOARD.TABLE.COLUMN")
|
||||||
|
@ -49,7 +49,7 @@ div.taskboard-table(tg-taskboard-squish-column)
|
||||||
span(translate="TASKBOARD.TABLE.FIELD_POINTS")
|
span(translate="TASKBOARD.TABLE.FIELD_POINTS")
|
||||||
include ../components/addnewtask
|
include ../components/addnewtask
|
||||||
|
|
||||||
div.taskboard-tasks-box.task-column(ng-repeat="st in taskStatusList track by st.id", tg-taskboard-sortable, class="squish-status-{{st.id}}", ng-class="{'column-fold':statusesFolded[st.id]}", tg-bind-scope)
|
div.taskboard-tasks-box.task-column(ng-repeat="st in taskStatusList track by st.id", class="squish-status-{{st.id}}", ng-class="{'column-fold':statusesFolded[st.id]}", tg-bind-scope)
|
||||||
div.taskboard-task(
|
div.taskboard-task(
|
||||||
ng-repeat="task in usTasks[us.id][st.id] track by task.id"
|
ng-repeat="task in usTasks[us.id][st.id] track by task.id"
|
||||||
tg-bind-scope
|
tg-bind-scope
|
||||||
|
@ -82,7 +82,7 @@ div.taskboard-table(tg-taskboard-squish-column)
|
||||||
h3.us-title
|
h3.us-title
|
||||||
span(translate="TASKBOARD.TABLE.ROW_UNASSIGED_TASKS_TITLE")
|
span(translate="TASKBOARD.TABLE.ROW_UNASSIGED_TASKS_TITLE")
|
||||||
include ../components/addnewtask.jade
|
include ../components/addnewtask.jade
|
||||||
div.taskboard-tasks-box.task-column(ng-repeat="st in taskStatusList track by st.id", tg-taskboard-sortable, class="squish-status-{{st.id}}", ng-class="{'column-fold':statusesFolded[st.id]}", tg-bind-scope)
|
div.taskboard-tasks-box.task-column(ng-repeat="st in taskStatusList track by st.id", class="squish-status-{{st.id}}", ng-class="{'column-fold':statusesFolded[st.id]}", tg-bind-scope)
|
||||||
div.taskboard-task(
|
div.taskboard-task(
|
||||||
ng-repeat="task in usTasks[null][st.id] track by task.id"
|
ng-repeat="task in usTasks[null][st.id] track by task.id"
|
||||||
tg-bind-scope
|
tg-bind-scope
|
||||||
|
|
|
@ -16,13 +16,11 @@
|
||||||
transition: color .3s linear, opacity .3s linear;
|
transition: color .3s linear, opacity .3s linear;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.ui-sortable-helper {
|
&.gu-mirror {
|
||||||
box-shadow: 1px 1px 15px rgba($black, .4);
|
box-shadow: 1px 1px 15px rgba($black, .4);
|
||||||
|
opacity: 1;
|
||||||
transition: box-shadow .3s linear;
|
transition: box-shadow .3s linear;
|
||||||
}
|
}
|
||||||
&.ui-sortable-placeholder {
|
|
||||||
background: $grayer;
|
|
||||||
}
|
|
||||||
&.blocked {
|
&.blocked {
|
||||||
background: $red;
|
background: $red;
|
||||||
border: 1px solid darken($red, 10%);
|
border: 1px solid darken($red, 10%);
|
||||||
|
|
|
@ -14,13 +14,10 @@
|
||||||
transition: color .3s linear, opacity .3s linear;
|
transition: color .3s linear, opacity .3s linear;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.ui-sortable-helper {
|
&.gu-mirror {
|
||||||
box-shadow: 1px 1px 15px rgba($black, .4);
|
box-shadow: 1px 1px 15px rgba($black, .4);
|
||||||
transition: box-shadow .3s linear;
|
transition: box-shadow .3s linear;
|
||||||
}
|
}
|
||||||
&.ui-sortable-placeholder {
|
|
||||||
background: $grayer;
|
|
||||||
}
|
|
||||||
.blocked {
|
.blocked {
|
||||||
background: $red;
|
background: $red;
|
||||||
border: 1px solid darken($red, 10%);
|
border: 1px solid darken($red, 10%);
|
||||||
|
|
|
@ -88,3 +88,39 @@ svg {
|
||||||
max-width: 2rem;
|
max-width: 2rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// scss-lint:disable QualifyingElement
|
||||||
|
div.awesomplete {
|
||||||
|
> ul {
|
||||||
|
background: rgba($black, .95);
|
||||||
|
color: $primary-light;
|
||||||
|
top: 2.25rem;
|
||||||
|
transition: all .2s ease;
|
||||||
|
&::before {
|
||||||
|
background: rgba($black, .95);
|
||||||
|
}
|
||||||
|
&[hidden] {
|
||||||
|
position: absolute;
|
||||||
|
top: 1.5rem;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
> li {
|
||||||
|
&:hover {
|
||||||
|
background: $primary-light;
|
||||||
|
color: $black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mark {
|
||||||
|
background: $primary-light;
|
||||||
|
color: $black;
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
&:hover {
|
||||||
|
mark {
|
||||||
|
background: $primary-light;
|
||||||
|
color: $black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -27,3 +27,25 @@
|
||||||
margin-left: .2rem;
|
margin-left: .2rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.backlog-us-mirror {
|
||||||
|
background: $white;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 2px 2px 5px $gray;
|
||||||
|
min-height: calc(40px + 1rem);
|
||||||
|
opacity: .9;
|
||||||
|
padding: 1rem;
|
||||||
|
.tags-block,
|
||||||
|
.votes,
|
||||||
|
.us-settings,
|
||||||
|
.status,
|
||||||
|
.points,
|
||||||
|
.icon-drag,
|
||||||
|
input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
&.is-checked,
|
||||||
|
&:hover {
|
||||||
|
background: $white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -165,7 +165,7 @@
|
||||||
&:last-child {
|
&:last-child {
|
||||||
border-bottom: 0;
|
border-bottom: 0;
|
||||||
}
|
}
|
||||||
&.ui-sortable-helper {
|
&.gu-mirror {
|
||||||
background: lighten($primary, 60%);
|
background: lighten($primary, 60%);
|
||||||
box-shadow: 1px 1px 10px rgba($black, .1);
|
box-shadow: 1px 1px 10px rgba($black, .1);
|
||||||
opacity: .9;
|
opacity: .9;
|
||||||
|
@ -183,10 +183,13 @@
|
||||||
width: .7rem;
|
width: .7rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.sortable-placeholder {
|
.gu-transit {
|
||||||
background: $whitish;
|
background: $whitish;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
* {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.row-selected,
|
.row-selected,
|
||||||
.is-checked {
|
.is-checked {
|
||||||
|
@ -301,6 +304,9 @@
|
||||||
@extend %light;
|
@extend %light;
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
.row {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
img {
|
img {
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,7 +158,7 @@
|
||||||
text-align: left;
|
text-align: left;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
&:hover {
|
&:hover {
|
||||||
background: lighten($gray-light, 12%);
|
background: rgba($gray-light, .2);
|
||||||
cursor: move;
|
cursor: move;
|
||||||
transition: background .2s ease-in;
|
transition: background .2s ease-in;
|
||||||
}
|
}
|
||||||
|
@ -168,10 +168,6 @@
|
||||||
&.readonly {
|
&.readonly {
|
||||||
cursor: auto;
|
cursor: auto;
|
||||||
}
|
}
|
||||||
&.sortable-placeholder {
|
|
||||||
background: lighten($gray-light, 12%);
|
|
||||||
height: 40px;
|
|
||||||
}
|
|
||||||
&.ui-sortable-helper {
|
&.ui-sortable-helper {
|
||||||
background: lighten($primary, 60%);
|
background: lighten($primary, 60%);
|
||||||
box-shadow: 1px 1px 10px rgba($black, .1);
|
box-shadow: 1px 1px 10px rgba($black, .1);
|
||||||
|
@ -180,6 +176,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
.gu-transit {
|
||||||
|
background: lighten($gray-light, 12%);
|
||||||
|
height: 40px;
|
||||||
|
* {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
.column-us {
|
.column-us {
|
||||||
@extend %small;
|
@extend %small;
|
||||||
flex-flow: 3;
|
flex-flow: 3;
|
||||||
|
@ -206,6 +209,11 @@
|
||||||
color: $red;
|
color: $red;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&.sprint-empty-wrapper { // hide dragging items
|
||||||
|
.row {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.button-gray {
|
.button-gray {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
|
@ -25,9 +25,6 @@ $column-margin: 0 10px 0 0;
|
||||||
.icon {
|
.icon {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
&.ui-sortable-helper {
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
&.task-column,
|
&.task-column,
|
||||||
.task-column {
|
.task-column {
|
||||||
|
@ -54,6 +51,12 @@ $column-margin: 0 10px 0 0;
|
||||||
&.readonly {
|
&.readonly {
|
||||||
cursor: auto;
|
cursor: auto;
|
||||||
}
|
}
|
||||||
|
&.gu-mirror {
|
||||||
|
opacity: 1;
|
||||||
|
.avatar-task-link {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -141,4 +141,13 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.gu-mirror {
|
||||||
|
background: lighten($primary, 60%);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.gu-transit {
|
||||||
|
* {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
15
bower.json
15
bower.json
|
@ -56,10 +56,6 @@
|
||||||
"angular-sanitize": "1.4.7",
|
"angular-sanitize": "1.4.7",
|
||||||
"checksley": "~0.6.0",
|
"checksley": "~0.6.0",
|
||||||
"jquery": "~2.1.1",
|
"jquery": "~2.1.1",
|
||||||
"select2": "~3.4.8",
|
|
||||||
"angular-ui-select2": "~0.0.5",
|
|
||||||
"google-diff-match-patch-js": "~1.0.0",
|
|
||||||
"underscore.string": "~2.3.3",
|
|
||||||
"markitup-1x": "~1.1.14",
|
"markitup-1x": "~1.1.14",
|
||||||
"jquery-textcomplete": "yuku-t/jquery-textcomplete#~0.7",
|
"jquery-textcomplete": "yuku-t/jquery-textcomplete#~0.7",
|
||||||
"flot-axislabels": "markrcote/flot-axislabels",
|
"flot-axislabels": "markrcote/flot-axislabels",
|
||||||
|
@ -67,10 +63,7 @@
|
||||||
"flot.tooltip": "~0.8.4",
|
"flot.tooltip": "~0.8.4",
|
||||||
"Sortable": "~0.1.8",
|
"Sortable": "~0.1.8",
|
||||||
"moment": "~2.10.6",
|
"moment": "~2.10.6",
|
||||||
"isMobile": "~0.3.1",
|
|
||||||
"favico.js": "0.3.9",
|
|
||||||
"pikaday": "~1.4.0",
|
"pikaday": "~1.4.0",
|
||||||
"malihu-custom-scrollbar-plugin": "~3.1.0",
|
|
||||||
"raven-js": "~1.1.16",
|
"raven-js": "~1.1.16",
|
||||||
"l.js": "~0.1.0",
|
"l.js": "~0.1.0",
|
||||||
"angular-translate": "~2.8.1",
|
"angular-translate": "~2.8.1",
|
||||||
|
@ -78,17 +71,19 @@
|
||||||
"angular-translate-loader-static-files": "~2.8.1",
|
"angular-translate-loader-static-files": "~2.8.1",
|
||||||
"angular-translate-interpolation-messageformat": "~2.8.1",
|
"angular-translate-interpolation-messageformat": "~2.8.1",
|
||||||
"ngInfiniteScroll": "1.2.1",
|
"ngInfiniteScroll": "1.2.1",
|
||||||
"eventemitter2": "~0.4.14",
|
|
||||||
"immutable": "~3.7.6",
|
"immutable": "~3.7.6",
|
||||||
"bluebird": "~2.10.2",
|
"bluebird": "~2.10.2",
|
||||||
"intro.js": "~1.1.1",
|
"intro.js": "~1.1.1",
|
||||||
"lodash": "~4.0.0"
|
"lodash": "~4.0.0",
|
||||||
|
"messageformat": "^0.1.8",
|
||||||
|
"dragula.js": "dragula#^3.6.6"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"lodash": "~4.0.0",
|
"lodash": "~4.0.0",
|
||||||
"moment": "~2.10.6",
|
"moment": "~2.10.6",
|
||||||
"jquery": "~2.1.1",
|
"jquery": "~2.1.1",
|
||||||
"angular": "1.4.7"
|
"angular": "1.4.7",
|
||||||
|
"messageformat": "0.1.8"
|
||||||
},
|
},
|
||||||
"private": true
|
"private": true
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,9 +22,9 @@ exports.config = {
|
||||||
compilers: 'js:babel-register',
|
compilers: 'js:babel-register',
|
||||||
require: 'babel-polyfill'
|
require: 'babel-polyfill'
|
||||||
},
|
},
|
||||||
capabilities: {
|
// capabilities: {
|
||||||
'browserName': 'firefox'
|
// 'browserName': 'firefox'
|
||||||
},
|
// },
|
||||||
// capabilities: {
|
// capabilities: {
|
||||||
// browserName: 'internet explorer',
|
// browserName: 'internet explorer',
|
||||||
// version: '11'
|
// version: '11'
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
var exports = module.exports = {};
|
var exports = module.exports = {};
|
||||||
var fs = require("fs");
|
var fs = require("fs");
|
||||||
|
var gutil = require('gulp-util');
|
||||||
|
|
||||||
var Theme = function() {
|
var Theme = function() {
|
||||||
var defaultTheme = "taiga";
|
var defaultTheme = "taiga";
|
||||||
|
@ -72,3 +73,88 @@ exports.themes = {
|
||||||
return Theme();
|
return Theme();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.unusedCss = function(options) {
|
||||||
|
var through = require('through2');
|
||||||
|
var css = require('css');
|
||||||
|
var path = require('path');
|
||||||
|
|
||||||
|
|
||||||
|
var content = fs.readFileSync(options.css, "utf8");
|
||||||
|
var ast = css.parse(content, {});
|
||||||
|
|
||||||
|
var files = [];
|
||||||
|
|
||||||
|
var validsSelectors = [];
|
||||||
|
|
||||||
|
ast.stylesheet.rules.forEach(function(rule) {
|
||||||
|
if (rule.selectors) {
|
||||||
|
rule.selectors.forEach(function(selectorRule) {
|
||||||
|
var selectors = selectorRule.split(" ");
|
||||||
|
|
||||||
|
selectors.forEach(function(selector) {
|
||||||
|
var valid = false;
|
||||||
|
|
||||||
|
if (selector.slice(0, 2) === 'tg') {
|
||||||
|
valid = true;
|
||||||
|
} else if (selector[0] === '.') {
|
||||||
|
valid = true;
|
||||||
|
|
||||||
|
selector = '.' + selector.split('.')[1]; // '.class1.class2 -> .class1'
|
||||||
|
}
|
||||||
|
|
||||||
|
selector = selector.split('::')[0];
|
||||||
|
selector = selector.split(':')[0];
|
||||||
|
|
||||||
|
if(valid && validsSelectors.indexOf(selector) === -1) {
|
||||||
|
validsSelectors.push(selector);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var addFile = function(file, encoding, cb) {
|
||||||
|
files.push(file);
|
||||||
|
cb();
|
||||||
|
};
|
||||||
|
|
||||||
|
var searchUnusedCss = function(cb) {
|
||||||
|
var invalid = [];
|
||||||
|
|
||||||
|
validsSelectors.forEach(function(validSelector) {
|
||||||
|
var finded = false;
|
||||||
|
|
||||||
|
files.every(function(file) {
|
||||||
|
var content = file.contents.toString();
|
||||||
|
var ext = path.extname(file.path);
|
||||||
|
var pattern = validSelector;
|
||||||
|
|
||||||
|
if (ext === '.html') {
|
||||||
|
pattern = validSelector.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(content.indexOf(pattern) !== -1) {
|
||||||
|
finded = true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!finded) {
|
||||||
|
invalid.push(validSelector);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
for(var i = 0; i < invalid.length; i++) {
|
||||||
|
gutil.log(gutil.colors.magenta(invalid[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
cb();
|
||||||
|
};
|
||||||
|
|
||||||
|
return through.obj(addFile, searchUnusedCss);
|
||||||
|
};
|
||||||
|
|
58
gulpfile.js
58
gulpfile.js
|
@ -27,9 +27,10 @@ var gulp = require("gulp"),
|
||||||
del = require("del"),
|
del = require("del"),
|
||||||
livereload = require('gulp-livereload'),
|
livereload = require('gulp-livereload'),
|
||||||
gulpFilter = require('gulp-filter'),
|
gulpFilter = require('gulp-filter'),
|
||||||
addsrc = require('gulp-add-src');
|
|
||||||
mergeStream = require('merge-stream'),
|
mergeStream = require('merge-stream'),
|
||||||
path = require('path'),
|
path = require('path'),
|
||||||
|
addsrc = require('gulp-add-src'),
|
||||||
|
jsonminify = require('gulp-jsonminify'),
|
||||||
coffeelint = require('gulp-coffeelint');
|
coffeelint = require('gulp-coffeelint');
|
||||||
|
|
||||||
var argv = require('minimist')(process.argv.slice(2));
|
var argv = require('minimist')(process.argv.slice(2));
|
||||||
|
@ -51,6 +52,7 @@ paths.distVersion = paths.dist + version + "/";
|
||||||
paths.tmp = "tmp/";
|
paths.tmp = "tmp/";
|
||||||
paths.extras = "extras/";
|
paths.extras = "extras/";
|
||||||
paths.vendor = "vendor/";
|
paths.vendor = "vendor/";
|
||||||
|
paths.modules = "node_modules/";
|
||||||
|
|
||||||
paths.jade = [
|
paths.jade = [
|
||||||
paths.app + "**/*.jade"
|
paths.app + "**/*.jade"
|
||||||
|
@ -66,8 +68,10 @@ paths.htmlPartials = [
|
||||||
paths.images = paths.app + "images/**/*";
|
paths.images = paths.app + "images/**/*";
|
||||||
paths.svg = paths.app + "svg/**/*";
|
paths.svg = paths.app + "svg/**/*";
|
||||||
paths.css_vendor = [
|
paths.css_vendor = [
|
||||||
paths.app + "styles/vendor/*.css",
|
paths.vendor + "intro.js/introjs.css",
|
||||||
paths.vendor + "intro.js/introjs.css"
|
paths.vendor + "dragula.js/dist/dragula.css",
|
||||||
|
paths.modules + "awesomplete/awesomplete.css",
|
||||||
|
paths.app + "styles/vendor/*.css"
|
||||||
];
|
];
|
||||||
paths.locales = paths.app + "locales/**/*.json";
|
paths.locales = paths.app + "locales/**/*.json";
|
||||||
paths.modulesLocales = paths.app + "modules/**/locales/*.json";
|
paths.modulesLocales = paths.app + "modules/**/locales/*.json";
|
||||||
|
@ -157,7 +161,7 @@ paths.libs = [
|
||||||
paths.vendor + "angular-translate-loader-partial/angular-translate-loader-partial.js",
|
paths.vendor + "angular-translate-loader-partial/angular-translate-loader-partial.js",
|
||||||
paths.vendor + "angular-translate-loader-static-files/angular-translate-loader-static-files.js",
|
paths.vendor + "angular-translate-loader-static-files/angular-translate-loader-static-files.js",
|
||||||
paths.vendor + "angular-translate-interpolation-messageformat/angular-translate-interpolation-messageformat.js",
|
paths.vendor + "angular-translate-interpolation-messageformat/angular-translate-interpolation-messageformat.js",
|
||||||
paths.vendor + "moment/min/moment-with-locales.js",
|
paths.vendor + "moment/moment.js",
|
||||||
paths.vendor + "checksley/checksley.js",
|
paths.vendor + "checksley/checksley.js",
|
||||||
paths.vendor + "pikaday/pikaday.js",
|
paths.vendor + "pikaday/pikaday.js",
|
||||||
paths.vendor + "jquery-flot/jquery.flot.js",
|
paths.vendor + "jquery-flot/jquery.flot.js",
|
||||||
|
@ -167,20 +171,19 @@ paths.libs = [
|
||||||
paths.vendor + "flot.tooltip/js/jquery.flot.tooltip.js",
|
paths.vendor + "flot.tooltip/js/jquery.flot.tooltip.js",
|
||||||
paths.vendor + "jquery-textcomplete/dist/jquery.textcomplete.js",
|
paths.vendor + "jquery-textcomplete/dist/jquery.textcomplete.js",
|
||||||
paths.vendor + "markitup-1x/markitup/jquery.markitup.js",
|
paths.vendor + "markitup-1x/markitup/jquery.markitup.js",
|
||||||
paths.vendor + "malihu-custom-scrollbar-plugin/jquery.mCustomScrollbar.concat.min.js",
|
|
||||||
paths.vendor + "raven-js/dist/raven.js",
|
paths.vendor + "raven-js/dist/raven.js",
|
||||||
paths.vendor + "l.js/l.js",
|
paths.vendor + "l.js/l.js",
|
||||||
paths.vendor + "messageformat/locale/*.js",
|
paths.vendor + "messageformat/locale/*.js",
|
||||||
paths.vendor + "ngInfiniteScroll/build/ng-infinite-scroll.js",
|
paths.vendor + "ngInfiniteScroll/build/ng-infinite-scroll.js",
|
||||||
paths.vendor + "eventemitter2/lib/eventemitter2.js",
|
|
||||||
paths.vendor + "immutable/dist/immutable.js",
|
paths.vendor + "immutable/dist/immutable.js",
|
||||||
paths.vendor + "intro.js/intro.js",
|
paths.vendor + "intro.js/intro.js",
|
||||||
paths.app + "js/jquery.ui.git-custom.js",
|
paths.vendor + "dragula.js/dist/dragula.js",
|
||||||
paths.app + "js/jquery-ui.drag-multiple-custom.js",
|
paths.app + "js/dom-autoscroller.js",
|
||||||
paths.app + "js/jquery.ui.touch-punch.min.js",
|
paths.app + "js/dragula-drag-multiple.js",
|
||||||
paths.app + "js/tg-repeat.js",
|
paths.app + "js/tg-repeat.js",
|
||||||
paths.app + "js/sha1-custom.js",
|
paths.app + "js/sha1-custom.js",
|
||||||
paths.app + "js/murmurhash3_gc.js"
|
paths.app + "js/murmurhash3_gc.js",
|
||||||
|
paths.modules + "awesomplete/awesomplete.js"
|
||||||
];
|
];
|
||||||
|
|
||||||
var isDeploy = argv["_"].indexOf("deploy") !== -1;
|
var isDeploy = argv["_"].indexOf("deploy") !== -1;
|
||||||
|
@ -218,6 +221,7 @@ gulp.task("copy-index", function() {
|
||||||
gulp.task("template-cache", function() {
|
gulp.task("template-cache", function() {
|
||||||
return gulp.src(paths.htmlPartials)
|
return gulp.src(paths.htmlPartials)
|
||||||
.pipe(templateCache({standalone: true}))
|
.pipe(templateCache({standalone: true}))
|
||||||
|
.pipe(gulpif(isDeploy, uglify()))
|
||||||
.pipe(gulp.dest(paths.distVersion + "js/"))
|
.pipe(gulp.dest(paths.distVersion + "js/"))
|
||||||
.pipe(gulpif(!isDeploy, livereload()));
|
.pipe(gulpif(!isDeploy, livereload()));
|
||||||
});
|
});
|
||||||
|
@ -378,6 +382,7 @@ gulp.task("app-loader", function() {
|
||||||
return gulp.src("app-loader/app-loader.coffee")
|
return gulp.src("app-loader/app-loader.coffee")
|
||||||
.pipe(replace("___VERSION___", version))
|
.pipe(replace("___VERSION___", version))
|
||||||
.pipe(coffee())
|
.pipe(coffee())
|
||||||
|
.pipe(gulpif(isDeploy, uglify()))
|
||||||
.pipe(gulp.dest(paths.distVersion + "js/"));
|
.pipe(gulp.dest(paths.distVersion + "js/"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -391,7 +396,10 @@ gulp.task("locales", function() {
|
||||||
var pluginFolder = pluginPath.split('/').pop();
|
var pluginFolder = pluginPath.split('/').pop();
|
||||||
|
|
||||||
localeFile.dirname = pluginFolder;
|
localeFile.dirname = pluginFolder;
|
||||||
}))
|
}));
|
||||||
|
|
||||||
|
return gulp.src(paths.locales)
|
||||||
|
.pipe(gulpif(isDeploy, jsonminify()))
|
||||||
.pipe(gulp.dest(paths.distVersion + "locales"));
|
.pipe(gulp.dest(paths.distVersion + "locales"));
|
||||||
|
|
||||||
var core = gulp.src(paths.locales)
|
var core = gulp.src(paths.locales)
|
||||||
|
@ -439,6 +447,12 @@ gulp.task("coffee", function() {
|
||||||
.pipe(livereload());
|
.pipe(livereload());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
gulp.task("moment-locales", function() {
|
||||||
|
return gulp.src(paths.vendor + "moment/locale/*")
|
||||||
|
.pipe(gulpif(isDeploy, uglify()))
|
||||||
|
.pipe(gulp.dest(paths.distVersion + "locales/moment-locales/"));
|
||||||
|
});
|
||||||
|
|
||||||
gulp.task("jslibs-watch", function() {
|
gulp.task("jslibs-watch", function() {
|
||||||
return gulp.src(paths.libs)
|
return gulp.src(paths.libs)
|
||||||
.pipe(plumber())
|
.pipe(plumber())
|
||||||
|
@ -451,17 +465,17 @@ gulp.task("jslibs-deploy", function() {
|
||||||
.pipe(plumber())
|
.pipe(plumber())
|
||||||
.pipe(sourcemaps.init())
|
.pipe(sourcemaps.init())
|
||||||
.pipe(concat("libs.js"))
|
.pipe(concat("libs.js"))
|
||||||
.pipe(uglify({mangle:false, preserveComments: false}))
|
.pipe(uglify())
|
||||||
.pipe(sourcemaps.write("./maps"))
|
.pipe(sourcemaps.write("./maps"))
|
||||||
.pipe(gulp.dest(paths.distVersion + "js/"));
|
.pipe(gulp.dest(paths.distVersion + "js/"));
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task("app-watch", ["coffee", "conf", "locales", "app-loader"]);
|
gulp.task("app-watch", ["coffee", "conf", "locales", "moment-locales", "app-loader"]);
|
||||||
|
|
||||||
gulp.task("app-deploy", ["coffee", "conf", "locales", "app-loader"], function() {
|
gulp.task("app-deploy", ["coffee", "conf", "locales", "moment-locales", "app-loader"], function() {
|
||||||
return gulp.src(paths.distVersion + "js/app.js")
|
return gulp.src(paths.distVersion + "js/app.js")
|
||||||
.pipe(sourcemaps.init())
|
.pipe(sourcemaps.init())
|
||||||
.pipe(uglify({mangle:false, preserveComments: false}))
|
.pipe(uglify())
|
||||||
.pipe(sourcemaps.write("./maps"))
|
.pipe(sourcemaps.write("./maps"))
|
||||||
.pipe(gulp.dest(paths.distVersion + "js/"));
|
.pipe(gulp.dest(paths.distVersion + "js/"));
|
||||||
});
|
});
|
||||||
|
@ -531,10 +545,24 @@ gulp.task("delete-tmp", function() {
|
||||||
del.sync(paths.tmp);
|
del.sync(paths.tmp);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
gulp.task("unused-css", ["default"], function() {
|
||||||
|
return gulp.src([
|
||||||
|
paths.distVersion + "js/app.js",
|
||||||
|
paths.tmp + "**/*.html"
|
||||||
|
])
|
||||||
|
.pipe(utils.unusedCss({
|
||||||
|
css: paths.distVersion + "styles/theme-taiga.css"
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
gulp.task("express", function() {
|
gulp.task("express", function() {
|
||||||
var express = require("express");
|
var express = require("express");
|
||||||
|
var compression = require('compression');
|
||||||
|
|
||||||
var app = express();
|
var app = express();
|
||||||
|
|
||||||
|
app.use(compression()); //gzip
|
||||||
|
|
||||||
app.use("/" + version + "/js", express.static(__dirname + "/dist/" + version + "/js"));
|
app.use("/" + version + "/js", express.static(__dirname + "/dist/" + version + "/js"));
|
||||||
app.use("/" + version + "/styles", express.static(__dirname + "/dist/" + version + "/styles"));
|
app.use("/" + version + "/styles", express.static(__dirname + "/dist/" + version + "/styles"));
|
||||||
app.use("/" + version + "/images", express.static(__dirname + "/dist/" + version + "/images"));
|
app.use("/" + version + "/images", express.static(__dirname + "/dist/" + version + "/images"));
|
||||||
|
|
15
package.json
15
package.json
|
@ -32,10 +32,12 @@
|
||||||
"chai-jquery": "^2.0.0",
|
"chai-jquery": "^2.0.0",
|
||||||
"cli-color": "^1.0.0",
|
"cli-color": "^1.0.0",
|
||||||
"coffee-script": "^1.9.1",
|
"coffee-script": "^1.9.1",
|
||||||
|
"compression": "^1.6.1",
|
||||||
"connect-livereload": "^0.5.4",
|
"connect-livereload": "^0.5.4",
|
||||||
|
"css": "^2.2.1",
|
||||||
"del": "^2.0.2",
|
"del": "^2.0.2",
|
||||||
"express": "^4.12.0",
|
"express": "^4.12.0",
|
||||||
"glob": "^5.0.14",
|
"glob": "^5.0.15",
|
||||||
"gulp": "^3.8.11",
|
"gulp": "^3.8.11",
|
||||||
"gulp-add-src": "^0.2.0",
|
"gulp-add-src": "^0.2.0",
|
||||||
"gulp-angular-templatecache": "^1.5.0",
|
"gulp-angular-templatecache": "^1.5.0",
|
||||||
|
@ -53,6 +55,7 @@
|
||||||
"gulp-insert": "^0.5.0",
|
"gulp-insert": "^0.5.0",
|
||||||
"gulp-jade": "^1.0.0",
|
"gulp-jade": "^1.0.0",
|
||||||
"gulp-jade-inheritance": "0.5.3",
|
"gulp-jade-inheritance": "0.5.3",
|
||||||
|
"gulp-jsonminify": "^1.0.0",
|
||||||
"gulp-livereload": "^3.8.1",
|
"gulp-livereload": "^3.8.1",
|
||||||
"gulp-minify-css": "^0.4.6",
|
"gulp-minify-css": "^0.4.6",
|
||||||
"gulp-order": "^1.1.1",
|
"gulp-order": "^1.1.1",
|
||||||
|
@ -62,9 +65,11 @@
|
||||||
"gulp-replace": "^0.5.3",
|
"gulp-replace": "^0.5.3",
|
||||||
"gulp-sass": "^2.0.4",
|
"gulp-sass": "^2.0.4",
|
||||||
"gulp-scss-lint": "0.3.6",
|
"gulp-scss-lint": "0.3.6",
|
||||||
|
"gulp-size": "^2.0.0",
|
||||||
"gulp-sourcemaps": "^1.5.0",
|
"gulp-sourcemaps": "^1.5.0",
|
||||||
"gulp-template": "^3.0.0",
|
"gulp-template": "^3.0.0",
|
||||||
"gulp-uglify": "~1.4.1",
|
"gulp-uglify": "~1.4.1",
|
||||||
|
"gulp-util": "^3.0.7",
|
||||||
"gulp-wrap": "^0.11.0",
|
"gulp-wrap": "^0.11.0",
|
||||||
"image-size": "^0.3.5",
|
"image-size": "^0.3.5",
|
||||||
"inquirer": "^0.10.0",
|
"inquirer": "^0.10.0",
|
||||||
|
@ -84,10 +89,14 @@
|
||||||
"readable-stream": "~2.0.2",
|
"readable-stream": "~2.0.2",
|
||||||
"run-sequence": "^1.0.2",
|
"run-sequence": "^1.0.2",
|
||||||
"sinon": "^1.14.1",
|
"sinon": "^1.14.1",
|
||||||
"through2": "~2.0.0",
|
"through2": "^2.0.1",
|
||||||
"vinyl-fs": "^2.1.1"
|
"vinyl-fs": "^2.1.1"
|
||||||
},
|
},
|
||||||
"pre-commit": [
|
"pre-commit": [
|
||||||
"scss-lint"
|
"scss-lint"
|
||||||
]
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"awesomplete": "^1.0.0",
|
||||||
|
"dom-autoscroller": "^1.2.3"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue