Merge branch 'master' into stable

stable
Jesús Espino 2014-10-20 13:44:33 +02:00
commit c390c55252
64 changed files with 4291 additions and 4326 deletions

1
.gitignore vendored
View File

@ -11,3 +11,4 @@ tags
tmp/
conf/
app/config/main.coffee
app/plugins/taiga-front-extras

View File

@ -1,5 +1,15 @@
# Changelog #
## 1.2.0 (Unreleased)
### Features
- Multiple User stories Drag & Drop in the backlog.
- Added beta ribbon.
### Misc
- Lots of small and not so small bugfixes
## 1.1.0 (2014-10-13)
### Misc ###

View File

@ -114,6 +114,8 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
{templateUrl: "/partials/mail-notifications.html"})
$routeProvider.when("/change-email/:email_token",
{templateUrl: "/partials/change-email.html"})
$routeProvider.when("/cancel-account/:cancel_token",
{templateUrl: "/partials/cancel-account.html"})
# Auth
$routeProvider.when("/login",

View File

@ -51,6 +51,7 @@ class RolesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fil
@scope.sectionName = "Permissions" #i18n
@scope.project = {}
@scope.anyComputableRole = true
promise = @.loadInitialData()
@ -67,6 +68,8 @@ class RolesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fil
return @rs.projects.get(@scope.projectId).then (project) =>
@scope.project = project
@scope.$emit('project:loaded', project)
@scope.anyComputableRole = _.some(_.map(project.roles, (point) -> point.computable))
return project
loadRoles: ->
@ -104,6 +107,7 @@ class RolesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fil
return @confirm.askChoice(title, subtitle, choices).then (response) =>
promise = @repo.remove(@scope.role, {moveTo: response.selected})
promise.then =>
@.loadProject()
@.loadRoles().finally ->
response.finish()
promise.then null, =>
@ -112,6 +116,7 @@ class RolesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fil
setComputable: debounce 2000, ->
onSuccess = =>
@confirm.notify("success")
@.loadProject()
onError = =>
@confirm.notify("error")
@ -168,6 +173,7 @@ NewRoleDirective = ($tgrepo, $confirm) ->
$scope.roles.push(role)
$ctrl.setRole(role)
$el.find(".add-button").show()
$ctrl.loadProject()
onError = ->
$confirm.notify("error")
@ -326,6 +332,7 @@ RolePermissionsDirective = ($rootscope, $repo, $confirm) ->
renderResume(target.parents(".category-config"), categories[categoryId])
$rootscope.$broadcast("projects:reload")
$confirm.notify("success")
$ctrl.loadProject()
onError = ->
$confirm.notify("error")

View File

@ -137,6 +137,10 @@ class AuthService extends taiga.Service
data = _.clone(data, false)
return @http.post(url, data)
cancelAccount: (data) ->
url = @urls.resolve("users-cancel-account")
data = _.clone(data, false)
return @http.post(url, data)
module.service("$tgAuth", AuthService)
@ -458,3 +462,42 @@ ChangeEmailDirective = ($repo, $model, $auth, $confirm, $location, $params, $nav
module.directive("tgChangeEmail", ["$tgRepo", "$tgModel", "$tgAuth", "$tgConfirm", "$tgLocation", "$routeParams",
"$tgNavUrls", ChangeEmailDirective])
#############################################################################
## Cancel account
#############################################################################
CancelAccountDirective = ($repo, $model, $auth, $confirm, $location, $params, $navUrls) ->
link = ($scope, $el, $attrs) ->
$scope.data = {}
$scope.data.cancel_token = $params.cancel_token
form = $el.find("form").checksley()
onSuccessSubmit = (response) ->
$auth.logout()
$location.path($navUrls.resolve("home"))
$confirm.success("Our Oompa Loompas removed your account") #TODO: i18n
onErrorSubmit = (response) ->
$confirm.notify("error", "One of our Oompa Loompas says
'#{response.data._error_message}'.") #TODO: i18n
submit = ->
if not form.validate()
return
promise = $auth.cancelAccount($scope.data)
promise.then(onSuccessSubmit, onErrorSubmit)
$el.on "submit", (event) ->
event.preventDefault()
submit()
$el.on "click", "a.button-cancel-account", (event) ->
event.preventDefault()
submit()
return {link:link}
module.directive("tgCancelAccount", ["$tgRepo", "$tgModel", "$tgAuth", "$tgConfirm", "$tgLocation", "$routeParams",
"$tgNavUrls", CancelAccountDirective])

View File

@ -256,6 +256,7 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
resortUserStories: (uses, field="backlog_order") ->
items = []
for item, index in uses
item[field] = index
if item.isModified()
@ -263,8 +264,9 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
return items
moveUs: (ctx, us, newUsIndex, newSprintId) ->
oldSprintId = us.milestone
moveUs: (ctx, usList, newUsIndex, newSprintId) ->
oldSprintId = usList[0].milestone
project = usList[0].project
# In the same sprint or in the backlog
if newSprintId == oldSprintId
@ -277,19 +279,24 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
userstories = @scope.sprintsById[newSprintId].user_stories
@scope.$apply ->
for us, key in usList
r = userstories.indexOf(us)
userstories.splice(r, 1)
userstories.splice(newUsIndex, 0, us)
args = [newUsIndex, 0].concat(usList)
Array.prototype.splice.apply(userstories, args)
# If in backlog
if newSprintId == null
# Rehash userstories order field
items = @.resortUserStories(userstories, "backlog_order")
data = @.prepareBulkUpdateData(items, "backlog_order")
# Persist in bulk all affected
# userstories with order change
@rs.userstories.bulkUpdateBacklogOrder(us.project, data).then =>
@rs.userstories.bulkUpdateBacklogOrder(project, data).then =>
for us in usList
@rootscope.$broadcast("sprint:us:moved", us, oldSprintId, newSprintId)
# For sprint
@ -300,25 +307,30 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
# Persist in bulk all affected
# userstories with order change
@rs.userstories.bulkUpdateSprintOrder(us.project, data).then =>
@rs.userstories.bulkUpdateSprintOrder(project, data).then =>
for us in usList
@rootscope.$broadcast("sprint:us:moved", us, oldSprintId, newSprintId)
return promise
# From sprint to backlog
if newSprintId == null
us.milestone = null
us.milestone = null for us in usList
@scope.$apply =>
# Add new us to backlog userstories list
@scope.userstories.splice(newUsIndex, 0, us)
@scope.visibleUserstories.splice(newUsIndex, 0, us)
# @scope.userstories.splice(newUsIndex, 0, us)
# @scope.visibleUserstories.splice(newUsIndex, 0, us)
args = [newUsIndex, 0].concat(usList)
Array.prototype.splice.apply(@scope.userstories, args)
Array.prototype.splice.apply(@scope.visibleUserstories, args)
# Execute the prefiltering of user stories
@.filterVisibleUserstories()
# Remove the us from the sprint list.
sprint = @scope.sprintsById[oldSprintId]
for us, key in usList
r = sprint.user_stories.indexOf(us)
sprint.user_stories.splice(r, 1)
@ -340,41 +352,53 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
# From backlog to sprint
newSprint = @scope.sprintsById[newSprintId]
if us.milestone == null
us.milestone = newSprintId
if oldSprintId == null
us.milestone = newSprintId for us in usList
@scope.$apply =>
args = [newUsIndex, 0].concat(usList)
# Add moving us to sprint user stories list
newSprint.user_stories.splice(newUsIndex, 0, us)
Array.prototype.splice.apply(newSprint.user_stories, args)
# Remove moving us from backlog userstories lists.
for us, key in usList
r = @scope.visibleUserstories.indexOf(us)
@scope.visibleUserstories.splice(r, 1)
r = @scope.userstories.indexOf(us)
@scope.userstories.splice(r, 1)
# From sprint to sprint
else
us.milestone = newSprintId
us.milestone = newSprintId for us in usList
@scope.$apply =>
args = [newUsIndex, 0].concat(usList)
# Add new us to backlog userstories list
newSprint.user_stories.splice(newUsIndex, 0, us)
Array.prototype.splice.apply(newSprint.user_stories, args)
# Remove the us from the sprint list.
for us in usList
oldSprint = @scope.sprintsById[oldSprintId]
r = oldSprint.user_stories.indexOf(us)
oldSprint.user_stories.splice(r, 1)
# Persist the milestone change of userstory
promise = @repo.save(us)
promises = _.map usList, (us) => @repo.save(us)
# Rehash userstories order field
# and persist in bulk all changes.
promise = promise.then =>
promise = @q.all.apply(null, promises).then =>
items = @.resortUserStories(newSprint.user_stories, "sprint_order")
data = @.prepareBulkUpdateData(items, "sprint_order")
return @rs.userstories.bulkUpdateSprintOrder(us.project, data).then =>
return @rs.userstories.bulkUpdateSprintOrder(project, data).then =>
@rootscope.$broadcast("sprint:us:moved", us, oldSprintId, newSprintId)
return @rs.userstories.bulkUpdateBacklogOrder(project, data).then =>
for us in usList
@rootscope.$broadcast("sprint:us:moved", us, oldSprintId, newSprintId)
promise.then null, ->
@ -545,6 +569,7 @@ BacklogDirective = ($repo, $rootscope) ->
# Enable move to current sprint only when there are selected us's
$el.on "change", ".backlog-table-body .user-stories input:checkbox", (event) ->
target = angular.element(event.currentTarget)
moveToCurrentSprintDom = $el.find("#move-to-current-sprint")
selectedUsDom = $el.find(".backlog-table-body .user-stories input:checkbox:checked")
@ -553,6 +578,8 @@ BacklogDirective = ($repo, $rootscope) ->
else
moveToCurrentSprintDom.hide()
target.closest('.us-item-row').toggleClass('ui-multisortable-multiple')
$el.on "click", "#move-to-current-sprint", (event) =>
# Calculating the us's to be modified
ussDom = $el.find(".backlog-table-body .user-stories input:checkbox:checked")
@ -661,6 +688,7 @@ UsRolePointsSelectorDirective = ($rootscope) ->
$el.append(selectionTemplate({"roles":roles}))
else
$el.find(".icon-arrow-bottom").remove()
$el.find(".header-points").addClass("not-clickable")
$scope.$on "uspoints:select", (ctx, roleId, roleName) ->
$el.find(".popover").popover().close()
@ -744,6 +772,20 @@ UsPointsDirective = ($repo) ->
if numberOfRoles == 1
selectedRoleId = _.keys(us.points)[0]
computableRoles = _.filter($scope.project.roles, "computable")
roles = _.map computableRoles, (role) ->
pointId = us.points[role.id]
pointObj = $scope.pointsById[pointId]
role = _.clone(role, true)
role.points = if pointObj.value? then pointObj.value else "?"
return role
if roles.length == 0
$el.find(".icon-arrow-bottom").remove()
$el.find("a.us-points").addClass("not-clickable")
renderPointsSelector = (us, roleId) ->
# Prepare data for rendering
points = _.map $scope.project.points, (point) ->
@ -767,17 +809,6 @@ UsPointsDirective = ($repo) ->
$el.find(".pop-points-open").popover().open()
renderRolesSelector = (us) ->
# Prepare data for rendering
computableRoles = _.filter($scope.project.roles, "computable")
roles = _.map computableRoles, (role) ->
pointId = us.points[role.id]
pointObj = $scope.pointsById[pointId]
role = _.clone(role, true)
role.points = if pointObj.value? then pointObj.value else "?"
return role
html = rolesTemplate({"roles": roles})
# Render into DOM and show the new created element
@ -818,6 +849,7 @@ UsPointsDirective = ($repo) ->
renderPoints(us, null)
selectedRoleId = null
if roles.length > 0
$el.on "click", "a.us-points span", (event) ->
event.preventDefault()
event.stopPropagation()

View File

@ -69,23 +69,35 @@ BacklogSortableDirective = ($repo, $rs, $rootscope) ->
cursorAt: {right: 15}
})
$el.on "sortreceive", (event, ui) ->
$el.on "multiplesortreceive", (event, ui) ->
itemUs = ui.item.scope().us
itemIndex = ui.item.index()
deleteElement(ui.item)
$scope.$emit("sprint:us:move", itemUs, itemIndex, null)
$scope.$emit("sprint:us:move", [itemUs], itemIndex, null)
ui.item.find('a').removeClass('noclick')
$el.on "sortstop", (event, ui) ->
$el.on "multiplesortstop", (event, ui) ->
# When parent not exists, do nothing
if ui.item.parent().length == 0
if $(ui.items[0]).parent().length == 0
return
itemUs = ui.item.scope().us
itemIndex = ui.item.index()
$scope.$emit("sprint:us:move", itemUs, itemIndex, null)
ui.item.find('a').removeClass('noclick')
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
item.find('a').removeClass('noclick')
return itemUs
$scope.$emit("sprint:us:move", us, index, null)
$el.on "sortstart", (event, ui) ->
ui.item.find('a').addClass('noclick')
@ -113,7 +125,7 @@ BacklogEmptySortableDirective = ($repo, $rs, $rootscope) ->
itemIndex = ui.item.index()
deleteElement(ui.item)
$scope.$emit("sprint:us:move", itemUs, itemIndex, null)
$scope.$emit("sprint:us:move", [itemUs], itemIndex, null)
ui.item.find('a').removeClass('noclick')
$scope.$on "$destroy", ->
@ -132,24 +144,33 @@ SprintSortableDirective = ($repo, $rs, $rootscope) ->
connectWith: ".sprint-table,.backlog-table-body,.empty-backlog"
})
$el.on "sortreceive", (event, ui) ->
itemUs = ui.item.scope().us
itemIndex = ui.item.index()
$el.on "multiplesortreceive", (event, ui) ->
items = _.sortBy ui.items, (item) ->
return $(item).index()
deleteElement(ui.item)
$scope.$emit("sprint:us:move", itemUs, itemIndex, $scope.sprint.id)
ui.item.find('a').removeClass('noclick')
index = _.min _.map items, (item) ->
return $(item).index()
$el.on "sortstop", (event, ui) ->
us = _.map items, (item) ->
item = $(item)
itemUs = item.scope().us
deleteElement(item)
item.find('a').removeClass('noclick')
return itemUs
$scope.$emit("sprint:us:move", us, index, $scope.sprint.id)
$el.on "multiplesortstop", (event, ui) ->
# When parent not exists, do nothing
if ui.item.parent().length == 0
return
itemUs = ui.item.scope().us
itemIndex = ui.item.index()
$scope.$emit("sprint:us:move", itemUs, itemIndex, $scope.sprint.id)
ui.item.find('a').removeClass('noclick')
$scope.$emit("sprint:us:move", [itemUs], itemIndex, $scope.sprint.id)
return {link:link}

View File

@ -51,6 +51,7 @@ urls = {
"forgot-password": "/forgot-password"
"change-password": "/change-password/:token"
"change-email": "/change-email/:token"
"cancel-account": "/cancel-account/:token"
"register": "/register"
"invitation": "/invitation/:token"
"create-project": "/create-project"

View File

@ -134,9 +134,10 @@ class RepositoryService extends taiga.Service
return @http.get(url, params, httpOptions).then (data) =>
return data.data
queryPaginated: (name, params) ->
queryPaginated: (name, params, options={}) ->
url = @urls.resolve(name)
return @http.get(url, params).then (data) =>
httpOptions = _.merge({headers: {}}, options)
return @http.get(url, params, httpOptions).then (data) =>
headers = data.headers()
result = {}
result.models = _.map(data.data, (x) => @model.make_model(name, x))

View File

@ -282,7 +282,10 @@ HistoryDirective = ($log, $loading) ->
return $scope.usersById[userId]?.full_name_display
getUserAvatar = (userId) ->
return $scope.usersById[userId]?.photo
if $scope.usersById[userId]?
return $scope.usersById[userId].photo
else
return "/images/unnamed.png"
countChanges = (comment) ->
return _.keys(comment.values_diff).length
@ -373,7 +376,7 @@ HistoryDirective = ($log, $loading) ->
return templateActivity({
avatar: getUserAvatar(comment.user.pk)
userFullName: getUserFullName(comment.user.pk)
userFullName: comment.user.name
creationDate: moment(comment.created_at).format("DD MMM YYYY HH:mm")
comment: comment.comment_html
changesText: renderChangesHelperText(comment)
@ -388,7 +391,7 @@ HistoryDirective = ($log, $loading) ->
renderChange = (change) ->
return templateActivity({
avatar: getUserAvatar(change.user.pk)
userFullName: getUserFullName(change.user.pk)
userFullName: change.user.name
creationDate: moment(change.created_at).format("DD MMM YYYY HH:mm")
comment: change.comment_html
changes: renderChangeEntries(change, false)

View File

@ -229,7 +229,12 @@ class IssuesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
@.markSelectedFilters(@scope.filters, urlfilters)
@rootscope.$broadcast("filters:loaded", @scope.filters)
loadIssues: ->
# We need to guarantee that the last petition done here is the finally used
# When searching by text loadIssues can be called fastly with different parameters and
# can be resolved in a different order than generated
# We count the requests made and only if the callback is for the last one data is updated
loadIssuesRequests: 0
loadIssues: =>
@scope.urlFilters = @.getUrlFilters()
# Convert stored filters to http parameters
@ -255,7 +260,11 @@ class IssuesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
name = "type"
@scope.httpParams[name] = values
return @rs.issues.list(@scope.projectId, @scope.httpParams).then (data) =>
promise = @rs.issues.list(@scope.projectId, @scope.httpParams)
@.loadIssuesRequests += 1
promise.index = @.loadIssuesRequests
promise.then (data) =>
if promise.index == @.loadIssuesRequests
@scope.issues = data.models
@scope.page = data.current
@scope.count = data.count

View File

@ -317,6 +317,9 @@ KanbanColumnHeightFixerDirective = ->
link = ($scope, $el, $attrs) ->
timeout(500, -> renderSize($el))
$scope.$on "resize", ->
renderSize($el)
$scope.$on "$destroy", ->
$el.off()

View File

@ -1 +1,22 @@
###
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
#
# 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: modules/projects.coffee
###
module = angular.module("taigaProject", [])

View File

@ -73,6 +73,7 @@ urls = {
"users-change-password-from-recovery": "/users/change_password_from_recovery"
"users-change-password": "/users/change_password"
"users-change-email": "/users/change_email"
"users-cancel-account": "/users/cancel"
"user-storage": "/user-storage"
"resolver": "/resolver"
"userstory-statuses": "/userstory-statuses"

View File

@ -35,11 +35,11 @@ resourceProvider = ($repo, $http, $urls, $storage, $q) ->
params.project = projectId
return $repo.queryOne("issues", issueId, params)
service.list = (projectId, filters) ->
service.list = (projectId, filters, options) ->
params = {project: projectId}
params = _.extend({}, params, filters or {})
service.storeQueryParams(projectId, params)
return $repo.queryPaginated("issues", params)
return $repo.queryPaginated("issues", params, options)
service.bulkCreate = (projectId, data) ->
url = $urls.resolve("bulk-create-issues")

View File

@ -303,6 +303,9 @@ TaskboardTableHeightFixerDirective = ->
link = ($scope, $el, $attrs) ->
timeout(500, -> renderSize($el))
$scope.$on "resize", ->
renderSize($el)
return {link:link}

View File

@ -58,7 +58,10 @@ DeleteUserDirective = ($repo, $rootscope, $auth, $location, $navUrls, lightboxSe
event.preventDefault()
submit()
return {link:link}
return {
link: link,
templateUrl: "/partials/views/modules/lightbox-delete-account.html"
}
module.directive("tgLbDeleteUser", ["$tgRepo", "$rootScope", "$tgAuth", "$tgLocation", "$tgNavUrls",
"lightboxService", DeleteUserDirective])

Binary file not shown.

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,141 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>
This is a custom SVG webfont generated by Font Squirrel.
Copyright : Copyright c 2011 by Tyler Finck All rights reserved
Designer : Tyler Finck
Foundry : Tyler Finck
Foundry URL : httpwwwsurslycom
</metadata>
<defs>
<font id="OstrichSansMedium" horiz-adv-x="1705" >
<font-face units-per-em="2048" ascent="1536" descent="-512" />
<missing-glyph horiz-adv-x="430" />
<glyph unicode=" " horiz-adv-x="430" />
<glyph unicode="&#x09;" horiz-adv-x="430" />
<glyph unicode="&#xa0;" horiz-adv-x="430" />
<glyph unicode="!" horiz-adv-x="190" d="M18 2v88h64v-88h-64zM18 203v1179h64v-1179h-64z" />
<glyph unicode="&#x22;" horiz-adv-x="331" d="M20 1200v180h64v-180h-64zM141 1200v180h66v-180h-66z" />
<glyph unicode="#" horiz-adv-x="1110" d="M-41 463v65h299l64 322h-299v65h311l90 465h68l-93 -465h291l88 465h66l-88 -465h245v-65h-260l-61 -322h258v-65h-272l-88 -463h-66l88 463h-289l-90 -463h-65l90 463h-287zM324 528h288l64 322h-289z" />
<glyph unicode="$" horiz-adv-x="655" d="M39 965q0 135 61 219q57 82 162 94v102h64v-102q41 -4 80.5 -19.5t71.5 -51.5t51.5 -95t19.5 -147v-33h-64v33q0 74 -14 121t-36.5 74.5t-51.5 38.5t-57 15v-514q4 -2 7 -3t7 -3q27 -12 63.5 -32.5t68.5 -53.5t54.5 -82t22.5 -120q0 -66 -15.5 -120.5t-46.5 -95.5 q-59 -78 -161 -90v-100h-64v100q-41 4 -81 20.5t-71.5 51.5t-51 93.5t-19.5 148.5v32h63v-32q0 -74 14.5 -121t37 -75t51 -40t57.5 -14v498l-8 4l-8 4l-63 35q-36 20 -67.5 54t-54 84t-22.5 122zM102 965q0 -53 15.5 -91t38 -64t51.5 -44t55 -31v477q-43 -6 -73.5 -29.5 t-50 -58t-28 -75.5t-8.5 -84zM326 166q76 10 117.5 72.5t41.5 167.5q0 53 -15 89.5t-38.5 62.5t-52.5 42l-53 29v-463z" />
<glyph unicode="%" horiz-adv-x="919" d="M0 922v176q0 84 47 136t121 52t121 -52t47 -136v-176q0 -84 -47 -136.5t-121 -52.5t-121 52.5t-47 136.5zM63 922q0 -43 24 -83t81 -40t79.5 40t22.5 83v176q0 43 -22.5 83t-79.5 40t-81 -40t-24 -83v-176zM109 0l553 1380h69l-553 -1380h-69zM502 281v176q0 84 46 135 t122 51q74 0 120 -51t46 -135v-176q0 -84 -46.5 -136.5t-119.5 -52.5q-76 0 -122 52.5t-46 136.5zM565 281q0 -43 22.5 -84t82.5 -41q57 0 79.5 41t22.5 84v176q0 20 -5 41.5t-17.5 40t-32 30t-48 11.5t-49 -11.5t-33 -30t-17.5 -40t-5 -41.5v-176z" />
<glyph unicode="&#x26;" horiz-adv-x="878" d="M0 340q0 68 16.5 119t44 91t63.5 73l73 65q16 16 34 32l37 32q-14 20 -36.5 59t-44 86t-37 99.5t-15.5 103.5q0 70 9.5 118t25.5 82q43 80 139 80q104 0 146 -94q18 -41 23 -92t5 -94q0 -74 -15 -133.5t-35.5 -104.5t-44 -74.5t-38.5 -46.5l268 -460q29 78 35 160.5 t6 148.5v20h66v-20q0 -78 -10 -180.5t-53 -194.5q0 -2 -1.5 -2t-1.5 -2l123 -211h-73l-86 147q-104 -147 -314 -147q-143 0 -227 100q-82 95 -82 240zM63 340q0 -51 14.5 -101.5t45.5 -89t77 -62.5t109 -24q190 0 277 146l-285 485l-32 -27l-31 -28q-37 -31 -69 -60.5 t-55.5 -63.5t-37 -76t-13.5 -99zM201 1100q0 -41 12 -85t29.5 -85t37 -76t33.5 -57q10 10 20.5 24.5t20.5 30.5q63 106 64 248q0 68 -8.5 109.5t-21.5 66t-33.5 33t-45.5 8.5q-23 0 -42 -7t-33.5 -30t-23.5 -65.5t-9 -114.5z" />
<glyph unicode="'" horiz-adv-x="165" d="M20 1200v180h64v-180h-64z" />
<glyph unicode="(" horiz-adv-x="339" d="M14 686q0 209 30 342t64.5 210t65.5 106.5t35 31.5l37 -55q-12 -10 -27 -27q-12 -14 -26.5 -36.5t-30.5 -57.5q-29 -66 -56.5 -188.5t-27.5 -325.5q0 -201 27.5 -323.5t56.5 -186.5q16 -35 29.5 -56.5t25.5 -35.5q12 -16 27 -25l-33 -55q-4 4 -36 33t-66 104 q-47 98 -71 234.5t-24 310.5z" />
<glyph unicode=")" horiz-adv-x="323" d="M27 1321h2l32 55q4 -4 36 -31.5t67 -105.5q94 -195 94 -545q0 -209 -29.5 -342t-65.5 -209.5t-68 -106.5t-34 -32l-34 55q10 10 24 27l26 37q15 23 32 57q29 63 56.5 187.5t27.5 326.5q0 100 -7.5 181t-19.5 143.5t-26.5 109t-28.5 76.5q-31 68 -56.5 92.5t-27.5 24.5z " />
<glyph unicode="*" horiz-adv-x="604" d="M20 1174l23 61l199 -74v211h63v-211l199 74l22 -61l-198 -74l149 -168l-49 -43l-156 176l-153 -176l-49 43l147 168z" />
<glyph unicode="+" horiz-adv-x="786" d="M25 651v66h299v299h65v-299h299v-66h-299v-299h-65v299h-299z" />
<glyph unicode="," horiz-adv-x="174" d="M27 -82v170h63v-170h-63z" />
<glyph unicode="-" horiz-adv-x="612" d="M27 657v64h499v-64h-499z" />
<glyph unicode="." horiz-adv-x="165" d="M25 0v88h65v-88h-65z" />
<glyph unicode="/" horiz-adv-x="786" d="M43 0l553 1380h70l-553 -1380h-70z" />
<glyph unicode="0" horiz-adv-x="655" d="M18 371v641q0 176 62 268q63 100 194 100q129 0 193 -100q63 -96 63 -268v-641q0 -172 -61 -269q-68 -102 -195 -102q-129 0 -194 102q-62 93 -62 269zM82 371q0 -307 192 -308q190 0 191 308v641q0 305 -191 305q-193 0 -192 -305v-641z" />
<glyph unicode="1" horiz-adv-x="397" d="M16 1108l209 272h64v-1380h-66v1270l-157 -203z" />
<glyph unicode="2" horiz-adv-x="655" d="M25 1010v32q0 162 63.5 250t192.5 88q123 0 194 -98q61 -90 62 -231q0 -279 -234 -488q-109 -96 -159 -215t-54 -285h191h223h33v-63h-33h-223h-256v33q0 92 12 167.5t37 141.5q53 150 186 270q211 193 211 439q0 123 -49 194.5t-141 71.5q-96 0 -142 -64 q-49 -68 -49 -211v-32h-65z" />
<glyph unicode="3" horiz-adv-x="620" d="M20 365v32h66v-32q0 -158 51 -230q47 -72 139 -72q88 0 140 80q27 39 39 93.5t12 119.5q0 92 -28.5 147.5t-59.5 84.5q-41 39 -83 54t-73 15h-59v64h59q37 0 80 18.5t79 55t60.5 93t24.5 134.5q0 66 -12.5 120t-38.5 95q-49 80 -140 80q-190 0 -190 -303v-33h-66v33 q0 174 62 266q68 100 194.5 100t194.5 -110q61 -92 61 -248q0 -106 -35.5 -174t-72.5 -103q-37 -37 -78 -57q45 -23 78 -55q37 -35 72.5 -101.5t35.5 -175.5q0 -164 -69.5 -260t-186.5 -96q-127 0 -194 98q-62 93 -62 267z" />
<glyph unicode="4" horiz-adv-x="663" d="M20 422v61l486 897h61v-1380h-65v422h-482zM96 485h406v750z" />
<glyph unicode="5" horiz-adv-x="634" d="M18 360v23h66v-23q0 -156 51 -227q45 -70 139 -70q90 0 140 84q27 41 39 95.5t12 117.5v146q0 152 -49 223q-45 66 -142 66q-90 0 -139 -78q-51 -80 -51 -211v-8v-4h-66v4v8v874h66h446v-63h-446v-561q70 104 190 104q125 0 195 -94q61 -90 61 -260v-146q0 -72 -16 -136 t-45 -111q-70 -113 -195 -113q-127 0 -192 96q-64 94 -64 264z" />
<glyph unicode="6" horiz-adv-x="638" d="M18 324v194v2v408q0 147 27 237t66 137.5t84 62.5t79 15q127 0 193 -90q63 -82 63 -225v-31h-65v31q0 121 -49 186.5t-142 65.5q-61 0 -99 -40t-58.5 -99.5t-26.5 -127t-6 -122.5v-154q70 104 190 105q131 0 195 -95q61 -92 61 -266v-149q0 -74 -16 -139.5t-45 -114.5 q-68 -115 -195 -115q-121 0 -188.5 85t-67.5 239zM84 324q0 -37 6 -82t26.5 -85t58.5 -67t99 -27q90 0 140 87q27 41 39 97t12 122v149q0 156 -49 230q-47 68 -142 67q-90 0 -139 -82q-51 -80 -51 -213v-196z" />
<glyph unicode="7" horiz-adv-x="770" d="M12 1317v63h682v-61l-551 -1319h-69l551 1317h-613z" />
<glyph unicode="8" horiz-adv-x="679" d="M14 360.5q0 178.5 66 268.5q30 41 70 61q-41 23 -70 62q-66 90 -66 268t66 268q68 92 213 92q143 0 211 -92q66 -86 65.5 -268t-65.5 -268q-29 -39 -70 -62q41 -20 70 -61q66 -86 65.5 -268.5t-65.5 -268.5q-68 -92 -211 -92q-145 0 -213 92q-66 90 -66 268.5zM80 360.5 q0 -159.5 51 -229.5q51 -68 161.5 -68t160.5 68q51 70 51 229.5t-51 231.5q-49 68 -160 67.5t-162 -67.5q-51 -72 -51 -231.5zM80 1020q0 -160 51 -229q51 -68 161.5 -68t160.5 68q51 70 51 229.5t-51 228.5q-49 68 -160 68t-162 -68q-51 -69 -51 -229z" />
<glyph unicode="9" horiz-adv-x="630" d="M16 334v33h66v-33q0 -133 49 -203q49 -68 141 -68q59 0 96.5 36t59 92.5t28.5 125t7 134.5v155q-33 -51 -81 -78.5t-110 -27.5q-127 0 -192 96q-63 90 -64 268v152q0 166 70 265t186 99q127 0 193 -88q63 -82 63 -231v-197v-2v-411q0 -211 -59 -326q-66 -125 -197 -125 q-127 0 -192 94q-64 88 -64 240zM82 864q0 -160 51 -231q45 -68 139 -68q90 0 140 82q25 39 38 94.5t13 120.5v199q0 127 -49 192q-47 63 -142 64q-88 0 -139 -82q-51 -84 -51 -219v-152z" />
<glyph unicode=":" horiz-adv-x="231" d="M29 334v155h63v-155h-63zM29 897v156h63v-156h-63z" />
<glyph unicode=";" horiz-adv-x="198" d="M27 260v229h65v-229h-65zM27 897v156h65v-156h-65z" />
<glyph unicode="&#x3c;" />
<glyph unicode="=" horiz-adv-x="638" d="M27 412v63h499v-63h-499zM27 842v63h499v-63h-499z" />
<glyph unicode="&#x3e;" />
<glyph unicode="?" horiz-adv-x="620" d="M29 997v31q0 166 63 258q66 96 193 96q121 0 194 -104q31 -47 46.5 -107.5t15.5 -132.5q0 -74 -17.5 -128t-42 -94t-52.5 -66.5t-50 -44.5l-19 -15q-8 -6 -12 -12q-41 -47 -64 -160q-22 -104 -22 -249v-25v-6v-33h-63v33v6q0 61 3 130.5t14 134t31.5 121t53.5 91.5 q12 16 37 33l45 41q25 23 45.5 56.5t33.5 78.5t13 108q0 61 -12 113.5t-37 89.5q-53 78 -141 78q-94 0 -140 -70q-51 -70 -51 -221v-31h-65zM197 2v88h65v-88h-65z" />
<glyph unicode="@" horiz-adv-x="1325" d="M25 727q18 117 66 214t122 175q68 70 152 115t180 65q68 14 133 14q22 0 44 -1q87 -6 164 -35t140.5 -79t108.5 -120q33 -51 51 -115.5t23 -133.5q2 -26 2 -53q0 -42 -5 -85q-8 -70 -32 -131q-23 -55 -69 -99t-102.5 -74t-114.5 -40q-27 -5 -51 -5q-29 0 -53 7 q-43 10 -64 43q-18 28 -18 65v15q-31 -23 -69.5 -28t-85.5 -5q-113 0 -162 58q-43 48 -43 134q0 28 4 60q14 125 74 191q66 70 178 69q29 0 76 -6t82 -31q12 -8 16 -14l4 16l4 31l64 -10l-4 -33l-5 -29l-13 -71l-16 -95q-9 -52 -16 -101l-14 -86q-6 -38 -8 -54q-1 -7 -1 -14 q0 -33 36 -43q16 -4 33 -5q33 0 74.5 12.5t81.5 36t73 56.5t49 74q20 53 29 114q5 38 5 76q0 24 -2 48q-5 62 -21.5 119.5t-43.5 100.5q-39 61 -96 105.5t-124.5 69t-144.5 29.5q-18 1 -37 1q-59 0 -120 -12q-74 -16 -148.5 -53t-140 -100.5t-115 -153.5t-67.5 -209 q-10 -61 -10 -119q0 -112 38 -205q56 -141 193 -225q86 -53 191 -76q82 -18 166 -18q23 0 47 2q109 6 212 38.5t187 94.5l25 18l39 -51l-27 -20q-88 -63 -198.5 -101.5t-233.5 -44.5h-45q-111 0 -212.5 25.5t-185.5 77.5q-154 96 -219 253q-43 102 -42 225q0 65 12 136z M410 680q-3 -28 -4 -51q0 -65 26 -92q33 -35 115 -35q51 0 79.5 5t49.5 23q23 23 39 74t33 139q5 27 4 47q0 22 -6 34q-11 24 -27 34q-16 12 -48 18.5t-73 6.5q-86 0 -131 -47q-23 -27 -37 -65t-20 -91z" />
<glyph unicode="A" horiz-adv-x="802" d="M20 0l109 479v6l209 895h51l316 -1380h-66l-94 422h-365l-94 -422h-66zM197 485h333l-168 721z" />
<glyph unicode="B" horiz-adv-x="688" d="M35 0v63v1317h227q158 0 234 -90q72 -90 71 -270q0 -178 -71 -268q-16 -20 -35 -35t-41 -27q43 -20 76 -59q72 -90 71.5 -270.5t-71.5 -270.5q-76 -90 -234 -90h-162h-65zM100 63h162q129 0 184.5 69t55.5 228.5t-58 229.5q-55 70 -182 69h-162v-596zM100 723h162 q127 0 182 70q57 70 58 227q0 160 -55.5 228.5t-184.5 68.5h-162v-594z" />
<glyph unicode="C" horiz-adv-x="704" d="M16 367v649q0 113 27 184.5t70 111.5t95 54t103 14q49 0 101.5 -14t95.5 -54t69.5 -112t26.5 -184v-33h-65v33q0 158 -58 229q-59 72 -170 72q-113 0 -172 -72q-57 -72 -57 -229v-649q0 -158 59 -232q55 -72 170 -72q111 0 170 72q57 76 58 232v32h65v-32 q0 -115 -26.5 -185.5t-69.5 -111.5t-95.5 -55.5t-101.5 -14.5q-51 0 -103 14.5t-95 55.5t-70 111.5t-27 185.5z" />
<glyph unicode="D" horiz-adv-x="712" d="M25 0v1380h235q164 0 244 -98q76 -90 76 -274v-635q0 -184 -76 -275q-80 -98 -244 -98h-235zM88 63h172q133 0 193.5 75t60.5 235v635q0 160 -60.5 234.5t-193.5 74.5h-172v-1254z" />
<glyph unicode="E" horiz-adv-x="620" d="M20 0v1380h496v-63h-432v-594h432v-64h-432v-596h432v-63h-496z" />
<glyph unicode="F" horiz-adv-x="630" d="M14 0v1380h496v-63h-432v-594h432v-64h-432v-659h-64z" />
<glyph unicode="G" horiz-adv-x="720" d="M20 365v653q0 178 72 268q76 94 223.5 94t221.5 -94q74 -90 73 -268v-33h-65v33q0 100 -22.5 159.5t-56.5 90t-74 40t-76.5 9.5t-77.5 -9.5t-75 -40t-56.5 -90t-22.5 -159.5v-653q0 -154 59 -230q55 -72 172 -72q115 0 170 72q59 76 60 230v180h-299v65h364v-245 q0 -178 -73 -271q-74 -94 -221.5 -94t-223.5 94q-72 95 -72 271z" />
<glyph unicode="H" horiz-adv-x="704" d="M20 0v1380h66v-659h418v659h65v-1380h-65v657h-418v-657h-66z" />
<glyph unicode="I" horiz-adv-x="223" d="M47 0v1380h64v-1380h-64z" />
<glyph unicode="J" horiz-adv-x="663" d="M0 338v31h63v-31q0 -92 20.5 -146.5t51.5 -83t68 -37t71 -8.5q102 0 156 64q53 70 53 211v1042h64v-1042q0 -168 -68 -250q-68 -88 -205 -88t-206 88q-68 82 -68 250z" />
<glyph unicode="K" horiz-adv-x="737" d="M16 0v1380h66v-784l524 784h78l-424 -632l424 -748h-74l-389 686l-139 -207v-479h-66z" />
<glyph unicode="L" horiz-adv-x="604" d="M8 0v1380h66v-1317h463v-63h-529z" />
<glyph unicode="M" horiz-adv-x="968" d="M14 0v1380h56l354 -1230l352 1230h58v-1380h-66v1118l-319 -1118h-50l-321 1118v-1118h-64z" />
<glyph unicode="N" horiz-adv-x="737" d="M18 0v1380h56l475 -1181v1181h65v-1380h-53l-477 1182v-1182h-66z" />
<glyph unicode="O" horiz-adv-x="729" d="M20 367v649q0 176 72 270q76 94 221 94q147 0 222 -94q74 -92 73 -270v-649q0 -115 -26.5 -185.5t-69.5 -111.5t-95 -55.5t-104 -14.5q-49 0 -101 14.5t-95 55.5t-70 111.5t-27 185.5zM84 367q0 -156 59 -232q55 -72 170 -72q113 0 172 72q57 72 58 232v649 q0 158 -57.5 229.5t-172.5 71.5q-117 0 -170 -72q-59 -76 -59 -229v-649z" />
<glyph unicode="P" horiz-adv-x="696" d="M23 0v1380h247q166 0 244 -90t78 -270t-78 -270.5t-244 -90.5h-184v-659h-63zM86 723h184q135 0 197 70q59 72 59 227q0 160 -59 228.5t-197 68.5h-184v-594z" />
<glyph unicode="Q" horiz-adv-x="712" d="M12 373v649q0 176 74 266q74 92 221 92q145 0 219 -92q74 -90 74 -266v-649q0 -176 -72 -273l-4 -4l133 -159h-84l-94 114q-70 -51 -172 -51q-147 0 -221 100q-74 95 -74 273zM78 373q0 -156 59 -234q55 -76 170 -76q78 0 131 37l-92 113h82l55 -66q51 76 52 226v649 q0 156 -58 227q-55 68 -169.5 68t-170.5 -68q-59 -76 -59 -227v-649z" />
<glyph unicode="R" horiz-adv-x="712" d="M23 2v1380h247q166 0 243 -89t77 -277q0 -184 -76 -275q-33 -39 -82 -63q39 -18 70 -49q92 -88 92 -244v-383h-66v383q0 125 -67.5 193.5t-190.5 68.5h-30h-154v-645h-63zM86 713h154h30q137 0 195 69q61 74 61 234q0 162 -59 231.5t-197 69.5h-184v-604z" />
<glyph unicode="S" horiz-adv-x="712" d="M20 1016q0 156 72 256q82 111 223 110q145 0 224 -96q74 -92 73 -270v-33h-65v33q0 156 -60 231q-55 70 -172 70q-111 0 -170 -84q-61 -84 -61 -217q0 -74 24.5 -125t60.5 -86t75 -55.5t69 -32.5l33 -15q27 -10 72 -31.5t88 -61.5t74.5 -101.5t31.5 -151.5 q0 -154 -73 -250q-80 -104 -224 -104q-147 0 -221 94q-74 88 -74 271v30h64v-30q0 -102 22.5 -162t56.5 -90.5t75 -39.5t77 -9q111 0 171.5 76.5t60.5 213.5q0 74 -25.5 124t-61.5 83t-75 51.5t-63 28.5l-35 14q-31 12 -76 36t-87 66t-73 104.5t-31 152.5z" />
<glyph unicode="T" horiz-adv-x="821" d="M10 1317v65h725v-65h-332v-1315h-63v1315h-330z" />
<glyph unicode="U" horiz-adv-x="712" d="M14 340v1042h64v-1042q0 -92 20.5 -146.5t51 -83t67.5 -36.5t72 -8q33 0 69.5 8t67.5 36.5t51.5 83t20.5 146.5v1042h63v-1042q0 -104 -25.5 -171t-64.5 -103.5t-88 -50t-94 -13.5q-47 0 -95.5 13.5t-88 50t-65.5 103.5t-26 171z" />
<glyph unicode="V" horiz-adv-x="821" d="M0 1382h66l305 -1216l303 1216h67l-346 -1380h-49z" />
<glyph unicode="W" horiz-adv-x="1333" d="M0 1380h66l266 -1198l264 1198h51l264 -1198l260 1198h68l-301 -1380h-53l-264 1198l-265 -1198h-51z" />
<glyph unicode="X" horiz-adv-x="802" d="M-8 0l327 690l-327 690h71l293 -614l287 614h72l-324 -690l328 -690h-72l-291 614l-293 -614h-71z" />
<glyph unicode="Y" horiz-adv-x="854" d="M-20 1380h73l342 -622l342 622h74l-383 -698v-682h-63v682z" />
<glyph unicode="Z" horiz-adv-x="837" d="M0 1317v63h672v-59l-590 -1258h600v-63h-672v63l588 1254h-598z" />
<glyph unicode="[" />
<glyph unicode="\" horiz-adv-x="778" d="M45 1380h70l553 -1380h-70z" />
<glyph unicode="]" />
<glyph unicode="^" />
<glyph unicode="_" horiz-adv-x="944" d="M41 0h827v-63h-827v63z" />
<glyph unicode="`" horiz-adv-x="165" d="M20 1200v180h64v-180h-64z" />
<glyph unicode="a" horiz-adv-x="802" d="M20 0l109 479v6l209 895h51l316 -1380h-66l-94 422h-365l-94 -422h-66zM197 485h333l-168 721z" />
<glyph unicode="b" horiz-adv-x="688" d="M35 0v63v1317h227q158 0 234 -90q72 -90 71 -270q0 -178 -71 -268q-16 -20 -35 -35t-41 -27q43 -20 76 -59q72 -90 71.5 -270.5t-71.5 -270.5q-76 -90 -234 -90h-162h-65zM100 63h162q129 0 184.5 69t55.5 228.5t-58 229.5q-55 70 -182 69h-162v-596zM100 723h162 q127 0 182 70q57 70 58 227q0 160 -55.5 228.5t-184.5 68.5h-162v-594z" />
<glyph unicode="c" horiz-adv-x="704" d="M16 367v649q0 113 27 184.5t70 111.5t95 54t103 14q49 0 101.5 -14t95.5 -54t69.5 -112t26.5 -184v-33h-65v33q0 158 -58 229q-59 72 -170 72q-113 0 -172 -72q-57 -72 -57 -229v-649q0 -158 59 -232q55 -72 170 -72q111 0 170 72q57 76 58 232v32h65v-32 q0 -115 -26.5 -185.5t-69.5 -111.5t-95.5 -55.5t-101.5 -14.5q-51 0 -103 14.5t-95 55.5t-70 111.5t-27 185.5z" />
<glyph unicode="d" horiz-adv-x="712" d="M25 0v1380h235q164 0 244 -98q76 -90 76 -274v-635q0 -184 -76 -275q-80 -98 -244 -98h-235zM88 63h172q133 0 193.5 75t60.5 235v635q0 160 -60.5 234.5t-193.5 74.5h-172v-1254z" />
<glyph unicode="e" horiz-adv-x="620" d="M20 0v1380h496v-63h-432v-594h432v-64h-432v-596h432v-63h-496z" />
<glyph unicode="f" horiz-adv-x="630" d="M14 0v1380h496v-63h-432v-594h432v-64h-432v-659h-64z" />
<glyph unicode="g" horiz-adv-x="720" d="M20 365v653q0 178 72 268q76 94 223.5 94t221.5 -94q74 -90 73 -268v-33h-65v33q0 100 -22.5 159.5t-56.5 90t-74 40t-76.5 9.5t-77.5 -9.5t-75 -40t-56.5 -90t-22.5 -159.5v-653q0 -154 59 -230q55 -72 172 -72q115 0 170 72q59 76 60 230v180h-299v65h364v-245 q0 -178 -73 -271q-74 -94 -221.5 -94t-223.5 94q-72 95 -72 271z" />
<glyph unicode="h" horiz-adv-x="704" d="M20 0v1380h66v-659h418v659h65v-1380h-65v657h-418v-657h-66z" />
<glyph unicode="i" horiz-adv-x="223" d="M47 0v1380h64v-1380h-64z" />
<glyph unicode="j" horiz-adv-x="663" d="M0 338v31h63v-31q0 -92 20.5 -146.5t51.5 -83t68 -37t71 -8.5q102 0 156 64q53 70 53 211v1042h64v-1042q0 -168 -68 -250q-68 -88 -205 -88t-206 88q-68 82 -68 250z" />
<glyph unicode="k" horiz-adv-x="737" d="M16 0v1380h66v-784l524 784h78l-424 -632l424 -748h-74l-389 686l-139 -207v-479h-66z" />
<glyph unicode="l" horiz-adv-x="604" d="M8 0v1380h66v-1317h463v-63h-529z" />
<glyph unicode="m" horiz-adv-x="968" d="M14 0v1380h56l354 -1230l352 1230h58v-1380h-66v1118l-319 -1118h-50l-321 1118v-1118h-64z" />
<glyph unicode="n" horiz-adv-x="737" d="M18 0v1380h56l475 -1181v1181h65v-1380h-53l-477 1182v-1182h-66z" />
<glyph unicode="o" horiz-adv-x="729" d="M20 367v649q0 176 72 270q76 94 221 94q147 0 222 -94q74 -92 73 -270v-649q0 -115 -26.5 -185.5t-69.5 -111.5t-95 -55.5t-104 -14.5q-49 0 -101 14.5t-95 55.5t-70 111.5t-27 185.5zM84 367q0 -156 59 -232q55 -72 170 -72q113 0 172 72q57 72 58 232v649 q0 158 -57.5 229.5t-172.5 71.5q-117 0 -170 -72q-59 -76 -59 -229v-649z" />
<glyph unicode="p" horiz-adv-x="696" d="M23 0v1380h247q166 0 244 -90t78 -270t-78 -270.5t-244 -90.5h-184v-659h-63zM86 723h184q135 0 197 70q59 72 59 227q0 160 -59 228.5t-197 68.5h-184v-594z" />
<glyph unicode="q" horiz-adv-x="712" d="M12 373v649q0 176 74 266q74 92 221 92q145 0 219 -92q74 -90 74 -266v-649q0 -176 -72 -273l-4 -4l133 -159h-84l-94 114q-70 -51 -172 -51q-147 0 -221 100q-74 95 -74 273zM78 373q0 -156 59 -234q55 -76 170 -76q78 0 131 37l-92 113h82l55 -66q51 76 52 226v649 q0 156 -58 227q-55 68 -169.5 68t-170.5 -68q-59 -76 -59 -227v-649z" />
<glyph unicode="r" horiz-adv-x="712" d="M23 2v1380h247q166 0 243 -89t77 -277q0 -184 -76 -275q-33 -39 -82 -63q39 -18 70 -49q92 -88 92 -244v-383h-66v383q0 125 -67.5 193.5t-190.5 68.5h-30h-154v-645h-63zM86 713h154h30q137 0 195 69q61 74 61 234q0 162 -59 231.5t-197 69.5h-184v-604z" />
<glyph unicode="s" horiz-adv-x="712" d="M20 1016q0 156 72 256q82 111 223 110q145 0 224 -96q74 -92 73 -270v-33h-65v33q0 156 -60 231q-55 70 -172 70q-111 0 -170 -84q-61 -84 -61 -217q0 -74 24.5 -125t60.5 -86t75 -55.5t69 -32.5l33 -15q27 -10 72 -31.5t88 -61.5t74.5 -101.5t31.5 -151.5 q0 -154 -73 -250q-80 -104 -224 -104q-147 0 -221 94q-74 88 -74 271v30h64v-30q0 -102 22.5 -162t56.5 -90.5t75 -39.5t77 -9q111 0 171.5 76.5t60.5 213.5q0 74 -25.5 124t-61.5 83t-75 51.5t-63 28.5l-35 14q-31 12 -76 36t-87 66t-73 104.5t-31 152.5z" />
<glyph unicode="t" horiz-adv-x="821" d="M10 1317v65h725v-65h-332v-1315h-63v1315h-330z" />
<glyph unicode="u" horiz-adv-x="712" d="M14 340v1042h64v-1042q0 -92 20.5 -146.5t51 -83t67.5 -36.5t72 -8q33 0 69.5 8t67.5 36.5t51.5 83t20.5 146.5v1042h63v-1042q0 -104 -25.5 -171t-64.5 -103.5t-88 -50t-94 -13.5q-47 0 -95.5 13.5t-88 50t-65.5 103.5t-26 171z" />
<glyph unicode="v" horiz-adv-x="821" d="M0 1382h66l305 -1216l303 1216h67l-346 -1380h-49z" />
<glyph unicode="w" horiz-adv-x="1333" d="M0 1380h66l266 -1198l264 1198h51l264 -1198l260 1198h68l-301 -1380h-53l-264 1198l-265 -1198h-51z" />
<glyph unicode="x" horiz-adv-x="802" d="M-8 0l327 690l-327 690h71l293 -614l287 614h72l-324 -690l328 -690h-72l-291 614l-293 -614h-71z" />
<glyph unicode="y" horiz-adv-x="854" d="M-20 1380h73l342 -622l342 622h74l-383 -698v-682h-63v682z" />
<glyph unicode="z" horiz-adv-x="837" d="M0 1317v63h672v-59l-590 -1258h600v-63h-672v63l588 1254h-598z" />
<glyph unicode="{" />
<glyph unicode="|" horiz-adv-x="182" d="M27 0v1380h63v-1380h-63z" />
<glyph unicode="}" />
<glyph unicode="~" />
<glyph unicode="&#xa9;" />
<glyph unicode="&#xad;" horiz-adv-x="612" d="M27 657v64h499v-64h-499z" />
<glyph unicode="&#xae;" />
<glyph unicode="&#x2000;" horiz-adv-x="690" />
<glyph unicode="&#x2001;" horiz-adv-x="1382" />
<glyph unicode="&#x2002;" horiz-adv-x="690" />
<glyph unicode="&#x2003;" horiz-adv-x="1382" />
<glyph unicode="&#x2004;" horiz-adv-x="460" />
<glyph unicode="&#x2005;" horiz-adv-x="344" />
<glyph unicode="&#x2006;" horiz-adv-x="229" />
<glyph unicode="&#x2007;" horiz-adv-x="229" />
<glyph unicode="&#x2008;" horiz-adv-x="172" />
<glyph unicode="&#x2009;" horiz-adv-x="276" />
<glyph unicode="&#x200a;" horiz-adv-x="75" />
<glyph unicode="&#x2010;" horiz-adv-x="612" d="M27 657v64h499v-64h-499z" />
<glyph unicode="&#x2011;" horiz-adv-x="612" d="M27 657v64h499v-64h-499z" />
<glyph unicode="&#x2012;" horiz-adv-x="612" d="M27 657v64h499v-64h-499z" />
<glyph unicode="&#x2013;" horiz-adv-x="612" d="M27 657v64h499v-64h-499z" />
<glyph unicode="&#x2014;" horiz-adv-x="612" d="M27 657v64h499v-64h-499z" />
<glyph unicode="&#x2018;" horiz-adv-x="165" d="M20 1200v180h64v-180h-64z" />
<glyph unicode="&#x2019;" horiz-adv-x="165" d="M20 1200v180h64v-180h-64z" />
<glyph unicode="&#x201c;" horiz-adv-x="307" d="M20 1200v180h64v-180h-64zM141 1200v180h66v-180h-66z" />
<glyph unicode="&#x201d;" horiz-adv-x="307" d="M20 1200v180h64v-180h-64zM141 1200v180h66v-180h-66z" />
<glyph unicode="&#x2022;" />
<glyph unicode="&#x2026;" horiz-adv-x="497" d="M25 0v88h65v-88h-65zM190 0v88h66v-88h-66zM356 0v88h66v-88h-66z" />
<glyph unicode="&#x202f;" horiz-adv-x="276" />
<glyph unicode="&#x205f;" horiz-adv-x="344" />
<glyph unicode="&#xe000;" horiz-adv-x="1380" d="M0 1380h1380v-1380h-1380v1380z" />
</font>
</defs></svg>

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -0,0 +1,160 @@
(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
});
} 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))

View File

@ -789,7 +789,6 @@ $.Widget.prototype = {
}
}
}
this.element.trigger( event, data );
return !( $.isFunction( callback ) &&
callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
@ -2368,11 +2367,9 @@ $.ui.ddmanager = {
if ( draggable.options.refreshPositions ) {
$.ui.ddmanager.prepareOffsets( draggable, event );
}
// Run through all droppables and check their positions based on specific tolerance options
$.each( $.ui.ddmanager.droppables[ draggable.options.scope ] || [], function() {
if ( this.options.disabled || this.greedyChild || !this.visible ) {
if ( this.options.disabled || this.greedyChild || !this.visible || $(this).data('dragMultipleActive')) {
return;
}
@ -4107,11 +4104,15 @@ return $.widget("ui.sortable", $.ui.mouse, {
//Rearrange
for (i = this.items.length - 1; i >= 0; i--) {
//Cache variables and intersection, continue if no intersection
item = this.items[i];
itemElement = item.item[0];
intersection = this._intersectsWithPointer(item);
if (item.item.data('dragMultipleActive')) {
continue;
}
if (!intersection) {
continue;
}
@ -4166,7 +4167,6 @@ return $.widget("ui.sortable", $.ui.mouse, {
},
_mouseStop: function(event, noPropagation) {
if(!event) {
return;
}
@ -4471,6 +4471,9 @@ return $.widget("ui.sortable", $.ui.mouse, {
for (i = this.items.length - 1; i >= 0; i--){
item = this.items[i];
if ($(item.item).data('dragMultipleActive')) {
continue;
}
//We ignore calculating positions of all connected containers when we're not over them
if(item.instance !== this.currentContainer && this.currentContainer && item.item[0] !== this.currentItem[0]) {
@ -4536,7 +4539,6 @@ return $.widget("ui.sortable", $.ui.mouse, {
return element;
},
update: function(container, p) {
// 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that
// 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified
if(className && !o.forcePlaceholderSize) {
@ -4661,7 +4663,6 @@ return $.widget("ui.sortable", $.ui.mouse, {
},
_createHelper: function(event) {
var o = this.options,
helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper === "clone" ? this.currentItem.clone() : this.currentItem);
@ -4888,7 +4889,6 @@ return $.widget("ui.sortable", $.ui.mouse, {
},
_rearrange: function(event, i, a, hardRefresh) {
a ? a[0].appendChild(this.placeholder[0]) : i.item[0].parentNode.insertBefore(this.placeholder[0], (this.direction === "down" ? i.item[0] : i.item[0].nextSibling));
//Various things done here to improve the performance:

View File

@ -21,6 +21,8 @@ block content
| {{ role.name }}
span ({{ role.members_count }} members with this role)
div.any-computable-role(ng-hide="anyComputableRole") Be careful, no role in your project will be able to estimate the point value for user stories
div.general-category
| When enabled, members assigned to this role will be able to estimate the point value for user stories
div.check

View File

@ -0,0 +1,14 @@
extends dummy-layout
block head
title Taiga Your agile, free, and open source project management tool
block content
div.wrapper
div.login-main
div.login-container
h1.logo
img.logo-svg(src="/svg/logo.svg", alt="TAIGA")
p.tagline Your agile, free, and open source project management tool
include views/modules/cancel-account-form

View File

@ -61,7 +61,6 @@ block content
ng-click="ctrl.openDeleteLightbox()") Delete Taiga account
div.lightbox.lightbox-delete-account.hidden(tg-lb-delete-user)
include views/modules/lightbox-delete-account
div.lightbox.lightbox-confirm-use-gravatar.hidden
include views/modules/lightbox-use-gravatar

View File

@ -0,0 +1,12 @@
div.change-email-form-container(tg-cancel-account)
p.change-password-text
strong Cancel your account <br />
span We're sorry you are leaving the taiga, we hope you enjoyed your stay :)
form(ng-submit="ctrl.submit()")
fieldset
input(type="hidden", name="cancel_token", ng-model="data.cancel_token", data-required="true",
placeholder="cancel account token")
a.button.button-cancel-account.button-gray(href="", title="Yes, I'm leaving") Yes, I'm leaving!
input(type="submit", style="display:none")

View File

@ -3,8 +3,8 @@
width: 100%;
.row {
@include table-flex(stretch, center, flex, row, nowrap, flex-start);
border-bottom: 1px solid $gray-light;
padding: .5rem 0;
border-bottom: 1px solid darken($whitish, 4%);
padding: .3rem 0;
text-align: left;
width: 100%;
@for $i from 1 through 8 {

View File

@ -1,8 +1,10 @@
.wysiwyg {
line-height: 1.4rem;
overflow: auto;
h1 {
@extend %xlarge;
@extend %text;
margin-bottom: .5rem;
line-height: 2.5rem;
text-transform: uppercase;
}
h2 {
@ -43,6 +45,7 @@
}
}
p {
line-height: 1.4rem;
margin-bottom: 1rem;
}
pre,
@ -56,7 +59,7 @@
white-space: pre;
}
pre {
line-height: 135%;
line-height: 1.4rem;
padding: .5rem;
}
table {

View File

@ -2,7 +2,7 @@
// Font face
@each $font-face in DroidSans, DroidSans-Bold, OpenSans-CondLight-webfont, taiga {
@each $font-face in OpenSans-CondLight, opensans-regular, opensans-semibold, taiga {
@font-face {
font-family: '#{$font-face}';
src: url('../fonts/#{$font-face}.eot?#iefix') format('embedded-opentype'),
@ -35,9 +35,9 @@ h6 {
%xxlarge {font-size: 3rem;}
// __Font Types__ //
%title {font-family: 'OpenSans-CondLight-webfont';}
%text {font-family: 'DroidSans';}
%bold {font-family: 'DroidSans-Bold';}
%title {font-family: 'OpenSans-CondLight';}
%text {font-family: 'opensans-regular'; line-height: 1.3rem;}
%bold {font-family: 'opensans-semibold';}
%taiga {font-family: 'taiga';}
h1 {

View File

@ -5,9 +5,9 @@ html {
width: 100%;
}
body {
@extend %text;
background: #fff; // fallback
color: #444;
font: 16px/21px 'DroidSans', Arial, sans-serif;
-webkit-font-smoothing: antialiased; // Fix for webkit renderin
height: 100%;
min-height: 100%;

View File

@ -11,6 +11,12 @@
padding-left: .5rem;
}
}
.any-computable-role {
background: $red;
color: $white;
margin-bottom: .5rem;
padding: .5rem;
}
.general-category {
align-items: center;
display: flex;

View File

@ -110,6 +110,7 @@
.backlog-table-body {
.row {
border-bottom: 1px solid darken($whitish, 4%);
cursor: move;
flex-wrap: nowrap;
position: relative;
@ -134,6 +135,14 @@
background: lighten($green-taiga, 60%);
box-shadow: 1px 1px 10px rgba($black, .1);
}
.points {
.not-clickable {
&:hover {
color: $black;
cursor: text;
}
}
}
}
.row-selected {
@include transition (background .2s ease-in);

View File

@ -382,6 +382,17 @@
@extend %large;
@extend %title;
}
.newsletter-delete {
margin-top: 1rem;
text-align: center;
input {
margin-right: .5rem;
+label {
@extend %small;
@extend %text;
}
}
}
.delete-options {
@include table-flex();
a {

View File

@ -86,6 +86,10 @@
img {
margin: 0 5px 10px;
width: 80%;
&:hover {
@include transition(border-color .3s linear);
border-color: $fresh-taiga;
}
}
.user-settings {
position: relative;

View File

@ -169,7 +169,7 @@
}
}
figcaption {
max-width: 60%;
max-width: 50%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;

View File

@ -30,7 +30,7 @@
}
.table-main {
@extend %small;
border-bottom: 1px solid $gray-light;
border-bottom: 1px solid darken($whitish, 4%);
}
.avatar {
@include table-flex(stretch, center, flex, row, wrap, flex-start);

View File

@ -3,6 +3,7 @@
"eventsUrl": "ws://localhost:8888/events",
"debug": "true",
"publicRegisterEnabled": true,
"feedbackEnabled": true,
"privacyPolicyUrl": null,
"termsOfServiceUrl": null
}

86
extras/humans.txt Normal file
View File

@ -0,0 +1,86 @@
/* the humans responsible & colophon */
/* humanstxt.org */
/* TEAM */
Taiga Engineer & Digital Hermit: Lord Juan Francisco Alcántara
Site: http://www.kaleidos.net/40826D/
Twitter: nil
Location: Madrid, Spain
Taiga Engineer & Spartan: Lord Alejandro Alonso
Site: http://kaleidos.net/FC8EAC/
Twitter: @_superalex_
Location: Madrid, Spain
Taiga Engineer & Vodka distiller: Lord Andrey Antukh
Site: http://kaleidos.net/A5694F/
Twitter: @niwibe
Location: Madrid, Russia
Taiga Engineer: Lord David Barragán Merino
Site: http://kaleidos.net/FFF8E7/
Twitter: @bameda
Location: Madrid, Spain
Taiga Engineer & Troll Master: Lord Jesús Espino García
Site: http://kaleidos.net/007000/
Twitter: @jespinog
Location: Madrid, Spain
Taiga UX Consultant & Mistress of the Dark: Pilar Esteban
Site: https://www.linkedin.com/in/pilaresteban
Twitter: @devilme
Location: Madrid, Spain
Taiga Engineer & African Dancer: Anler Hernández
Site: http://www.kaleidos.net/2099DB/
Twitter: @anler
Location: Madrid, Cuba
Taiga UI Designer, Frontend Engineer & Paella Masterchef: Lord Xavier Julián
Site: http://kaleidos.net/CC0000/
Twitter: @Xaviju
Location: Madrid, Spain
Taiga UI Designer: Juan de la Cruz
Site: http://kaleidos.net
Twitter: @elhombretecla
Location: Madrid, Spain
Taiga CEO, Community Manager & Chico Almodobar: Ricky Posner
Twitter: @eposner
Location: Madrid, Spain
Taiga CEO & Dungeon Master: Lord Pablo Ruiz Múzquiz
Site: http://kaleidos.net/761CEC/
Twitter: @diacritica
Location: Madrid, Spain
/* SITE */
Standards: HTML5, CSS3
Components: Django, AngularJS, jQuery
Software: PostgreSQL, RabbitMQ, Redis
Languajes: Python, CoffeeScript, Jade, Sass
.,,.
,;;*;;;;, for ponies with
.-'``;-');;. magical powers!
/' .-. /*;;
.' \d \;; .;;;,
/ o ` \; ,__. ,;*;;;*;,
\__, _.__,' \_.-') __)--.;;;;;*;;;;,
`""`;;;\ /-')_) __) `\' ';;;;;;
;*;;; -') `)_) |\ | ;;;;*;
;;;;| `---` O | | ;;*;;;
*;*;\| O / ;;;;;*
;;;;;/| .-------\ / ;*;;;;;
;;;*;/ \ | '. (`. ;;;*;;;
;;;;;'. ; | ) \ | ;;;;;;
,;*;;;;\/ |. / /` | ';;;*;
;;;;;;/ |/ / /__/ ';;;
'*jgs/ | / | ;*;
`""""` `""""` ;'

6
extras/robots.txt Normal file
View File

@ -0,0 +1,6 @@
User-agent: *
Disallow:
# You rush a miracle man, you get rotten miracles.
#
# -- Miracle Max --

View File

@ -7,6 +7,7 @@ uglify = require("gulp-uglify")
plumber = require("gulp-plumber")
wrap = require("gulp-wrap")
rename = require("gulp-rename")
flatten = require('gulp-flatten')
minifyHTML = require("gulp-minify-html")
sass = require("gulp-ruby-sass")
@ -23,10 +24,12 @@ paths = {}
paths.app = "app/"
paths.dist = "dist/"
paths.tmp = "tmp/"
paths.extras = "extras/"
paths.jade = [
paths.app + "index.jade",
paths.app + "partials/**/*.jade"
paths.app + "partials/**/*.jade",
paths.app + "plugins/**/*.jade"
]
paths.images = paths.app + "images/**/*"
@ -36,6 +39,7 @@ paths.locales = paths.app + "locales/**/*.json"
paths.sass = [
paths.app + "styles/**/*.scss"
"!#{paths.app}/styles/bourbon/**/*.scss"
paths.app + "plugins/**/*.scss"
]
paths.coffee = [
@ -81,8 +85,9 @@ paths.js = [
paths.app + "vendor/jquery-textcomplete/jquery.textcomplete.js",
paths.app + "vendor/markitup/markitup/jquery.markitup.js",
paths.app + "vendor/malihu-custom-scrollbar-plugin/jquery.mCustomScrollbar.concat.min.js",
paths.app + "js/jquery.ui.git.js",
paths.app + "js/sha1.js",
paths.app + "js/jquery.ui.git-custom.js",
paths.app + "js/jquery-ui.drag-multiple-custom.js",
paths.app + "js/sha1-custom.js",
paths.app + "plugins/**/*.js"
]
@ -117,15 +122,17 @@ gulp.task "sass-lint", ->
.pipe(scsslint({config: "scsslint.yml"}))
gulp.task "sass-watch", ["sass-lint"], ->
gulp.src(paths.app + "styles/main.scss")
gulp.src(["#{paths.app}/styles/main.scss", "#{paths.app}/plugins/**/*.scss"])
.pipe(plumber())
.pipe(concat("all.scss"))
.pipe(sass())
.pipe(rename("app.css"))
.pipe(gulp.dest(paths.tmp))
gulp.task "sass-deploy", ->
gulp.src(paths.app + "styles/main.scss")
gulp.src(["#{paths.app}/styles/main.scss", "#{paths.app}/plugins/**/*.scss"])
.pipe(plumber())
.pipe(concat("all.scss"))
.pipe(sass())
.pipe(rename("app.css"))
.pipe(gulp.dest(paths.tmp))
@ -169,7 +176,7 @@ gulp.task "conf", ->
gulp.src("conf/main.json")
.pipe(wrap("angular.module('taigaBase').value('localconf', <%= contents %>);"))
.pipe(concat("conf.js"))
.pipe(gulp.dest(paths.tmp));
.pipe(gulp.dest(paths.tmp))
gulp.task "locales", ->
gulp.src("app/locales/en/app.json")
@ -206,7 +213,7 @@ gulp.task "app-watch", ["coffee", "conf", "locales"], ->
gulp.src(_paths)
.pipe(concat("app.js"))
.pipe(gulp.dest(paths.dist + "js/"));
.pipe(gulp.dest(paths.dist + "js/"))
gulp.task "app-deploy", ["coffee", "conf", "locales"], ->
_paths = [
@ -218,7 +225,7 @@ gulp.task "app-deploy", ["coffee", "conf", "locales"], ->
gulp.src(_paths)
.pipe(concat("app.js"))
.pipe(uglify({mangle:false, preserveComments: false}))
.pipe(gulp.dest(paths.dist + "js/"));
.pipe(gulp.dest(paths.dist + "js/"))
##############################################################################
# Common tasks
@ -237,11 +244,20 @@ gulp.task "copy-images", ->
gulp.src("#{paths.app}/images/**/*")
.pipe(gulp.dest("#{paths.dist}/images/"))
gulp.src("#{paths.app}/plugins/**/images/*")
.pipe(flatten())
.pipe(gulp.dest("#{paths.dist}/images/"))
gulp.task "copy-plugin-templates", ->
gulp.src("#{paths.app}/plugins/**/templates/*")
.pipe(gulp.dest("#{paths.dist}/plugins/"))
gulp.task "copy", ["copy-fonts", "copy-images", "copy-plugin-templates", "copy-svg"]
gulp.task "copy-extras", ->
gulp.src("#{paths.extras}/*")
.pipe(gulp.dest("#{paths.dist}/"))
gulp.task "copy", ["copy-fonts", "copy-images", "copy-plugin-templates", "copy-svg", "copy-extras"]
gulp.task "express", ->
express = require("express")

View File

@ -27,6 +27,7 @@
"gulp-coffeelint": "~0.4.0",
"gulp-concat": "^2.1.7",
"gulp-csslint": "^0.1.5",
"gulp-flatten": "0.0.4",
"gulp-if": "0.0.5",
"gulp-jade": "^0.5.0",
"gulp-jade-inheritance": "0.0.4",