Commit 5b354e33 by Hiếu Lê

Update easy_gantt

parent eff2e526
...@@ -128,6 +128,9 @@ GEM ...@@ -128,6 +128,9 @@ GEM
redcarpet (3.4.0) redcarpet (3.4.0)
redmine_crm (0.0.34) redmine_crm (0.0.34)
liquid (< 2.6.4) liquid (< 2.6.4)
redmine_extensions (0.2.14)
actionpack (>= 4.2, < 6)
rails (>= 4.2, < 6)
request_store (1.0.5) request_store (1.0.5)
rmagick (2.16.0) rmagick (2.16.0)
roadie (3.2.2) roadie (3.2.2)
...@@ -190,6 +193,7 @@ DEPENDENCIES ...@@ -190,6 +193,7 @@ DEPENDENCIES
rdoc (~> 4.3) rdoc (~> 4.3)
redcarpet (~> 3.4.0) redcarpet (~> 3.4.0)
redmine_crm redmine_crm
redmine_extensions
request_store (= 1.0.5) request_store (= 1.0.5)
rmagick (>= 2.14.0) rmagick (>= 2.14.0)
roadie (~> 3.2.1) roadie (~> 3.2.1)
......
gem 'redmine_extensions' unless Dir.exist?(File.expand_path('../../easyproject', __FILE__))
# Easy Gantt
For documentation and requirements, go to https://www.easyredmine.com/redmine-gantt-plugin
= easy_gantt
== REQUIREMENTS
* Redmine 3+
* You need to REST api enabled first (Administration > Settings > Authentication)
== INSTALLATION
1. Copy your plugin directory into #{RAILS_ROOT}/plugins.
2. Restart Redmine
You should now be able to see the plugin list in Administration -> Plugins.
easy_extensions = Redmine::Plugin.installed?(:easy_extensions)
app_dir = File.join(File.dirname(__FILE__), 'app')
lib_dir = File.join(File.dirname(__FILE__), 'lib', 'easy_gantt')
# Redmine patches
patch_path = File.join(lib_dir, 'redmine_patch', '**', '*.rb')
Dir.glob(patch_path).each do |file|
require file
end
ActiveSupport::Dependencies.autoload_paths << File.join(app_dir, 'models', 'queries')
if easy_extensions
ActiveSupport::Dependencies.autoload_paths << File.join(app_dir, 'models', 'easy_queries')
EasyQuery.register(EasyGanttEasyIssueQuery)
EasyQuery.register(EasyGanttEasyProjectQuery)
# RedmineExtensions::QueryOutput.whitelist_outputs_for_query 'EasyGanttEasyIssueQuery', 'list'
# RedmineExtensions::QueryOutput.whitelist_outputs_for_query 'EasyGanttEasyProjectQuery', 'list'
Rails.application.configure do
config.assets.precompile.concat [
proc { |logical_path, filename|
if logical_path.start_with?('easy_gantt/')
basename = File.basename(logical_path)
if basename.start_with?('_')
false
else
true
end
end
}
]
end
end
# this block is called every time rails are reloading code
# in development it means after each change in observed file
# in production it means once just after server has started
# in this block should be used require_dependency, but only if necessary.
# better is to place a class in file named by rails naming convency and let it be loaded automatically
# Here goes query registering, custom fields registering and so on
ActionDispatch::Reloader.to_prepare do
require 'easy_gantt/easy_gantt'
require 'easy_gantt/hooks'
RedmineExtensions::EasySettingPresenter.boolean_keys << :easy_gantt_show_holidays << :easy_gantt_show_project_progress << :easy_gantt_show_lowest_progress_tasks << :easy_gantt_show_task_soonest_start << :easy_gantt_relation_delay_in_workdays << :easy_gantt_fixed_delay
end
Redmine::MenuManager.map :top_menu do |menu|
menu.push(:easy_gantt, { controller: 'easy_gantt', action: 'index', set_filter: 0 },
caption: :label_easy_gantt,
after: :documents,
html: { class: 'icon icon-stats' },
if: proc { User.current.allowed_to_globally?(:view_global_easy_gantt) })
end
Redmine::MenuManager.map :project_menu do |menu|
menu.push(:easy_gantt, { controller: 'easy_gantt', action: 'index' },
param: :project_id,
caption: :button_project_menu_easy_gantt,
if: proc { |p| User.current.allowed_to?(:view_easy_gantt, p) })
end
Redmine::MenuManager.map :easy_gantt_tools do |menu|
menu.push(:back, 'javascript:void(0)',
param: :project_id,
caption: :button_back,
html: { icon: 'icon-back' })
menu.push(:task_control, 'javascript:void(0)',
param: :project_id,
caption: :button_edit,
html: { icon: 'icon-edit' })
menu.push(:add_task, 'javascript:void(0)',
param: :project_id,
caption: :label_new,
html: { trial: true, icon: 'icon-add' },
if: proc { |p| p.present? })
menu.push(:critical, 'javascript:void(0)',
param: :project_id,
caption: :'easy_gantt.button.critical_path',
html: { trial: true, icon: 'icon-summary' },
if: proc { |p| p.present? })
menu.push(:baseline, 'javascript:void(0)',
param: :project_id,
caption: :'easy_gantt.button.create_baseline',
html: { trial: true, icon: 'icon-projects icon-project' },
if: proc { |p| p.present? })
menu.push(:resource, proc { |project| defined?(EasyUserAllocations) ? { controller: 'user_allocation_gantt', project_id: project } : nil },
param: :project_id,
caption: :'easy_gantt.button.resource_management',
html: { trial: true, icon: 'icon-stats', easy_text: defined?(EasyExtensions) },
if: proc { |p| p.present? })
end
# this block is executed once just after Redmine is started
# means after all plugins are initialized
# it is place for plain requires, not require_dependency
# it should contain hooks, permissions - base class in Redmine is required thus is not reloaded
ActiveSupport.on_load(:easyproject, yield: true) do
if easy_extensions
Redmine::MenuManager.map :projects_easy_page_layout_service_box do |menu|
menu.push(:project_easy_gantt, :easy_gantt_path,
caption: :label_easy_gantt,
html: { class: 'icon icon-stats button button-2' },
if: proc { User.current.allowed_to_globally?(:view_global_easy_gantt) })
end
Redmine::MenuManager.map :easy_quick_top_menu do |menu|
menu.push(:project_easy_gantt, { controller: 'easy_gantt', action: 'index', id: nil, set_filter: 0 },
caption: :label_easy_gantt,
parent: :projects,
html: { class: 'icon icon-stats' },
if: proc { User.current.allowed_to_globally?(:view_global_easy_gantt) })
menu.push(:issues_easy_gantt, { controller: 'easy_gantt', action: 'index', id: nil, set_filter: 0 },
caption: :label_easy_gantt,
parent: :issues,
html: { class: 'icon icon-stats project-gantt' },
if: proc { User.current.allowed_to_globally?(:view_global_easy_gantt) })
end
end
end
ActionDispatch::Reloader.to_prepare do
Redmine::AccessControl.map do |map|
map.project_module :easy_gantt do |pmap|
# View project level
pmap.permission(:view_easy_gantt, {
easy_gantt: [:index, :issues, :projects],
easy_gantt_pro: [:lowest_progress_tasks, :cashflow_data],
easy_gantt_resources: [:index, :project_data, :users_sums, :projects_sums, :allocated_issues],
easy_resource_limits: [:index]
}, read: true)
# Edit project level
pmap.permission(:edit_easy_gantt, {
easy_gantt: [:change_issue_relation_delay, :reschedule_project],
easy_gantt_resources: [:bulk_update_or_create],
easy_resource_limits: [:new, :create, :edit, :update, :destroy]
}, require: :member)
# View global level
pmap.permission(:view_global_easy_gantt, {
easy_gantt: [:index, :issues, :projects, :project_issues],
easy_gantt_pro: [:lowest_progress_tasks, :cashflow_data],
easy_gantt_resources: [:index, :project_data, :global_data, :projects_sums, :allocated_issues],
easy_resource_limits: [:index]
}, global: true, read: true)
# Edit global level
pmap.permission(:edit_global_easy_gantt, {
easy_gantt_resources: [:bulk_update_or_create],
easy_resource_limits: [:new, :create, :edit, :update, :destroy]
}, global: true, require: :loggedin)
# View personal level
pmap.permission(:view_personal_easy_gantt, {
easy_gantt_resources: [:global_data],
easy_resource_limits: [:index]
}, global: true, read: true)
# Edit personal level
pmap.permission(:edit_personal_easy_gantt, {
easy_gantt_resources: [:bulk_update_or_create],
easy_resource_limits: [:new, :create, :edit, :update, :destroy]
}, global: true, require: :loggedin)
end
end
end
module EasyGanttHelper
def easy_gantt_include_js(*javascripts, from_plugin: 'easy_gantt')
plugin = (EasyGantt.easy_extensions? ? nil : from_plugin)
result = ''
javascripts.flatten!
javascripts.compact!
javascripts.each do |javascript|
result << javascript_include_tag("#{from_plugin}/#{javascript}", plugin: plugin)
end
result.html_safe
end
def easy_gantt_include_css(*stylesheets, media: 'screen', from_plugin: 'easy_gantt')
plugin = (EasyGantt.easy_extensions? ? nil : from_plugin)
result = ''
stylesheets.flatten!
stylesheets.compact!
stylesheets.each do |stylesheet|
result << stylesheet_link_tag("#{from_plugin}/#{stylesheet}", plugin: plugin, media: media)
end
result.html_safe
end
def easy_gantt_js_button(text, options={})
if text.is_a?(Symbol)
lang_key = text
text = l(lang_key, scope: [:easy_gantt, :button])
options[:title] ||= l(lang_key, scope: [:easy_gantt, :title], default: text)
end
options[:class] = "gantt-menu-button #{options[:class]}"
options[:class] << ' button' unless options.delete(:no_button)
if (icon = options.delete(:icon))
options[:class] << " icon #{icon}"
end
link_to(text, options[:url] || 'javascript:void(0)', options)
end
def easy_gantt_help_button(*args)
options = args.extract_options!
feature = args.shift
text = args.shift
options[:class] = "gantt-menu-help-button #{options[:class]}"
options[:icon] ||= 'icon-help'
options[:id] = 'button_' + feature.to_s + '_help'
help_text = raw l(options.delete(:easy_text) ? :easy_text : :text, scope: [:easy_gantt, :popup, feature])
easy_gantt_js_button(text || '&#8203;'.html_safe, options) + %Q(
<div id="#{feature}_help_modal" style="display:none">
<h3 class="title">#{raw l(:heading, scope: [:easy_gantt, :popup, feature]) }</h3>
<p>#{help_text}</p>
</div>
).html_safe
end
def api_render_versions(api, versions)
return if versions.blank?
api.array :versions do
versions.each do |version|
api.version do
api.id version.id
api.name version.name
api.start_date version.effective_date
api.project_id version.project_id
api.permissions do
api.editable version.gantt_editable?
end
end
end
end
end
def api_render_columns(api, query)
api.array :columns do
query.columns.each do |c|
api.column do
api.name c.name
api.title c.caption
end
end
end
end
def api_render_scheme(api, table)
if table.is_a?(Symbol)
if Object.const_defined?(table)
table = Object.const_get(table)
else
return
end
end
return unless table.column_names.include?('easy_color_scheme')
col = table.arel_table[:easy_color_scheme]
records = table.where(col.not_eq(nil).and(col.not_eq(''))).pluck(:id, :easy_color_scheme)
api.array table.to_s do
records.each do |id, scheme|
api.entity do
api.id id
api.scheme scheme
end
end
end
end
def api_render_holidays(api, startdt, enddt)
wc = User.current.try(:current_working_time_calendar)
return if wc.nil?
api.array :holidays do
startdt.upto(enddt) do |date|
api.date date if wc.holiday?(date)
end
end
end
def api_render_issues(api, issues, with_columns: false)
api.array :issues do
issues.each do |issue|
api.issue do
api.id issue.id
api.name issue.subject
api.start_date issue.start_date
api.due_date issue.due_date
api.estimated_hours issue.estimated_hours
api.done_ratio issue.done_ratio
api.closed issue.closed?
api.fixed_version_id issue.fixed_version_id
api.overdue issue.overdue?
api.parent_issue_id issue.parent_id
api.project_id issue.project_id
api.tracker_id issue.tracker_id
api.priority_id issue.priority_id
api.status_id issue.status_id
api.assigned_to_id issue.assigned_to_id
if EasySetting.value(:easy_gantt_show_task_soonest_start) && @project.nil?
api.soonest_start issue.soonest_start
end
if EasySetting.value(:easy_gantt_show_task_latest_due) && @project.nil?
api.latest_due issue.latest_due
end
api.is_planned !!issue.project.try(:is_planned)
api.permissions do
api.editable issue.gantt_editable?
end
if with_columns
api.array :columns do
@query.columns.each do |c|
api.column do
api.name c.name
api.value gantt_format_column(issue, c, c.value(issue))
end
end
end
end
end
end
end
end
def api_render_relations(api, relations)
api.array :relations do
relations.each do |rel|
api.relation do
api.id rel.id
api.source_id rel.issue_from_id
api.target_id rel.issue_to_id
api.type rel.relation_type
api.delay rel.delay.to_i
end
end
end
end
def api_render_projects(api, projects, with_columns: false)
api.array :projects do
projects.each do |project|
api.project do
api.id project.id
api.name project.name
api.start_date project.gantt_start_date || Date.today
api.due_date project.gantt_due_date || Date.today
api.parent_id project.parent_id
api.is_baseline project.try(:easy_baseline_for_id?)
# Schema
api.status_id project.status
api.priority_id project.try(:easy_priority_id)
api.permissions do
api.editable project.gantt_editable?
end
if EasySetting.value(:easy_gantt_show_project_progress)
api.done_ratio project.gantt_completed_percent
end
if @projects_issues_counts && @projects_issues_counts.has_key?(project.id)
api.issues_count @projects_issues_counts[project.id]
end
if @parent_ids && @parent_ids.include?(project.id)
api.has_subprojects true
end
if with_columns
api.array :columns do
@query.columns.each do |c|
api.column do
api.name c.name
api.value gantt_format_column(project, c, c.value(project))
end
end
end
end
end
end
end
end
# This method exist because
# 1. EntityAttributeHelper is for complex html formating
# 2. Redmine doest not have it
# Gantt should show light and non-html values
def gantt_format_column(entity, column, value)
if entity.is_a?(Project) && column.name == :status && respond_to?(:format_project_attribute)
format_project_attribute(Project, column, value)
elsif value.is_a?(Float)
locale = User.current.language.presence || ::I18n.locale
number_with_precision(value, locale: locale).to_s
elsif value.is_a?(Array)
value.join(', ')
else
value.to_s
end
end
def prepare_test_includes(tests)
includes = []
tests.each do |test_file|
plugin = 'easy_gantt'
if test_file.include?('/')
split = test_file.split('/')
plugin = split[0]
test_file = split[1]
end
includes << [test_file, plugin]
end
includes
end
end
module EasyGanttsHelper
def easy_gantt_js_button(text, options={})
if text.is_a?(Symbol)
text = l(text, :scope => [:easy_gantt, :button])
options[:title] ||= l(text, :scope => [:easy_gantt, :title], :default => text)
end
options[:class] = "gantt_button #{options[:class]}"
options[:class] << ' button button-2' unless options.delete(:no_button)
if (icon = options.delete(:icon))
options[:class] << " icon #{icon}"
end
link_to(text, options[:url] || 'javascript:void(0)', options)
end
def easy_gantt_help_button(*args)
options = args.extract_options!
feature = args.shift
text = args.shift
options[:class] = "gantt-help-button #{options[:class]}"
options[:icon] ||= 'icon-help'
options[:id] = feature.to_s + '_help'
easy_gantt_js_button(text || '&#8203;'.html_safe, options) + %Q(
<div id="#{feature}_help_modal" style="display:none">
<h3 class="title">#{raw l(:heading, :scope => [:easy_gantt, :popup, feature]) }</h3>
<p>#{raw l(:text, :scope => [:easy_gantt, :popup, feature]) }</p>
</div>
).html_safe
end
end
module EasyGantt module EasyGantt
class EasyGanttIssueQuery < IssueQuery class EasyGanttIssueQuery < IssueQuery
def from_params(params) attr_accessor :entity_scope
build_from_params(params) attr_accessor :opened_project
def default_columns_names
[:subject, :priority, :assigned_to]
end end
def entity def entity
Issue Issue
end end
def from_params(params)
build_from_params(params)
end
def to_params
params = { set_filter: 1, type: self.class.name, f: [], op: {}, v: {} }
filters.each do |filter_name, opts|
params[:f] << filter_name
params[:op][filter_name] = opts[:operator]
params[:v][filter_name] = opts[:values]
end
params[:c] = column_names
params
end
def to_partial_path
'easy_gantt/easy_queries/show'
end
def initialize_available_filters
super
@available_filters.delete('subproject_id')
end
def entity_scope def entity_scope
scope = Issue.visible @entity_scope ||= begin
if Project.column_names.include? 'easy_baseline_for_id' scope = Issue.visible
scope = scope.where(Project.table_name => {easy_baseline_for_id: nil}) if Project.column_names.include? 'easy_baseline_for_id'
scope = scope.where(Project.table_name => {easy_baseline_for_id: nil})
end
scope
end end
scope
end end
def create_entity_scope(options={}) def create_entity_scope(options={})
entity_scope.includes(options[:includes]).references(options[:includes]).preload(options[:preload]).where(statement) entity_scope.includes(options[:includes]).
references(options[:includes]).
preload(options[:preload]).
where(statement).
where(options[:conditions])
end end
def entities(options={}) def entities(options={})
create_entity_scope(options).order(options[:order]) create_entity_scope(options).order(options[:order])
end end
def to_partial_path def project_statement
'easy_gantt/easy_queries/show' p_table = Project.table_name
conditions = "#{p_table}.status = #{Project::STATUS_ACTIVE}"
if opened_project
conditions = "#{conditions} AND #{Project.table_name}.id = #{opened_project.id}"
end
conditions
end end
def default_columns_names def without_opened_project
columns_names = [:subject, :priority, :assigned_to] _opened_project = opened_project
#columns_names.unshift(:project) if project.nil? self.opened_project = nil
columns_names yield self
ensure
self.opened_project = _opened_project
end end
end end
end end
class EasyGanttEasyIssueQuery < EasyIssueQuery
KEEP_AVAILABLE_COLUMNS = [
# Issues
:id, :project, :parent, :status, :tracker, :priority, :fixed_version, :subject, :start_date, :due_date, :created_on, :updated_on, :easy_status_updated_on, :open_duration_in_hours, :easy_last_updated_by, :done_ratio, :easy_due_date_time_remaining, :closed_on, :easy_closed_by, :is_private, :category, :assigned_to, :author, :estimated_hours, :total_estimated_hours, :spent_hours, :total_spent_hours, :remaining_timeentries, :total_remaining_timeentries, :spent_estimated_timeentries, :total_spent_estimated_timeentries,
# Projects
:"projects.name", :"projects.parent", :"projects.root", :"projects.status", :"projects.start_date", :"projects.due_date", :"projects.created_on", :"projects.updated_on", :"projects.easy_indicator", :"projects.identifier", :"projects.completed_percent", :"projects.priority", :"projects.sum_estimated_hours", :"projects.total_sum_estimated_hours", :"projects.sum_of_timeentries", :"projects.remaining_timeentries", :"projects.total_remaining_timeentries", :"projects.total_spent_hours", :"projects.author"
]
attr_accessor :opened_project
def query_after_initialize
super
self.display_filter_group_by_on_index = false
self.display_filter_sort_on_index = false
self.display_filter_settings_on_index = false
self.display_filter_group_by_on_edit = false
self.display_filter_sort_on_edit = false
self.display_filter_settings_on_edit = false
self.display_show_sum_row = false
self.display_load_groups_opened = false
self.export_formats = {}
self.is_tagged = true if new_record?
end
def available_outputs
[]
end
def groupable_columns
[]
end
def available_outputs
['list']
end
def default_list_columns
super.presence || ['subject', 'assigned_to']
end
def easy_query_entity_controller
'easy_gantt'
end
def easy_query_entity_action
'index'
end
def column_groups_ordering
[
l(:label_most_used),
l("label_filter_group_#{class_name_underscored}"),
EasyQuery.column_filter_group_name(nil),
l(:label_filter_group_easy_time_entry_query),
l(:label_filter_group_status_time),
l(:label_filter_group_status_count),
l(:label_filter_group_easy_project_query),
EasyQuery.column_filter_group_name(:project),
l(:label_user_plural)
]
end
def entity_easy_query_path(**options)
project = options[:project] || project
if project
easy_gantt_path(project, options)
end
end
# Delete these filters because of project_scope
def initialize_available_filters
super
@available_filters.delete('subproject_id')
end
def initialize_available_columns
super
add_associated_columns EasyProjectQuery
@available_columns.keep_if { |column|
KEEP_AVAILABLE_COLUMNS.include?(column.name) ||
column.name.to_s.start_with?('cf_') ||
column.name.to_s.start_with?('projects.cf_')
}
end
def project_scope
# Except closed and archived
scope = Project.active_and_planned
# Templates should be included only on template context
if project.nil? || !project.easy_is_easy_template?
scope = scope.non_templates
end
if opened_project
scope = scope.where(id: opened_project.id)
end
if has_filter?('is_planned')
op = value_for('is_planned') == '1' ? '=' : '<>'
scope = scope.where("#{Project.table_name}.status #{op} ?", Project::STATUS_PLANNED)
end
scope
end
def without_opened_project
_opened_project = opened_project
self.opened_project = nil
self.additional_scope = nil
yield self
ensure
self.opened_project = _opened_project
self.additional_scope = nil
end
def self.chart_support?
false
end
end
class EasyGanttEasyProjectQuery < EasyProjectQuery
attr_accessor :opened_project
def query_after_initialize
super
self.display_filter_group_by_on_index = false
self.display_filter_sort_on_index = false
self.display_filter_settings_on_index = false
self.display_filter_group_by_on_edit = false
self.display_filter_sort_on_edit = false
self.display_filter_settings_on_edit = false
self.display_show_sum_row = false
self.display_load_groups_opened = false
self.export_formats = {}
self.is_tagged = true if new_record?
end
def groupable_columns
[]
end
def available_outputs
['list']
end
def default_list_columns
super.presence || ['name']
end
def filter_groups_ordering
[
l(:label_most_used),
l(:label_filter_group_easy_project_query),
EasyQuery.column_filter_group_name(nil)
]
end
def column_groups_ordering
[
l(:label_most_used),
l(:label_filter_group_easy_project_query),
EasyQuery.column_filter_group_name(nil),
l(:label_filter_group_easy_time_entry_query),
l(:label_user_plural)
]
end
def easy_query_entity_controller
'easy_gantt'
end
def easy_query_entity_action
'index'
end
def entity_easy_query_path(**options)
easy_gantt_path(options)
end
def additional_scope
if opened_project
Project.where(id: opened_project.id)
else
nil
end
end
def without_opened_project
_opened_project = opened_project
self.opened_project = nil
self.additional_scope = nil
yield self
ensure
self.opened_project = _opened_project
self.additional_scope = nil
end
def self.chart_support?
false
end
end
class EasyGanttProjectQuery < Query
attr_accessor :opened_project
self.queried_class = Project
self.available_columns = [
QueryColumn.new(:name, sortable: "#{Project.table_name}.name"),
QueryColumn.new(:created_on, sortable: "#{Project.table_name}.created_on"),
QueryColumn.new(:updated_on, sortable: "#{Project.table_name}.updated_on"),
]
def initialize(*args)
super
self.filters ||= {}
end
def default_columns_names
[:name]
end
def initialize_available_filters
add_available_filter 'name', type: :text
add_available_filter 'created_on', type: :date_past
add_available_filter 'updated_on', type: :date_past
end
def from_params(params)
build_from_params(params)
end
def to_params
params = { set_filter: 1, type: self.class.name, f: [], op: {}, v: {} }
filters.each do |filter_name, opts|
params[:f] << filter_name
params[:op][filter_name] = opts[:operator]
params[:v][filter_name] = opts[:values]
end
params[:c] = column_names
params
end
def to_partial_path
'easy_gantt/easy_queries/show'
end
def entities(options={})
scope = Project.active.visible
if Project.column_names.include?('easy_baseline_for_id')
scope = scope.where(Project.table_name => { easy_baseline_for_id: nil })
end
scope = scope.includes(options[:includes]).
references(options[:includes]).
preload(options[:preload]).
where(statement).
where(options[:conditions]).
order(options[:order])
if opened_project
scope = scope(projects: { id: opened_project.id })
end
scope.to_a
end
def entity_scope
Project.visible
end
def create_entity_scope(options={})
entity_scope.includes(options[:includes]).
references(options[:includes]).
preload(options[:preload]).
where(statement).
where(options[:conditions])
end
def without_opened_project
_opened_project = opened_project
self.opened_project = nil
yield self
ensure
self.opened_project = _opened_project
end
end
<p class="error">
<%= l(:error_epm_easy_gantt_already_active) %>
</p>
<% heads_for_wiki_formatter %> <%
heads_for_wiki_formatter
if EasyGantt.easy_extensions?
main_css = 'easy_gantt'
else
main_css = 'generated/easy_gantt'
end
%>
<%= content_for :header_tags do %> <%= content_for :header_tags do %>
<%= stylesheet_link_tag(:dhtmlxgantt, :plugin => 'easy_gantt', :media => 'all') %> <%= easy_gantt_include_css(main_css, media: 'all') %>
<%= stylesheet_link_tag(:easy_gantt, :plugin => 'easy_gantt', :media => 'all') %> <% if EasyGantt.combine_by_pipeline?(params) %>
<%= javascript_include_tag(:dhtmlxgantt, :plugin => 'easy_gantt') %> <%= javascript_include_tag('easy_gantt/easy_gantt') %>
<%= javascript_include_tag(:dhtmlxgantt_marker, :plugin => 'easy_gantt') %> <% else %>
<%= javascript_include_tag(:easy_gantts, :plugin => 'easy_gantt') %> <%= easy_gantt_include_js(
<%= javascript_include_tag(:data, :plugin => 'easy_gantt') %> 'utils',
<%= javascript_include_tag(:loader, :plugin => 'easy_gantt') %> 'dhtmlxgantt',
<%= javascript_include_tag(:saver, :plugin => 'easy_gantt') %> 'dhtmlxgantt_marker',
<%= javascript_include_tag(:logger, :plugin => 'easy_gantt') %> 'main',
<%= javascript_include_tag(:test, :plugin => 'easy_gantt') %> 'data',
<%= javascript_include_tag(:widget, :plugin => 'easy_gantt') %> 'loader',
<%= javascript_include_tag(:panel_widget, :plugin => 'easy_gantt') %> 'saver',
<%= javascript_include_tag(:gantt_widget, :plugin => 'easy_gantt') %> 'logger',
<%= javascript_include_tag(:libs, :plugin => 'easy_gantt') %> 'widget',
<%= javascript_include_tag(:view, :plugin => 'easy_gantt') %> 'panel_widget',
<%= javascript_include_tag(:history, :plugin => 'easy_gantt') %> 'gantt_widget',
<%= javascript_include_tag(:dhtml_modif, :plugin => 'easy_gantt') %> 'view',
<%= javascript_include_tag(:dhtml_addons, :plugin => 'easy_gantt') %> 'history',
<%= javascript_include_tag(:dhtml_rewrite, :plugin => 'easy_gantt') %> 'dhtml_modif',
<%= javascript_include_tag(:background, :plugin => 'easy_gantt') %> 'dhtml_addons',
<%= javascript_include_tag(:pro_manager, :plugin => 'easy_gantt') %> 'dhtml_rewrite',
<% end %> ('dhtml_relations' if EasySetting.value(:easy_gantt_fixed_delay)),
\ No newline at end of file 'background',
'pro_manager',
'storage',
'tooltip',
'toolpanel',
'print',
'left_grid',
'sumrow',
'bars',
'problem_finder',
'collapsor',
'libs/libs',
'libs/svg.min',
) %>
<% end %>
<%= easy_gantt_include_js(
('sample' unless EasyGantt.easy_gantt_pro?),
('libs/moment' unless EasyGantt.easy_extensions?)
) %>
<script type="application/javascript">
$(ysy.initGantt);
</script>
<% end %>
<%= content_for :header_tags do %>
<script type="text/javascript">
window.ysy = window.ysy || {};
ysy.settings = ysy.settings || {};
$.extend(true, ysy.settings, <%= {
platform: EasyGantt.platform,
easyRedmine: EasyGantt.easy_extensions?,
isGantt: (params[:controller] == 'easy_gantt'),
language: I18n.locale.to_s,
project: ({ id: @project.id, name: @project.name } if @project),
dateFormat: (Setting.date_format.presence || I18n.t('date.formats.default')),
nonWorkingWeekDays: EasyGantt.non_working_week_days,
holidays: [],
milestonePush: ((EasyGantt.easy_extensions? && !EasySetting.value('milestone_effective_date_from_issue_due_date')) ? true : false),
workDayDelays: EasySetting.value(:easy_gantt_relation_delay_in_workdays),
fixedRelations: EasySetting.value(:easy_gantt_fixed_delay),
defaultZoom: EasySetting.value(:easy_gantt_default_zoom),
paths: {
rootPath: home_path
},
labels: {
buttons: {
button_delete: l(:button_delete),
button_submit: l(:button_submit),
button_yes: l(:general_text_Yes),
button_no: l(:general_text_No),
button_cancel: l(:button_cancel),
button_reload: l('easy_gantt.button.reload'),
button_save: l(:button_save)
},
sample_text: l('easy_gantt.sample.text').html_safe,
sample_global_free_text: l('easy_gantt.sample_global_free.text').html_safe,
date: {
month_full: Array(l('date.month_names')).compact,
month_short: Array(l('date.abbr_month_names')).compact,
day_full: Array(l('date.day_names')).compact,
day_short: Array(l('date.abbr_day_names')).compact
},
types: {
project: l(:field_project),
issue: l(:field_issue),
milestone: l(:field_version),
relation: l(:field_relation)
},
errors: Array(l('activerecord.errors.messages')).compact,
errors2: {
unsaved_parent: l('easy_gantt.errors.unsaved_parent')
},
problems: {
overMilestone: l('easy_gantt.errors.overmile'),
too_short: l('easy_gantt.errors.too_short'),
overdue: l('easy_gantt.errors.overdue'),
progressDateOverdue: l('easy_gantt.errors.progress_date_overdue'),
shortDelay: l('easy_gantt.errors.short_delay')
},
gateway: {
sendFailed: l('easy_gantt.gateway.send_failed'),
entitySaveFailed: l('easy_gantt.gateway.entity_save_failed'),
allSended: l(:notice_successful_update)
},
titles: {
easyGantt: l(:heading_easy_gantts_issues)
}
},
styles: {
backgrounds:{
selected: '#fff3a1',
line: 'rgba(200,200,200,0.5)',
line_month: '#aaaaff'
}
}
}.to_json.html_safe %>)
ysy.view = ysy.view || {};
ysy.view.templates = $.extend(ysy.view.templates, <%= {
TaskTooltip: %{
<h3 class="gantt-tooltip-header">{{name}}</h3>
{{#start_date}}
<div class="gantt-tooltip-start_date"><span class="gantt-tooltip-label">#{l(:field_start_date)}:</span> {{start_date}}</div>
{{/start_date}}
{{#end_date}}
<div class="gantt-tooltip-end_date"><span class="gantt-tooltip-label">#{l(:field_due_date)}:</span> {{end_date}}</div>
{{/end_date}}
{{#milestone}}
<div class="gantt-tooltip-milestone"><span class="gantt-tooltip-label">#{l(:field_version)}:</span> {{milestone.name}}</div>
{{/milestone}}
{{#columns}}
<div class="gantt-tooltip-column-{{name}}"><span class="gantt-tooltip-label">{{label}}:</span> {{value}}</div>
{{/columns}}
{{#problems}}
<div class="gantt-tooltip-problem">{{.}}</div>
{{/problems}}
},
Button: %{
<span class="button {{active}}" title="{{title}}">
<a id="{{elid}}_button_in" class="gantt_button{{icon}}" href="javascript:void(0)">{{name}}</a>
</span>
},
LinkButton: %{
<a class="{{css}}" title="{{title}}" href="javascript:void(0)">{{name}}</a>
},
SuperPanel: %{},
# reloadModal: %{
# <h3 class="title">#{l('easy_gantt.reload_modal.title')}</h3>
# <h4>#{l('easy_gantt.reload_modal.label_errors')}:</h4>
# <ul class="gantt-reload-modal-errors">
# {{#errors}}
# <li class="gantt-reload-model-error">{{.}}</li>
# {{/errors}}
# </ul>
# <p>#{l('easy_gantt.reload_modal.text_reload_appeal')}</p>
# },
preBlocker: %{
<div style="left:{{pos_x}}px" class="gantt_task_relation_stop gantt_task_relation_stop_left" title="#{l('easy_gantt.text_blocker_move_pre')}">
</div>
},
endBlocker: %{
<div style="left:{{pos_x}}px" class="gantt_task_relation_stop gantt_task_relation_stop_right" title="#{l('easy_gantt.text_blocker_move_end')}">
</div>
},
ProblemFinder: %{
#{l('easy_gantt.button.problem_finder')}:
<span class="gantt-menu-problems-count{{#count}} gantt-with-problems{{/count}}">{{count}}</span>
},
ProblemFinderList: %{
<ol>
{{#entities}}
<li>
<a href="javascript:ysy.pro.problemFinder.scrollToEntity('{{entityId}}')">
{{#isProject}}#{l(:field_project)}{{/isProject}}{{^isProject}}#{l(:field_issue)}{{/isProject}}: <strong>{{name}}</strong>
<br>
<span class="gantt-menu-problems-reason">{{text}}</span>
</a>
</li>
{{/entities}}
{{#relations}}
<li>
<a href="javascript:ysy.pro.problemFinder.scrollToEntity('{{entityId}}')">
#{l(:field_relation)}: <strong>{{sourceName}}</strong> - <strong>{{targetName}}</strong>
<br>
<span class="gantt-menu-problems-reason">{{text}}</span>
</a>
</li>
{{/relations}}
</ol>
}
}.to_json.html_safe %>)
</script>
<% end %>
<div id="supertop_panel" class="clear"></div> <%
<div id="easy_gantt_menu" class="easy-gantt-menu clear"> unless defined?(show_menu_items)
<div class="clear"> show_menu_items = true
<div class="push-left"> end
<% if Rails.env.development? %>
<span id="button_test" class="selected">
<%= easy_gantt_js_button(l(:button_test)) %>
</span>
<% end %>
<span id="button_jump_today"> zooms = { 'day' => 'calendar-day', 'week' => 'calendar-week', 'month' => 'calendar-month' }
<%= easy_gantt_js_button('&#8203;'.html_safe, :title => l(:jump_today, :scope => [:easy_gantt, :title]), :icon => 'icon-calendar') %> %>
</span> <span id="close_all_something">
<% {'day' => 'calendar-day', 'week' => 'calendar-week', 'month' => 'calendar-month'}.each do |x, i| %> <a id="button_close_all_projects" title="<%= l(:close_all, scope: [:easy_gantt, :button])+' '+l(:label_project_plural) %>" class="gantt-button-symbol icon-folder" href="javascript:void(0)"></a>
<div id="button_<%= x %>_zoom" class="menu-item"> <a id="button_close_all_milestones" title="<%= l(:close_all, scope: [:easy_gantt, :button])+' '+l(:label_version_plural) %>" class="gantt-button-symbol gantt-icon-milestone" href="javascript:void(0)"></a>
<%= easy_gantt_js_button(:"#{x}_zoom", :icon => "icon-#{i}") %> <a id="button_close_all_parent_issues" title="<%= l(:close_all, scope: [:easy_gantt, :button])+' '+l(:label_parent_issue_plural) %>" class="gantt-button-symbol gantt-icon-parent-issue" href="javascript:void(0)"></a>
</div> </span>
<% end %> <div id="supertop_panel" class="gantt-supertop-panel easy-gantt__supertop-panel clear"></div>
</div> <div id="easy_gantt_menu" class="easy-gantt__menu clear">
<div class="push-right"> <div class="push-left">
<% menu_items_for(:easy_gantt, @project) do |node| %> <% if Rails.env.development? %>
<% opts = node.html_options.dup; opts[:url] = (node.url.is_a?(Proc) ? node.url.call(@project) : node.url) %> <%= easy_gantt_js_button l(:button_test), id: 'button_test' %>
<div id="button_<%= node.name %>" class="menu-item"> <% end %>
<% if opts.delete(:trial) %>
<%= easy_gantt_help_button(node.name, node.caption, opts) %> <%= easy_gantt_js_button('&#8203;'.html_safe, id: 'button_jump_today', title: l(:jump_today, scope: [:easy_gantt, :title]), icon: 'icon-calendar') %>
<% else %>
<%= easy_gantt_js_button(node.caption, opts) %> <% zooms.each do |name, icon| %>
<%= easy_gantt_js_button(:"#{name}_zoom", id: "button_#{name}_zoom", icon: "icon-#{icon}") %>
<% end %>
</div>
<div class="push-right">
<% if show_menu_items %>
<%= easy_gantt_js_button(:problem_finder, {
url: 'javascript:void(0)',
id: 'button_problem_finder',
class: 'problem-finder',
no_button: true
}) %>
<div class="easy-gantt__menu-group easy-gantt__menu-group--tooltiped easy-gantt__menu-tools">
<a href="javascript:void(0)" class="button gantt-menu-button icon icon-settings easy-gantt__menu-tools-button"><%= l(:tool_panel, :scope => [:easy_gantt, :button]) %></a>
<ul id="easy_gantt_tool_panel" class="easy-gantt__menu-item">
<% menu_items_for(:easy_gantt_tools, @project) do |node| %>
<li>
<%
opts = node.html_options.dup
opts[:url] = (node.url.is_a?(Proc) ? node.url.call(@project) : node.url)
opts[:id] = "button_#{node.name}"
opts[:no_button] = 'true'
caption = opts[:caption].is_a?(Proc) ? opts[:caption].call(params[:gantt_type]) : node.caption
%>
<% if opts.delete(:trial) %>
<%= easy_gantt_help_button(node.name, caption, opts) %>
<% else %>
<%= easy_gantt_js_button(caption, opts) %>
<% end %>
</li>
<% end %> <% end %>
</div> </ul>
<% end %> </div>
</div> <%= easy_gantt_js_button(l(:button_save), {
url: 'javascript:void(0)',
id: 'button_save',
no_button: true,
class: 'button-positive button-1 icon icon-save'
}) %>
<% end %>
</div> </div>
<%= call_hook(:view_easy_gantts_issues_toolbars, {:project => @project}) %> <%= call_hook(:view_easy_gantts_issues_toolbars, project: @project) %>
</div> </div>
<!-- This is container for gantt-->
<div id="gantt_cont" style="width:100%;" class="clear"></div> <!-- This is container for gantt -->
<!-- End container for gantt--> <div id="gantt_cont" style="width: 100%;" class="clear"></div>
\ No newline at end of file <!-- End container for gantt -->
<%=
easy_gantt_include_js(
# test framework
'jasmine/helpers/test',
'jasmine/jasmine_lib/jasmine',
'jasmine/jasmine_lib/jasmine-html',
'jasmine/jasmine_lib/boot',
# common tests
'jasmine/main',
'jasmine/loader',
'jasmine/working_helper',
'jasmine/working_helper_add',
'jasmine/pos_date',
'jasmine/history',
'jasmine/model_relations',
'jasmine/load_reorder'
)
%>
<% extra_test_names = params[:run_jasmine_tests]
if extra_test_names != 'true'
if extra_test_names.is_a?(String)
extra_tests = prepare_test_includes([extra_test_names])
elsif extra_test_names.is_a?(Array)
extra_tests = prepare_test_includes(extra_test_names)
else
extra_tests = []
end
extra_tests.each do |test, plugin| %>
<%= easy_gantt_include_js("jasmine/#{test}", from_plugin: plugin) %>
<% end %>
<% end %>
<%= easy_gantt_include_css('jasmine', media: 'all') %>
<%
unless defined?(form_options)
form_options = {}
end
query ||= @query
form_path ||= easy_gantt_path(@project)
form_options[:additional_elements_to_serialize] ||= 'null'
%>
<div class="content-title"><%= title(easy_query_name) %></div> <div class="content-title"><%= title(easy_query_name) %></div>
<%= form_tag(issues_easy_gantt_path(@project), :method => :get, :id => 'query_form') do %> <%= form_tag(form_path, method: :get, id: 'query_form') do %>
<div id="query_form_with_buttons" class="hide-when-print"> <div id="query_form_with_buttons" class="hide-when-print">
<%= hidden_field_tag 'set_filter', '1' %> <%= hidden_field_tag 'set_filter', '1' %>
<div id="query_form_content"> <div id="query_form_content">
<fieldset id="filters" class="collapsible collapsed"> <fieldset id="filters" class="collapsible collapsed">
<legend onclick="toggleFieldset(this);"><%= l(:label_filter_plural) %></legend> <legend onclick="toggleFieldset(this);"><%= l(:label_filter_plural) %></legend>
<div style="display: none;"> <div style="display: none;">
<%= render :partial => 'queries/filters', :locals => {:query => @query} %> <%= render 'queries/filters', query: query %>
</div> </div>
</fieldset> </fieldset>
<fieldset class="collapsible collapsed"> <fieldset class="collapsible collapsed">
<legend onclick="toggleFieldset(this);"><%= l(:label_options) %></legend> <legend onclick="toggleFieldset(this);"><%= l(:label_options) %></legend>
<div style="display: none;"> <div style="display: none;">
<table> <table>
<tr> <tr>
<td><%= l(:field_column_names) %></td> <td><%= l(:field_column_names) %></td>
<td><%= render_query_columns_selection(@query) %></td> <td><%= render_query_columns_selection(query) %></td>
</tr> </tr>
</table> </table>
</div> </div>
</fieldset> </fieldset>
</div> </div>
<p class="buttons"> <p class="buttons">
<%= link_to_function l(:button_apply), '$("#query_form").submit()', :class => 'icon icon-checked' %> <%= link_to_function l(:button_apply), 'applyEasyGanttQuery()', class: 'icon icon-checked' %>
<%= link_to l(:button_clear), { :set_filter => 1, :project_id => @project }, :class => 'icon icon-reload' %> <%= link_to l(:button_clear), { set_filter: 1, project_id: @project }, class: 'icon icon-reload' %>
</p> </p>
</div> </div>
<% end %> <% end %>
<script>
var additionalElementsToSerialize;
$(document).ready(function(){
additionalElementsToSerialize = <%=raw form_options[:additional_elements_to_serialize] %>;
});
function applyEasyGanttQuery(){
if (additionalElementsToSerialize) {
var data = additionalElementsToSerialize.serializeArray()[0];
if (data) {
var newInput = $("<input />").attr("type", "hidden")
.attr("name", data.name)
.attr("value", data.value);
newInput.appendTo("#query_form");
}
}
$("#query_form").submit();
}
</script>
api.easy_gantt_data do api.easy_gantt_data do
api.start_date @start_date api.start_date @start_date
api.end_date @end_date api.end_date @end_date
api.array(:permissions) do
%w(view_easy_gantt edit_easy_gantt manage_issue_relations add_issues edit_issues manage_versions show_baselines manage_baselines).each do |perm|
api.permission(:name => perm, :value => User.current.allowed_to?(perm.to_sym, @project, :global => true))
end
end
api.array :columns do api_render_columns(api, @query)
@query.columns.each do |c| api_render_projects(api, @projects)
api.column do api_render_issues(api, @issues, with_columns: true)
api.name c.name
api.title c.caption
end
end
end
api.array :projects do
@projects.each do |project|
api.project do
api.id project.id
api.name project.name
api.start_date project.start_date
api.due_date project.due_date
api.parent_id project.parent_id
api.is_baseline project.easy_baseline_for_id? if project.respond_to?(:easy_baseline_for_id?)
end
end
end
api.array :issues do
@entities.each do |issue|
call_hook :view_easy_gantt_issue_top, :api => api, :project => @project, :issue => issue, :query => @query
api.issue do
api.id issue.id
api.name issue.subject
api.start_date issue.start_date
api.due_date issue.due_date
api.estimated_hours issue.estimated_hours
api.done_ratio issue.done_ratio
css = issue.css_classes
css << ' issue-overdue' if issue.overdue?
api.css css
api.fixed_version_id issue.fixed_version_id
api.overdue issue.overdue?
api.parent_issue_id issue.parent_id
api.project_id issue.project_id
api.assigned_to_id issue.assigned_to_id
api.array :columns do api_render_relations(api, @relations)
@query.columns.each do |c|
api.column do
api.name c.name
api.value c.value(issue).to_s
end
end
end
call_hook :view_easy_gantt_issue_inner_bottom, :api => api, :project => @project, :issue => issue, :query => @query
end
call_hook :view_easy_gantt_issue_bottom, :api => api, :project => @project, :issue => issue, :query => @query
end
end
api.array :relations do api_render_versions(api, @versions)
@relations.each do |rel|
api.relation do
api.id rel.id
api.source_id rel.issue_from_id
api.target_id rel.issue_to_id
api.type rel.relation_type
api.delay rel.delay.to_i
end
end if EasySetting.value(:easy_gantt_show_holidays) && params[:subproject_loading].blank?
api_render_holidays(api, @start_date - 1.month, @end_date + 1.month)
end end
api.array :versions do # Load only on first request
@fixed_versions.each do |version| if params[:subproject_loading].nil?
api.version do api.schemes do
api.id version.id api_render_scheme(api, IssuePriority)
api.name version.name api_render_scheme(api, IssueStatus)
api.start_date version.effective_date api_render_scheme(api, Tracker)
api.project_id version.project_id
end
end end
end end
......
<div id="easy_gantt" class="<%= defined?(EasyExtensions) ? 'easy' : 'redmine' %> gantt easy-content-page clear">
<%= render @query, :easy_query_name => l(:heading_easy_gantts_issues), :wrapper_class => '' %>
<%= render :partial => 'menu_graph' %>
<div id="easy_gantt_footer">
<div id="gantt_footer_buttons" class="easy-gantt-menu">
<p id="button_print">
<%= easy_gantt_js_button(l(:button_print), :icon => 'icon-print') %>
</p>
<p id="button_sample">
<%= easy_gantt_js_button(:load_sample_data) %>
</p>
</div>
<%= content_tag(:p, link_to(l(:text_easy_gantt_footer), l(:link_easy_gantt_plugin), :target => '_blank')) unless defined?(EasyExtensions) %>
</div>
</div>
<%= render :partial => 'includes' %>
<%= content_for :header_tags do %>
<script type="text/javascript">
window.ysy = window.ysy || {};
ysy.settings = ysy.settings || {};
$.extend(true,ysy.settings,{
easyRedmine: <%= defined?(EasyExtensions) ? true : false %>,
<% if @project %>
project: <%= "{'id': '#{@project.id}', 'name': '#{@project.name.html_safe}'}".html_safe %>,
<% end %>
nonWorkingWeekDays: <%= EasyGantt.non_working_week_days.to_json.html_safe %>,
dateFormat: '<%=j Setting.date_format %>',
withSample: <%= params[:sample] ? params[:sample] : 0 %>,
holidays: [],
paths: {
rootPath: '<%= home_path %>',
<% if @project %>
mainGantt: "<%= issues_easy_gantt_path(@project, :format => 'json', :key => User.current.api_key)%>",
<% else %>
mainGantt: "<%= projects_easy_gantt_path(:format => 'json', :key => User.current.api_key) %>",
<% end %>
projectGantt: "<%= issues_easy_gantt_path(':projectID', params.merge(:format => 'json', :no_turn_on_check => 1, :key => User.current.api_key).except!(:controller, :action)).html_safe %>",
issuePOST: "<%= issues_path(:format => 'json', :key => User.current.api_key) %>",
issuePUT: "<%= issue_path(':issueID', :format => 'json', :key => User.current.api_key) %>",
issueDELETE: "<%= issue_path(':issueID', :format => 'json', :key => User.current.api_key) %>",
relationPOST: "<%= issue_relations_path(':issueID', :format => 'json', :key => User.current.api_key) %>",
relationPUT: "<%= relation_easy_gantt_path(':projectID',':relaID', :format => 'json', :key => User.current.api_key) %>",
relationDELETE: "<%= url_for(:id => ':relaID', :controller => :issue_relations, :action => :destroy, :format => 'json', :key => User.current.api_key, :only_path => true)%>",
versionPOST: "<%= project_versions_path(':projectID', :format => 'json', :key => User.current.api_key) %>",
versionPUT: "<%= version_path(':versionID', :format => 'json', :key => User.current.api_key) %>",
sample_data: "<%= home_path %>plugin_assets/easy_gantt/javascripts/sample_{{version}}.json"
},
labels: {
buttons: {
button_delete: '<%= l(:button_delete) %>',
button_submit: '<%= l(:button_submit) %>',
button_cancel: '<%= l(:button_cancel) %>',
button_reload: '<%= l(:reload, :scope => [:easy_gantt, :button]) %>',
button_save: '<%= l(:button_save) %>'
},
types:{
project:'<%= l(:field_project) %>',
issue:'<%= l(:field_issue) %>',
milestone:'<%= l(:field_version) %>',
relation:'<%= l(:field_relation) %>'
},
sample_text: '<%= l(:text, :scope => [:easy_gantt, :sample]).html_safe %>',
sample_global_free_text:'<%= l(:text, :scope => [:easy_gantt, :sample_global_free]).html_safe %>',
date: {
month_full: <%=raw l('date.month_names').compact.to_json %>,
month_short: <%=raw l('date.abbr_month_names').compact.to_json %>,
day_full: <%=raw l('date.day_names').compact.to_json %>,
day_short: <%=raw l('date.abbr_day_names').compact.to_json %>
},
link_dir: {
link_start: "<%= l(:link_start, :scope => [:easy_gantt, :link_dir]) %>",
link_end: "<%= l(:link_end, :scope => [:easy_gantt, :link_dir]) %>"
},
errors: <%=raw l('activerecord.errors.messages').to_json %>,
errors2:{
unsaved_parent:'<%= l(:unsaved_parent, :scope => [:easy_gantt, :errors]) %>',
loop_link:'<%= l(:loop_link, :scope => [:easy_gantt, :errors]) %>',
link_target_new:'<%= l(:link_target_new, :scope => [:easy_gantt, :errors]) %>',
unsupported_link_type:'<%= l(:unsupported_link_type, :scope => [:easy_gantt, :errors]) %>',
duplicate_link:'<%= l(:duplicate_link, :scope => [:easy_gantt, :errors]) %>'
},
gateway:{
sendFailed:"<%= l(:send_failed, :scope => [:easy_gantt, :gateway]) %>",
entitySaveFailed:"<%= l(:entity_save_failed, :scope => [:easy_gantt, :gateway]) %>",
allSended:"<%= l(:notice_successful_update) %>"
}
}
});
$(document).ready(function () {
$("p.nodata").remove()
})
</script>
<script type="text/javascript">
window.ysy = window.ysy || {};
ysy.view = ysy.view || {};
ysy.view.templates = $.extend(ysy.view.templates,{
TaskTooltip:
'<div class="name"><b>{{name}}</b></div>\
<div class="start_date"><%= l(:field_start_date) %>: <strong>{{start_date}}</strong></div>\
{{#end_date}}\
<div class="end_date"><%= l(:field_due_date) %>: <strong>{{end_date}}</strong></div>\
{{/end_date}}\
{{#assignee}}\
<div class="assignee"><%= l(:field_assigned_to) %>: <strong>{{assignee}}</strong></div>\
{{/assignee}}\
{{#problems}}\
{{#overmilestone}}\
<div class="tooltip-problem"><%= l(:overmile, :scope => [:easy_gantt, :errors]) %></div>\
{{/overmilestone}}\
{{#too_short}}\
<div class="tooltip-problem"><%= l(:too_short, :scope => [:easy_gantt, :errors]) %></div>\
{{/too_short}}\
{{/problems}}\
',
LinkConfigPopup:
"<h3 class='title'><%= l(:heading_delay_popup) %></h3> <label for='link_delay_input'><%= l(:field_delay) %>:</label><input id='link_delay_input' type='number' min='0' value='{{delay}}' size='3'><hr>\
<a id='link_delete' class='icon icon-del button' href='javascript:void(0)'><%= l(:button_delete) %></a>\
<a id='link_close' class='icon icon-save button-positive' href='javascript:void(0)' style='float:right'><%= l(:button_submit) %></a>\
",
SuperPanel:'\
{{#sample}}\
<div id="sample_cont" class="flash notice">\
<h2 id="sample_label"><%= l(:header, :scope => [:easy_gantt, :sample]) %></h2>\
<p>{{{text}}}</p>\
\
<p class="" style="text-align:center">\
<a id="sample_video_button" class="icon youtube-icon" href="javascript:void(0)"><%= l(:video_label, :scope => [:easy_gantt, :sample]) %></a>\
{{^global_free}}\
<a id="sample_close_button" class="button button-important" href="javascript:void(0)" title="<%= l(:close_label, :scope => [:easy_gantt, :sample]).html_safe %>"><%= l(:close_label, :scope => [:easy_gantt, :sample]).html_safe %></a>\
{{/global_free}}\
{{#global_free}}\
<a id="sample_upgrade_button" class="button button-positive" href="<%= l(:link_easy_gantt_plugin).html_safe %>" target="_blank" title="<%= l(:label_pro_upgrade, :scope => [:easy_gantt]) %>"><%= l(:label_pro_upgrade, :scope => [:easy_gantt]) %></a>\
{{/global_free}}\
</p>\
<div class="clear"></div>\
</div>\
{{/sample}}\
',
Button:'<span class="button {{active}}" title="{{title}}"><a id="{{elid}}_button_in" class="gantt_button{{icon}}" href="javascript:void(0)" >{{name}}</a></span>',
LinkButton:'<a class="{{css}}" title="{{title}}" href="javascript:void(0)">{{name}}</a>',
easy_unimplemented:'<h3 class="title">{{modal.title}}</h3><span>{{{modal.text}}}</span>',
video_modal:'<h3 class="title"><%= l(:title, :scope => [:easy_gantt, :sample, :video]) %></h3>\
<iframe class="modal_video" width="800" height="450"\
src="//www.youtube.com/embed/<%= l(:video_id, :scope => [:easy_gantt, :sample, :video]) %>?autoplay=1">\
</iframe>\
<p><%= l(:text, :scope => [:easy_gantt, :sample, :video]) %></p>\
',
video_modal_global:'<h3 class="title"><%= l(:title, :scope => [:easy_gantt, :sample_global_free, :video]) %></h3>\
<iframe class="modal_video" width="800" height="450"\
src="//www.youtube.com/embed/<%= l(:video_id, :scope => [:easy_gantt, :sample_global_free, :video]) %>?autoplay=1">\
</iframe>\
<p><%= l(:text, :scope => [:easy_gantt, :sample_global_free, :video]) %></p>\
'
});
</script>
<% end %>
<%= call_hook(:view_easy_gantts_issues_bottom, {:project => @project}) %>
<% content_for :sidebar do %>
<%= call_hook(:view_easy_gantts_issues_sidebar, {:project => @project, :query => @query}) %>
<% end %>
<tr>
<td colspan="2"><%= l(:label_easy_gantt) %>:</td>
</tr>
<tr>
<td>
<%= easy_printable_template_link_to_add_token('%easy_gantt_current%', f, l(:label_easy_gantt)) %>
</td>
<td>
<%= l(:text_easy_gantt_print_easy_gantt_current) %>
</td>
</tr>
<div style="background: #009ee0; width: 100%; height: 5px">&nbsp;</div>
<p>&nbsp;</p>
<table border="0" cellpadding="1" cellspacing="1" style="width:500px">
<tbody>
<tr>
<td style="width: 100px"><img alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QINEAYbWX8kIAAADbFJREFUaN7tW3l0W9WZ/9339LRatmzL+yLJVuIkwg2JszlxFoJDIQEOZJi2UELpciiUgTIlbTkwk87QaaeFDrMwwDktdEiTNocZpimUQgohp4lJnMSO0ySOY8dWLMXyEnmVtdiS3nt3/ugAetZ98iraOdP7n+697737u/dbft/3XQH/zxpJ14tramqEwODgYpmQKo6QKgpYAFgIRQYFDRNKwpTQIKFcp8Sjg+f59q6urvH/U4ArSyud4KSdALZSgjoCmGbxuATQsyDkCEfIbzq7uxsA0D85wE6nU4d4/F4K7osAXb9Qm0iBbo5gnwi85PF4Bv7ogO12u54DHiQUuwGUpFEKJwD6Ci9JP7js8/X+UQBX2mxbCcgLFFjyCdqbCCF4Vmcyff/ixYuxTwSw0+nMRFx8iQL3zPF7kyBkEjI0IDRjjiu+QGT5c11Xr7alFbDTZltBKXkNBItmMD0mGzPbJxavGQkv2WSK2lx5cWt5KeU1msRJmtGBazrfpX5DV1PA1HpMrxnrqwKllpmcNggedns8r6YFcKXdfjOleH06qytrje3B2p3+kW1frpaNluxZL0YW4+bTb53Nfu9lqhntXQ2AS2nYKP7xylXPU7Ox5tMCrrDb7yYUewEIqh/WmS4O3vXkRLBm+6qFUljtQGd3wb4n+7T9XbWpgFOQV654ux8AIM8bsLPMcTvl5P8GiIb9NBkdrf9K68jND24A4bh0WCpjR2NrwX98Q+Bik1UpQL94xdv98LwAV5RX1BEivwvAwBRfo/l8z+OvWcXsouJUH9AQYJVF01eTIwzka2ncquVohkBjWQKne+z8RLUnLBunXaQkxop//FCjvrN5k/qayR63t/u7cwLscDgKOJn+HkAhazxatqyh99G9tVON0IdtpUXj/7pT676lQNBadcROgFzWvM3HgiPHhqScD3/X5mr6Tg6LxWoKaTm670TuG8+tAqBlqzS99YrX+3YqwCwx5DhZ3q8GNrKs7qjvsf11LLB3FAudPbeYT57ZmpF1X7m2Nk9HatTAstrr60z9gdssl3cv0rayTmJs8671A7t+2AogwlQwYK+zxFk6K8CVNsfXAFLPPFn78ob+rzy/GYQo1lNh5IZa680NB9eZ7KUGfh0A3ZwUVqYwC1j8bLXxur4dWS012Xz/1CnhFTet9H/u6VaAigzMVsqLL88YsMPhKAAoUw/i2YWnfY/8dMPU/r8oEc61f9occWXyG1NZ8tm2Qh1Z2XyDWf+jan3j1LHgmtvWBLZ84YSKkn660ma7c0aAiYzv4Q9h3JSX8L2+bxxwTrXEf7dE3/T6WlOlQEh5muhk9uOL9OtObDGf4InS7Qzd/teb4vn2RhXT9M8ul0ubErDD4bAR0PtYkwbu/6FfNllyEvv2LNEf/84yfTWADKS3kdocfv25+oyTU0H7vv4zFzjOz3jGFg2FdqUETGR8kyWS8YKKE+HqG1ck9v1lqdD298v0KwDop1ttVMYViVLffFG7zJr1hzaYTilU3mDOHN7+V50q4eUTAHgm4OLiYqPK6cb7vvwvtsSOIh0Z+flqkx5AKv8peSK05Z6miNv4xliFf1KeUTxLpyFC9fnC2j1L9c0Ky731i+tlnfESQzCcTru9ngnYqNHtBGBOigBKqk6J1jJFrPv2BlO7QFChGg5J1HPv6UiP41Bg5YGeWKU8i5zFjsZI8bWo3JrKyO5Zoi+tMHLBRGQjOx4JsHeQ7GICpoTezZo/uPPb1sTf2wqE9ustmhq11fRE5FbHu8HCn/ti9rmI7bkxsbD8nXHXyWHxrNocnqBw72rjhcS+wIbPrKEc158sMfTO0tJSBVPkXC6XlgKbkyYL+q5JxwpFgP/icuOYmo/ti+L80sPBpQMTsn4+uhqTQTYcC61oHlUHXZerqV6UwX98qoTjJqrWXWZRcS2nVbhSbiI4sYYV9kVcdQpD4zDx15wZ5Dr2ImnfqiPjFWGR8gthlmUKbGkIf2o0JqsF+ea/qdK1J3aM3nC/VeVtWxWACeT1rGnBVbcr4tlvLdZdUXNBz3XFxvon5AV1T2GR8p9viugBiKzx24sFhSRNVqxcAkICDL65XgmYgBF2UTGyeK1CnG8pEGQVt+N7+tLE0nQ44HeuiRUDUco8ZYtAFhfquY85NcfzotnKclEKHBwrEUd5rY9qtLrEkKrMwGZT7/njvgkpfQn9Az1xSWXIUJ/PX1GoVr6DFVQU2O12S6KVTkqxyubcQYXvNXCDHAEz7v11XzwznTTrrf54mdrY9Vn82JSwlR2Ty3JJgg4n+1/JkKFIgy7P0gRYrAUA2oJyXjoBHx+OWyk7HESRgVeomWSxsoMXjjMninSSsZF0JoUY5Qg0qrag0ThN6wlHZUCSMcrUY43SoMmGbK1KKiozVQKAMZ9Iqsk2wraiU3Z4fjpOVKkonTKPyesk6eN+jgChJDYTDSvENxiXVQP6AgMXSOcJazmABztpHxaVW8FFRuNMdsbRYKJIJ5Uo+YlxhWicDdBMAMxTXpHJDM9mnQ5Wazfma/oIIVmsse6IpFgnPzYYZ3MPOZgg0iSpQMUFRxWG6GpEKqRAP5MAFAmTM4iD5gz4rmKtagGtdVxW2A99zyXmPJHjehNEmnYkp0VjpUSMJgIhfZPUy3rZmlxNhVVPUoLmydxT1neVCGpGMfRWX0wRtQn+blbIOuDxeMYSRJq0MyRQY+w4pdiI966JUZUMQv6zLkNbOvT3AbvQmSmQxayx4RjtHBM/9jBEEkVNcIg1t0O5Xioxk2HmpjcVruCZyxOLAIRZc79g09quy+KvqUv07EXarCHxf6o2EjX9/1W/qJAqQ3dLByjLRZLjCsCWvLzTAE2y1MZLHygYzqWgXOadoM0qFin36MaMoSwtW7TpHHT4yCbThQyBOFXc0ch3LkYU/D3r/VeH2HstHVEAPnPmTBzgjiaBiEcr9e4WhRV4qCWcA4AJKkdLXB3bMjsK9CTC8OMzBsoT4P2NGU2rLJqVanPeHYi39U4mlFVlSTJ0nq5ibEw4JssnkjMekH/BenHeL38wPCV6qT4XkI6r+mQdWe65OXPgjhJt95QvzwjxIjM3dvmmzJateZrVqskywHtvc+R6xek2vNZEZIlRKaEHfT7fRBLgmCQdBJBEILT9nbVav0eRCLitMXy9ROFRW5CeIxUH1xrL2m/KbNleJPQCgIakrkSUGkjotTWmkx3bMmMVJm5lqoTIg2ciw0OxBCJCKc19+/kcNsHj9qkSgkqb7d8A8khS2jK/vPHqE2/UJvbdVChc+O16UzGmrxvRkIjLRh6FHEESefjttXjTUjPPlRu5RQCm5eT/1Rv/3WdOhbck9mUffvmDnLdfqGMoeqf7qmfpVML0EYXMsVpbIdOHp0ZFfDhQFsuvaIkXVRZ92OcOyQXDMXphe6GQDXYl76MN1XKwEsLOXzsz+JIsgRRj+loUPT4s/u6W4+EtiYfEhQNjxT9+JAegxmSqQ781Ggi0MLOWAOB2u3tA8Crra4X7nyzkQ2MjiX3/7o6uvb85ch7Agt2hUlPb3/TH3687Gto81UWV/Ou97YBsZRir7myrdZ8Kb0g8Tv5vAUYoRqXisuc+64YsKcRj79VY7baG0EiMwp0OpBTwf7dj8sStjeH6qWu1HnzmqHbIt44JSiaP/cH7TAPY7Xb7KcFTTHcx5l9d+vz9SSTl8KC4LO/NMWvjiHRIzWXNBWvfJG1c/v54eM/FyY1TBzNP/upUVsMvNqg8e6irp/tNVbeXFNCPjbVkZ1lqCUFlUqok4LfpPeePhmq22xKda5RC/4on5mwaFTs3WPk2i8DlY26l0/hgjJ7afWHCf8/pyFp/lCbdBDK3vNOcf2BPNUBYej/IS+KO4fHx4KzCtsrKynyI0lmAnceKFVV94Ht8/1rKaZigrsvkrzy9TO+7MV8wZGqwFKkrjJGQSC+dGBZD3+uI5h8bElUzoP9rkdeqbKYMgh1uj+fQnOJUp81WS0EOQ6VoJulMF327D1jE3LKU9yxzBARvLtT2lBq54XwB8SyBcBEZoj9Ktd6InH34mlg8EJVTujciRieLXvzqaYPn3CZ1IPSpLq/3+/MKzCvKK24lRDqY4tpSYHTLfedGbn20Lm3Xltoazhfu3W0k8Zgzhco/7/Z6H51HtihBvO32z4LiZ6n8rawztg/f8e3x8TW3rZ4VcU7RdL3t7oJ9T1wT/N7a1OukP3F7vQ9iIS6mfdgc5Y5tHJF/CZCU5RQq6C+Pr7uzf7T+Sy7JbLXOOlcnxqLmU2+ctRz5KSeMDqyaPslI/8Ht9e7BQl49/Ai0w/EpTqb/CaBqBl5FlA2Z7ROVNUORZRsNk7bqvHierSSxmgEqy5rR/gFdT9uAsbMpaGo7auTHBpcA1DyD94cI5R7qutq9f4ESoOzmynNlTBpDLwJk1xwlNQZCIpRSYZZ/D0gkI7+XCe72eDztC5jxnUavbbYbAPICgLQU0dTc1yd+QVzhtpxOHY1JDwD0myAoSydQEPxEI0nPdPT09M33ZfO2qC6XSzsZDt8D4EugqMPC/VPGTSn2EYF/ye12+xdq9xa0zFllszlEYCdAtgJ003RWfSqXoUAzKI4QQt9ye72N+FP8G49a2wJoem22RTKwhFKymOOQTYFsQpFBCUKUkhBHaEim9DJkvkMwCh0dHR1B/LktbPsfTqw3z5AwTsEAAAAASUVORK5CYII=" style="border:0px solid black; height:60px; margin-bottom:0px; margin-left:0px; margin-right:0px; margin-top:0px; width:60px" /></td>
<td>
<p>&nbsp;<span style="font-size:28px"><strong>EasyGantt</strong></span></p>
</td>
</tr>
</tbody>
</table>
<h1>&nbsp;</h1>
<h1>%project_name%</h1>
<table border="0" cellpadding="2" cellspacing="2" style="width:500px">
<tbody>
<tr>
<td style="width: 150px"><strong>Start date:</strong></td>
<td>%project_start_date%</td>
</tr>
<tr>
<td style="width: 150px"><strong>Due date:</strong></td>
<td>%project_due_date%</td>
</tr>
</tbody>
</table>
<p>%easy_gantt_current%</p>
api.easy_gantt_data do
api_render_issues(api, @issues)
api_render_relations(api, @relations)
api_render_versions(api, @versions)
end
api.easy_gantt_data do api.easy_gantt_data do
api.array :columns do api.start_date @start_date
@query.columns.each do |c| api.end_date @end_date
api.column do
api.name c.name api_render_columns(api, @query)
api.title c.caption api_render_projects(api, @projects, with_columns: true)
end
end if EasySetting.value(:easy_gantt_show_holidays)
api_render_holidays(api, @start_date - 1.month, @end_date + 1.month)
end end
api.array :projects do
@projects.each do |project| # Load only on first request
api.project do if params[:subproject_loading].nil?
api.id project.id api.schemes do
api.name project.name api_render_scheme(api, :EasyProjectPriority)
api.start_date project.start_date
api.due_date project.due_date
api.parent_id project.parent_id
api.is_baseline project.easy_baseline_for_id? if project.respond_to?(:easy_baseline_for_id?)
api.has_children true
end
end end
end end
end end
<% # Just for hiding %>
<script>
$("#custom_formatting").remove();
</script>
<%
default_zoom_options = [
[l('easy_gantt.button.day_zoom'), 'day'],
[l('easy_gantt.button.week_zoom'), 'week'],
[l('easy_gantt.button.month_zoom'), 'month']
]
%>
<%= title l(:title_easy_gantt_settings) %>
<div class="box tabular">
<p>
<%= form.label :show_holidays, l(:field_easy_gantt_show_holidays) %>
<%= form.check_box :show_holidays %>
<em class="small"><%= l(:text_easy_gantt_show_holidays) %></em>
</p>
<p>
<%= form.label :relation_delay_in_workdays, l(:field_easy_gantt_relation_delay_in_workdays) %>
<%= form.check_box :relation_delay_in_workdays %>
<em class="small"><%= l(:text_easy_gantt_relation_delay_in_workdays) %></em>
</p>
<p>
<%= form.label :show_project_progress, l(:field_easy_gantt_show_project_progress) %>
<%= form.check_box :show_project_progress %>
<em class="small"><%= l(:text_easy_gantt_show_project_progress) %></em>
</p>
<p>
<%= form.label :show_task_soonest_start, l(:field_easy_gantt_show_task_soonest_start) %>
<%= form.check_box :show_task_soonest_start %>
<em class="small"><%= l(:text_easy_gantt_show_task_soonest_start) %></em>
</p>
<p>
<%= form.label :default_zoom, l(:field_easy_gantt_default_zoom) %>
<%= form.select :default_zoom, options_for_select(default_zoom_options, form.object.default_zoom) %>
</p>
<%= call_hook :view_easy_gantt_settings, form: form %>
</div>
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
window.ysy = window.ysy || {};
ysy.view = ysy.view || {};
ysy.view.getGanttBackground = function () {
var colors = {
weekend: "#eeeeee",
selected: "#ffec6e",
selected_weekend: "#F4E88A",
line: "#ebebeb",
line_selected: "#ffec6e",
line_month: "#aaaaff",
assignee: "#bbbbbb"
};
return {
container: gantt.$task_bg,
renderer: true,
filter: gantt._create_filter(['_filter_task', '_is_chart_visible', '_is_std_background']),
lastCounts: {},
_render_bg_canvas: function (canvas, items, limits) {
var rowHeight = gantt.config.row_height;
var cfg = gantt._tasks;
var widths = cfg.width;
var fullHeight = rowHeight * (limits.toY - limits.fromY);
var fullWidth = 0;
var needClear = true;
var width;
var partWidth;
var context = canvas.getContext('2d');
for (var i = limits.fromX; i < limits.toX; i++) {
fullWidth += widths[i];
}
if (canvas.height !== fullHeight) {
canvas.style.height = fullHeight + "px";
canvas.height = fullHeight;
needClear = false;
}
if (canvas.width !== fullWidth) {
canvas.style.width = fullWidth + "px";
canvas.width = fullWidth;
needClear = false;
}
// -- CLEARING --
if (needClear) {
context.clearRect(0, 0, fullWidth, fullHeight);
}
// -- WEEKENDS BACKGROUND --
if (gantt.config.scale_unit === "day") {
partWidth = 0;
context.fillStyle = colors.weekend;
for (i = limits.fromX; i < limits.toX; i++) {
width = widths[i];
if (cfg.weekends[i]) {
context.fillRect(partWidth, 0, width, fullHeight);
}
partWidth += width;
}
}
// -- HORIZONTAL LINES --
context.strokeStyle = colors.line;
context.beginPath();
for (i = 1; i <= limits.toY - limits.fromY; i++) {
context.moveTo(0, i * rowHeight - 0.5);
context.lineTo(fullWidth, i * rowHeight - 0.5);
}
// -- VERTICAL LINES --
partWidth = -0.5;
for (i = limits.fromX; i < limits.toX; i++) {
width = widths[i];
if (width <= 0) continue; //do not render skipped columns
partWidth += width;
context.moveTo(partWidth, 0);
context.lineTo(partWidth, fullHeight);
}
context.stroke();
// -- SELECTED --
for (i = limits.fromY; i < limits.toY; i++) {
if (gantt.getState().selected_task == items[i].id) {
break;
}
}
// -- SELECTED ROW --
if (i < limits.toY) {
context.fillStyle = colors.selected;
context.fillRect(0, (i - limits.fromY) * rowHeight, fullWidth, rowHeight);
}
// -- SELECTED WEEKENDS --
if (gantt.config.scale_unit === "day") {
partWidth = 0;
context.fillStyle = colors.selected_weekend;
for (var j = limits.fromX; j < limits.toX; j++) {
width = widths[j];
if (cfg.weekends[j]) {
context.fillRect(partWidth, i * rowHeight, width, rowHeight);
}
partWidth += width;
}
}
if (ysy.settings.resource.open) {
// -- ASSIGNEE BACKGROUND --
context.globalAlpha = 0.5;
context.fillStyle = colors.assignee;
for (i = limits.fromY; i < limits.toY; i++) {
if (items[i].type === "assignee") {
context.fillRect(0, (i - limits.fromY) * rowHeight, fullWidth, rowHeight);
}
}
context.globalAlpha = 1;
}
// -- BLUE LINE --
if (gantt.config.scale_unit === "day") {
partWidth = 0.5;
context.strokeStyle = colors.line_month;
context.beginPath();
for (i = limits.fromX; i < limits.toX; i++) {
width = cfg.width[i];
var first = moment(cfg.trace_x[i]).date() === 1;
if (first) {
context.moveTo(partWidth, 0);
context.lineTo(partWidth, fullHeight);
}
partWidth += width;
}
context.stroke();
}
},
render_bg_line: function (canvas, index, item) {
},
render_item: function (item, container) {
ysy.log.debug("render_item BG", "canvas_bg");
},
render_items: function (items, container) {
var y, x;
ysy.log.debug("render_items FULL BG", "canvas_bg");
container = container || this.node;
if (!this.rendered.length)this.rendered = [];
var cfg = gantt._tasks;
var rowHeight = gantt.config.row_height;
var colWidth = cfg.col_width;
var countX = cfg.count;
var countY = items.length;
var fullHeight = rowHeight * countY;
var partCountY = Math.ceil(8170 / rowHeight),
partCountX = Math.ceil(8170 / colWidth);
var nCanvasX = Math.ceil(countX / partCountX);
var nCanvasY = Math.ceil(countY / partCountY);
if (this.lastCounts.nCanvasY > nCanvasY) {
for (y = 0; y < this.lastCounts.nCanvasY - nCanvasY; y++) {
var removed = this.rendered.pop();
for (x = 0; x < removed.length; x++) {
container.removeChild(removed[x]);
}
}
}
if (this.lastCounts.nCanvasX > nCanvasX) {
var diff = this.lastCounts.nCanvasX - nCanvasX;
for (y = 0; y < this.rendered.length; y++) {
for (x = 0; x < diff; x++) {
removed = this.rendered[y].pop();
container.removeChild(removed);
}
}
}
this.lastCounts = {nCanvasX: nCanvasX, nCanvasY: nCanvasY};
//var fullWidth = cfg.full_width;
var lastElement;
for (y = 0; y < nCanvasY; y++) {
if (!this.rendered[y]) this.rendered.push([]);
for (x = 0; x < nCanvasX; x++) {
var canvas = this.rendered[y][x];
if (canvas === undefined) {
canvas = document.createElement("canvas");
if (lastElement && lastElement.nextSibling) {
container.insertBefore(canvas, lastElement.nextSibling);
} else {
container.appendChild(canvas);
}
canvas.style.float = "left";
this.rendered[y].push(canvas);
}
this._render_bg_canvas(canvas, items, {
fromX: x * partCountX,
toX: Math.min((x + 1) * partCountX, countX),
fromY: y * partCountY,
toY: Math.min((y + 1) * partCountY, countY)
});
lastElement = canvas;
}
}
container.style.height = fullHeight + "px";
}
};
};
\ No newline at end of file
/* bars.js */
/* global ysy */
window.ysy = window.ysy || {};
ysy.view = ysy.view || {};
ysy.view.bars = ysy.view.bars || {};
$.extend(ysy.view.bars, {
_dateCache: {}, // for faster parsing YYYY-MM-DD to moment
_rendererStack: {},
registerRenderer: function (entity, renderer) {
if (this._rendererStack[entity] === undefined) {
this._rendererStack[entity] = [];
}
var renderers = this._rendererStack[entity];
var found = false;
for (var i = 0; i < renderers.length; i++) {
if (renderers[i] === renderer) found = true;
}
if (found) return;
renderers.push(renderer);
this.reconstructRenderer(entity);
},
removeRenderer: function (entity, renderer) {
var renderers = this._rendererStack[entity];
if (!renderers) return;
for (var i = 0; i < renderers.length; i++) {
if (renderers[i] === renderer) {
renderers.splice(i, 1);
this.reconstructRenderer(entity);
return;
}
}
},
reconstructRenderer: function (entity) {
var renderers = this._rendererStack[entity];
if (renderers.length === 0) {
gantt.config.type_renderers[entity] = gantt._task_default_render;
return;
}
gantt.config.type_renderers[entity] = function (task) {
var i = renderers.length - 1;
var nextRenderer = function () {
if (i < 0) return gantt._task_default_render;
return renderers[i--];
};
return nextRenderer().call(this, task, nextRenderer);
}
},
getFromDateCache: function (allodate) {
var alloMoment = this._dateCache[allodate];
if (alloMoment === undefined) {
alloMoment = moment(allodate);
this._dateCache[allodate] = alloMoment;
}
return alloMoment;
},
insertCanvas:function (canvas,rootDiv) {
var taskLeftElements = rootDiv.getElementsByClassName("task_left");
if (taskLeftElements.length === 0) {
rootDiv.appendChild(canvas);
} else {
rootDiv.insertBefore(canvas, taskLeftElements[0]);
}
},
canvasListBuilder: function () {
return {
__proto__: this.canvasListPrototype
};
//return canvasList;
},
canvasListPrototype: {
limit: 8170,
build: function (task, gantt, start_date, end_date) {
// initialization
this.canvases = [];
this.contexts = [];
this.starts = [];
this.gantt = gantt;
this.isAssignee = task.type === "assignee";
this.el = null;
this.height = this.isAssignee ? gantt.config.row_height : gantt._tasks.bar_height;
this.columnWidth = gantt._tasks.col_width;
var startX = gantt.posFromDate(start_date || task.start_date);
var endX = gantt.posFromDate(end_date || task.end_date);
//var fullWidth = gantt._get_task_width(task);
var fullWidth = endX - startX;
this.startX = startX;
this.fullWidth = fullWidth;
var config = gantt._tasks;
if (fullWidth < this.limit) {
this.el = this.createCanvas(fullWidth);
this.starts.push(startX);
this.staticPack = {
ctx: this.contexts[0],
canvas: this.el,
start: startX,
end: startX + fullWidth
};
} else {
this.el = document.createElement("div");
var lefts = config.left;
for (var i = 0; i < lefts.length; i++) {
if (lefts[i] >= startX) break;
}
var partX = startX;
for (; i < lefts.length; i++) {
if (lefts[i] > fullWidth + startX) break;
if (lefts[i] >= partX + this.limit) {
var canvas = this.createCanvas(lefts[i - 1] - partX);
this.el.appendChild(canvas);
this.starts.push(partX);
partX = lefts[i - 1];
}
}
canvas = this.createCanvas(startX + fullWidth - partX);
this.el.appendChild(canvas);
this.starts.push(partX);
}
this.el.className += " gantt-task-bar-line";
if (this.isAssignee) {
var y = this.gantt.getTaskTop(task.id);
this.el.style.left = startX + "px";
this.el.style.top = y + "px";
}
},
createCanvas: function (width) {
var el = document.createElement("canvas");
//var height = this.gantt._tasks.bar_height;
width = Math.round(width);
el.style.width = width + "px";
el.width = width;
el.height = this.height - 1;
el.className = "gantt-task-bar-canvas";
this.canvases.push(el);
var ctx = el.getContext("2d");
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
this.contexts.push(ctx);
return el;
},
getElement: function () {
return this.el;
},
inRange: function (date) {
var pos = gantt.posFromDateCached(date);
return pos + this.columnWidth >= this.startX && pos < this.fullWidth + this.startX;
},
fillTextAt: function (date, text, styles) {
var x = gantt.posFromDateCached(date);
var pack = this.getPack(x);
var posPack = this.getPosPack(x, pack);
var ctx = pack.ctx;
if (styles.backgroundColor) {
this.fillRectAtPosPack(posPack, pack, styles.backgroundColor);
}
ctx.font = styles.fontStyle;
ctx.fillStyle = styles.textColor;
text = this.fitTextInWidth(text, posPack.width, ctx);
ctx.fillText(text, posPack.middle, this.height / 2 + 1);
},
fillFormattedTextAt: function (date, formatter, value, styles) {
var x = gantt.posFromDateCached(date);
var pack = this.getPack(x);
var posPack = this.getPosPack(x, pack);
var ctx = pack.ctx;
if (styles.backgroundColor) {
this.fillRectAtPosPack(posPack, pack, styles.backgroundColor, styles.shrink);
}
ctx.font = styles.fontStyle;
ctx.fillStyle = styles.textColor;
var text = formatter(value, posPack.width);
text = this.fitTextInWidth(text, posPack.width, ctx);
ctx.fillText(text, posPack.middle, this.height / 2 + 1);
},
fillTwoTextAt: function (date, textUpper, textBottom, styles) {
var x = gantt.posFromDateCached(date);
var pack = this.getPack(x);
var posPack = this.getPosPack(x, pack);
var ctx = pack.ctx;
if (styles.backgroundColor) {
this.fillRectAtPosPack(posPack, pack, styles.backgroundColor, styles.shrink);
}
var bottomLine = this.height / 2 + 1;
ctx.fillStyle = styles.textColor;
if (textUpper) {
ctx.font = styles.fontStyle.replace("12px", "9px");
bottomLine = bottomLine * 13.0 / 10;
textUpper = this.fitTextInWidth(textUpper, posPack.width, ctx);
ctx.fillText(textUpper, posPack.middle, this.height * 3 / 12);
}
if (textBottom) {
ctx.font = styles.fontStyle;
// ctx.fillStyle = styles.textColor;
textBottom = this.fitTextInWidth(textBottom, posPack.width, ctx);
ctx.fillText(textBottom, posPack.middle, bottomLine);
}
},
fillRectAtPosPack: function (posPack, pack, fillColor, shrink) {
pack.ctx.fillStyle = fillColor;
if (shrink) {
pack.ctx.fillRect(posPack.start + 1, 1, posPack.width - 3, this.height - 3);
} else {
pack.ctx.fillRect(posPack.start, 0, posPack.width, this.height);
}
},
roundTo1: function (number) {
if (number === undefined) return "";
var modulated = number % 1;
if (modulated < 0) {
modulated += 1;
}
if (modulated < this.MARGIN || modulated > (1 - this.MARGIN)) {
return number.toFixed();
}
return number.toFixed(1);
},
fitTextInWidth: function (text, width, ctx) {
width -= 2;
if (text.length * 7.2 < width) return text;
ctx.font = ctx.font.replace("12px", "9px");
if (text.length * 5.3 < width) return text;
var splitPos = Math.floor(width / 5.3 - 1);
return text.substring(0, splitPos) + "#";
},
getPosPack: function (x, pack) {
var start, end, width = this.columnWidth;
if (!pack) return null;
start = x - pack.start;
if (x < pack.start || x > pack.end - width) {
end = start + width + Math.min(0, pack.end - width - x);
start = Math.max(start, 0);
return {
start: start,
end: end,
middle: Math.floor((start + end) / 2),
width: end - start
};
} else {
return {
start: start,
end: start + width,
middle: Math.floor(start + width / 2),
width: width
};
}
},
getPack: function (pos) {
if (this.staticPack) return this.staticPack;
//var pos = gantt.posFromDateCached(date);
// var min = this.startX;
var mid = pos + this.columnWidth / 2;
// if (pos + width < min) return null;
// if (pos >= this.fullWidth + min) return null;
for (var i = 1; i < this.starts.length; i++) {
if (this.starts[i] > mid) break;
}
if (i >= this.starts.length) {
i = this.starts.length;
var end = this.startX + this.fullWidth;
} else {
end = this.starts[i];
}
return {
ctx: this.contexts[i - 1],
canvas: this.canvases[i - 1],
start: this.starts[i - 1],
end: end
};
}
}
});
/* collapsor.js */
/* global ysy */
window.ysy = window.ysy || {};
ysy.pro = ysy.pro || {};
ysy.pro.collapsor = ysy.pro.collapsor || {};
$.extend(ysy.pro.collapsor, {
templateHtml: null,
patch: function () {
var $sourceDiv = $("#close_all_something");
this.templateHtml = '<div id="gantt_grid_collapsors" class="gantt-grid-header-collapse-buttons">' + $sourceDiv.html() + '</div>';
$sourceDiv.remove();
},
extendees: [
{
id: "close_all_parent_issues",
bind: function () {
this.model = ysy.data.limits;
},
func: function () {
var openings = this.model.openings;
var issues = ysy.data.issues.getArray();
this.model.parentsIssuesClosed = !this.model.parentsIssuesClosed;
if (this.model.parentsIssuesClosed) {
for (var i = 0; i < issues.length; i++) {
openings[issues[i].getID()] = false;
}
} else {
for (i = 0; i < issues.length; i++) {
delete openings[issues[i].getID()];
}
}
this.model._fireChanges(this, "close_all_parent_issues");
return false;
},
isOn: function () {
return this.model.parentsIssuesClosed;
}
},
{
id: "close_all_milestones",
bind: function () {
this.model = ysy.data.limits;
},
func: function () {
var openings = this.model.openings;
var milestones = ysy.data.milestones.getArray();
this.model.milestonesClosed = !this.model.milestonesClosed;
if (this.model.milestonesClosed) {
for (var i = 0; i < milestones.length; i++) {
openings[milestones[i].getID()] = false;
}
} else {
for (i = 0; i < milestones.length; i++) {
delete openings[milestones[i].getID()];
}
}
this.model._fireChanges(this, "close_all_milestones");
return false;
},
isOn: function () {
return this.model.milestonesClosed;
}
},
{
id: "close_all_projects",
bind: function () {
this.model = ysy.data.limits;
},
func: function () {
var openings = this.model.openings;
var projects = ysy.data.projects.getArray();
this.model.projectsClosed = !this.model.projectsClosed;
if (this.model.projectsClosed) {
for (var i = 0; i < projects.length; i++) {
if (projects[i].id === ysy.settings.projectID) continue;
delete openings[projects[i].getID()];
// gantt.close(projects[i].getID());
}
} else {
for (i = 0; i < projects.length; i++) {
if (!projects[i].needLoad) {
openings[projects[i].getID()] = true;
}
//gantt.open(projects[i].getID());
}
}
this.model._fireChanges(this, "close_all_projects");
return false;
},
isOn: function () {
return this.model.projectsClosed;
}
}
]
});
//#############################################################################################
ysy.view.Collapsors = function () {
ysy.view.Widget.call(this);
};
ysy.main.extender(ysy.view.Widget, ysy.view.Collapsors, {
name: "CollapsorsWidget",
_postInit:function(){
this.model = ysy.settings.resource;
this.model.unregister(this);
this.model.register(this.requestRepaint,this);
},
_updateChildren: function () {
for (var i = 0; i < this.children.length; i++) {
this.children.destroy();
}
this.children = [];
var collapsorClass = ysy.pro.collapsor;
for (i = 0; i < collapsorClass.extendees.length; i++) {
var extendee = collapsorClass.extendees[i];
var button = new ysy.view.Button();
$.extend(button, extendee);
button.init();
this.children.push(button);
}
},
repaint: function (force) {
var $target = $("#gantt_grid_collapsors");
if(this.repaintRequested){
if(this.model.open){
$target.hide();
return;
}else{
$target.show();
}
$target.off("click").on("click",function(){
return false;
});
}
for (var i = 0; i < this.children.length; i++) {
var child = this.children[i];
child.$target = this.getChildTarget(child);
if(!child.$target.length) continue;
child.repaint(force || this.repaintRequested);
}
this.repaintRequested=false;
},
getChildTarget: function (child) {
return this.$target.find("#" + child.elementPrefix + child.id);
}
});
/*
* = require_directory ./libs
* = require easy_gantt/utils
* = require easy_gantt/data
* = require easy_gantt/widget
* = require_directory .
* = stub easy_gantt/sample
* = stub easy_gantt/libs/moment
*/
\ No newline at end of file
/* history.js */
/* global ysy */
window.ysy = window.ysy || {}; window.ysy = window.ysy || {};
ysy.history = ysy.history || {}; ysy.history = ysy.history || {};
$.extend(ysy.history, { $.extend(ysy.history, {
......
describeExtra("global_gantt", function () {
describe("FREE Global gantt", function () {
it("should fail anywhere but global gantt", function () {
expect(ysy.settings.global).toBe(true);
expect(ysy.settings.isGantt).toBe(true);
});
});
});
\ No newline at end of file
window.ysy = window.ysy || {};
ysy.pro = ysy.pro || {};
ysy.pro.test = {
jasmineStarted: false,
startCounter: 0,
beatCallbacks: [],
extraTestNames: [],
extraTestFunctions: [],
patch: function () {
ysy.view.onRepaint.push($.proxy(this.beat, this));
this.loadExtraTests();
},
beat: function () {
if (!this.jasmineStarted) {
if (ysy.data.loader.loaded) {
if (this.startCounter++ > 3) {
this.jasmineStarted = true;
this.jasmineStart();
}
}
}
if (this.beatCallbacks.length) {
var newCallbacks = [];
for (var i = 0; i < this.beatCallbacks.length; i++) {
var callPack = this.beatCallbacks[i];
if (callPack.rounds === 1) {
callPack.callback();
} else {
callPack.rounds--;
newCallbacks.push(callPack);
}
}
this.beatCallbacks = newCallbacks;
}
},
fewBeatsAfter: function (callback, count) {
if (count === undefined) count = 2;
this.beatCallbacks.push({callback: callback, rounds: count});
},
loadExtraTests: function () {
var self = this;
describe("(EXTRA)", function () {
for (var i = 0; i < self.extraTestFunctions.length; i++) {
self.extraTestFunctions[i]();
}
});
},
parseResult: function () {
var specs = window.jsApiReporter.specs();
var shortReport = "";
var report = "";
var allPassed = true;
var result = "";
for (var i = 0; i < specs.length; i++) {
var spec = specs[i];
if (spec.status === "passed") {
shortReport += ".";
} else {
allPassed = false;
shortReport += "X";
report += "__TEST " + spec.fullName + "______\n";
for (var j = 0; j < spec.failedExpectations.length; j++) {
var fail = spec.failedExpectations[j];
var split = fail.stack.split("\n");
result+=window.location+"\n";
report += " " + fail.message + "\n";
for (var k = 1; k < split.length; k++) {
if (split[k].indexOf("/jasmine_lib/") > -1) break;
report += split[k] + "\n";
}
}
}
}
if (allPassed) {
return "success";
}
result += " RESULTS: " + shortReport + "\n" + report;
$("#content").text(result.replace("\n","<br>"));
return result;
}
};
window.describeExtra = function (file, func) {
if (file.indexOf("/") === -1) {
file = "easy_gantt/" + file
}
ysy.pro.test.extraTestNames.push(file);
ysy.pro.test.extraTestFunctions.push(function () {
describe(file, func);
});
};
describe("History",function(){
var issue;
var history;
beforeAll(function(){
history = ysy.history;
issue = new ysy.data.Issue();
issue.init({
id:12,
start_date:"2016-05-25",
due_date:"2016-05-29",
estimated: 25,
subject:"task H",
columns:[]
});
});
it("should revert estimated change",function(){
issue.set("estimated",60);
expect(issue.estimated).toBe(60);
expect(issue._changed).toBe(true);
history.revert();
expect(issue.estimated).toBe(25);
expect(issue._changed).toBe(false);
});
it("should revert start_date change",function(){
issue.set({start_date:moment("2016-05-15")});
expect(issue.start_date.isSame(moment("2016-05-15"))).toBe(true);
history.revert();
expect(issue.start_date.isSame(moment("2016-05-25"))).toBe(true);
});
});
\ No newline at end of file
/**
Starting with version 2.0, this file "boots" Jasmine, performing all of the necessary initialization before executing the loaded environment and all of a project's specs. This file should be loaded after `jasmine.js` and `jasmine_html.js`, but before any project source files or spec files are loaded. Thus this file can also be used to customize Jasmine for a project.
If a project is using Jasmine via the standalone distribution, this file can be customized directly. If a project is using Jasmine via the [Ruby gem][jasmine-gem], this file can be copied into the support directory via `jasmine copy_boot_js`. Other environments (e.g., Python) will have different mechanisms.
The location of `boot.js` can be specified and/or overridden in `jasmine.yml`.
[jasmine-gem]: http://github.com/pivotal/jasmine-gem
*/
(function() {
/**
* ## Require &amp; Instantiate
*
* Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference.
*/
window.jasmine = jasmineRequire.core(jasmineRequire);
/**
* Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference.
*/
jasmineRequire.html(jasmine);
/**
* Create the Jasmine environment. This is used to run all specs in a project.
*/
var env = jasmine.getEnv();
/**
* ## The Global Interface
*
* Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged.
*/
var jasmineInterface = jasmineRequire.interface(jasmine, env);
/**
* Add all of the Jasmine global/public interface to the global scope, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`.
*/
extend(window, jasmineInterface);
/**
* ## Runner Parameters
*
* More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface.
*/
var queryString = new jasmine.QueryString({
getWindowLocation: function() { return window.location; }
});
var catchingExceptions = queryString.getParam("catch");
env.catchExceptions(typeof catchingExceptions === "undefined" ? true : catchingExceptions);
var throwingExpectationFailures = queryString.getParam("throwFailures");
env.throwOnExpectationFailure(throwingExpectationFailures);
var random = queryString.getParam("random");
env.randomizeTests(random);
var seed = queryString.getParam("seed");
if (seed) {
env.seed(seed);
}
/**
* ## Reporters
* The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any).
*/
var htmlReporter = new jasmine.HtmlReporter({
env: env,
onRaiseExceptionsClick: function() { queryString.navigateWithNewParam("catch", !env.catchingExceptions()); },
onThrowExpectationsClick: function() { queryString.navigateWithNewParam("throwFailures", !env.throwingExpectationFailures()); },
onRandomClick: function() { queryString.navigateWithNewParam("random", !env.randomTests()); },
addToExistingQueryString: function(key, value) { return queryString.fullStringWithNewParam(key, value); },
getContainer: function() { return document.body; },
createElement: function() { return document.createElement.apply(document, arguments); },
createTextNode: function() { return document.createTextNode.apply(document, arguments); },
timer: new jasmine.Timer()
});
/**
* The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript.
*/
env.addReporter(jasmineInterface.jsApiReporter);
env.addReporter(htmlReporter);
/**
* Filter which specs will be run by matching the start of the full name against the `spec` query param.
*/
var specFilter = new jasmine.HtmlSpecFilter({
filterString: function() { return queryString.getParam("spec"); }
});
env.specFilter = function(spec) {
return specFilter.matches(spec.getFullName());
};
/**
* Setting up timing functions to be able to be overridden. Certain browsers (Safari, IE 8, phantomjs) require this hack.
*/
window.setTimeout = window.setTimeout;
window.setInterval = window.setInterval;
window.clearTimeout = window.clearTimeout;
window.clearInterval = window.clearInterval;
/**
* ## Execution
*
* Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded.
*/
var currentWindowOnload = window.onload;
ysy.pro.test.jasmineStart = function() {
// window.onload = function() { // HOSEKP
// if (currentWindowOnload) {
// currentWindowOnload();
// } // HOSEKP
htmlReporter.initialize();
env.execute();
};
/**
* Helper function for readability above.
*/
function extend(destination, source) {
for (var property in source) destination[property] = source[property];
return destination;
}
}());
This source diff could not be displayed because it is too large. You can view the blob instead.
describe("Loader", function () {
describe("reorder", function () {
var oldIds = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
var newIds = [1, 2, 4, 3, 5, 11, 6, 7, 8, 9, 10, 12, 19, 14, 16, 21, 15, 17, 22, 20];
var targetIds = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 17, 19, 20, 21, 22];
var codeTable = null;
/**
*
* @param {Array.<{id:int}>} array
* @return {Array.<int>}
*/
function getIdArray(array) {
return array.map(function (item) {
return item.id;
});
// var result = [];
// for (var i = 0; i < array.length; i++) {
// result.push(array[i].id);
// }
// return result;
}
/**
*
* @param {Array.<int>} idArray
* @return {Array.<int>}
*/
function encodeIdArray(idArray) {
if (codeTable === null) {
codeTable = [];
while (codeTable.length < 25) {
var code = getRandomInt(1000, 5000);
if (codeTable.indexOf(code) > -1) continue;
codeTable.push(code);
}
}
return idArray.map(function (id) {
return codeTable[id];
});
}
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
function toDataArray(idArray) {
var array = [];
for (var i = 0; i < idArray.length; i++) {
var entityId = idArray[i];
array.push({id: entityId, name: "entity #" + entityId});
}
return array;
}
it("should be defined", function () {
expect(typeof(ysy.data.loader.reorderArray)).toBe("function");
});
it("should reorder by ID array", function () {
var reordered = ysy.data.loader.reorderArray(toDataArray(newIds), oldIds);
var reorderedIdArray = getIdArray(reordered);
expect(reorderedIdArray).toEqual(targetIds);
});
it("should reorder encoded ID array", function () {
var reordered = ysy.data.loader.reorderArray(toDataArray(encodeIdArray(newIds)), encodeIdArray(oldIds));
var reorderedIdArray = getIdArray(reordered);
expect(reorderedIdArray).toEqual(encodeIdArray(targetIds));
});
});
});
describe("Loader", function () {
describe("init", function () {
var sett = ysy.settings;
it("should prepare proper rootPath", function () {
expect(sett.paths.rootPath.substring(-1)).toBe("/");
});
it("should prepare global/projectID variables", function () {
if (sett.global) {
expect(sett.global).toBe(true);
expect(sett.projectID).toBeUndefined();
} else {
expect(sett.global).toBeUndefined();
expect(sett.projectID).toEqual(jasmine.any(Number));
}
});
it("should init many settings", function () {
expect(sett.zoom._name).toEqual("Zoom");
expect(sett.controls._name).toEqual("Task controls");
expect(sett.baseline._name).toEqual("Baselines");
expect(sett.critical._name).toEqual("Critical path");
expect(sett.addTask._name).toEqual("Add Task");
expect(sett.resource._name).toEqual("Resource Management");
expect(sett.scheme._name).toEqual("Schema switch");
});
});
describe("Project load", function () {
var projectsBackup;
beforeEach(function () {
projectsBackup = ysy.data.projects;
ysy.data.projects = new ysy.data.Array();
});
afterEach(function () {
ysy.data.projects = projectsBackup;
});
var projectsJson = [{
id: 25,
name: "superproject",
start_date: "2016-06-25",
end_date: "2016-07-16"
}, {
id: 26,
name: "subproject",
start_date: "2016-06-25",
end_date: "2016-07-16"
}, {
id: 31,
name: "project",
start_date: "2016-01-01",
end_date: "2016-02-16"
}];
it("should load project array", function () {
expect(ysy.data.projects.getArray().length).toBe(0);
ysy.data.loader._loadProjects(projectsJson);
expect(ysy.data.projects.getArray().length).toBe(3);
});
});
describe("Issue load", function () {
var issuesBackup;
beforeEach(function () {
issuesBackup = ysy.data.issues;
ysy.data.issues = new ysy.data.Array();
});
afterEach(function () {
ysy.data.issues = issuesBackup;
});
var issuesJson = [{
id: 25,
name: "superissue",
start_date: "2016-06-25",
end_date: "2016-07-16",
columns: []
}, {
id: 26,
name: "subissue",
start_date: "2016-06-25",
end_date: "2016-07-16",
columns: []
}];
it("should load issue array", function () {
expect(ysy.data.issues.getArray().length).toBe(0);
ysy.data.loader._loadIssues(issuesJson, "root");
expect(ysy.data.issues.getArray().length).toBe(2);
});
// it("should throw error when no columns property", function () {
// expect(function () {
// ysy.data.loader._loadIssues([{id: 25}])
// }).toThrowError(TypeError)
// });
});
});
\ No newline at end of file
describe("Test framework", function () {
it("should load", function () {
expect(true).toBe(true);
// expect(false).toBe(true);
});
it("should start after gantt is loaded", function () {
expect($(".gantt_data_area").length).toBe(1);
});
it("should handle long tests", function (done) {
setTimeout(function () {
expect(true).toBe(true);
done();
}, 100);
});
var getQueryString = function () {
var query_string = {};
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split("=");
// If first entry with this name
if (typeof query_string[pair[0]] === "undefined") {
query_string[pair[0]] = decodeURIComponent(pair[1]);
// If second entry with this name
} else if (typeof query_string[pair[0]] === "string") {
query_string[pair[0]] = [query_string[pair[0]], decodeURIComponent(pair[1])];
// If third or later entry with this name
} else {
query_string[pair[0]].push(decodeURIComponent(pair[1]));
}
}
return query_string;
};
var prefixTestFile = function (file) {
if (file.indexOf("/") > -1) return file;
return "easy_gantt/" + file;
};
it("should load extra tests if any", function () {
var params = getQueryString();
var requestedTests = params["run_jasmine_tests"] || params["run_jasmine_tests[]"] || params["run_jasmine_tests%5B%5D"];
if (requestedTests === "true") {
requestedTests = [];
} else if (typeof(requestedTests) === "string") {
requestedTests = [prefixTestFile(requestedTests)];
} else if (typeof(requestedTests) === "object" && requestedTests.length !== undefined) {
requestedTests = requestedTests.map(function (requestedTest) {
return prefixTestFile(requestedTest);
})
} else {
throw "Wrong type of run_jasmine_tests - \""+requestedTests+"\" is not true|string|Array<String>";
}
var extraTests = ysy.pro.test.extraTestNames;
if (requestedTests.length > extraTests.length) {
for (var i = 0; i < requestedTests.length; i++) {
expect(extraTests).toContain(requestedTests[i], "extraTests missing " + requestedTests[i]);
}
return;
}
expect(requestedTests.length).toBe(extraTests.length, "requested tests !== loaded tests");
for (i = 0; i < extraTests.length; i++) {
expect(requestedTests).toContain(extraTests[i]);
}
});
});
\ No newline at end of file
describe("Model relations",function(){
});
\ No newline at end of file
describe("PosFromDate", function () {
beforeAll(function () {
this._max_date = gantt._max_date;
this._min_date = gantt._min_date;
this.fullWidth = gantt._tasks.full_width;
this.scale = gantt._tasks;
});
it("should return 0 if min_date", function () {
expect(gantt.posFromDate(this._min_date)).toBe(0);
});
it("should return fullWidth if max_date", function () {
expect(gantt.posFromDate(this._max_date)).toBe(this.fullWidth);
});
it("should work for all columns", function () {
for (var i = 0; i < this.scale.count; i++) {
expect(gantt.posFromDate(this.scale.trace_x[i])).toEqual(this.scale.left[i]);
}
});
it("should work for quarter-column", function () {
for (var i = 0; i < this.scale.count - 1; i++) {
// expect(gantt.posFromDate(moment(this.scale.trace_x[i]).add(6, "hours")))
expect(gantt.posFromDate(moment(
+this.scale.trace_x[i] + 0.25 * (this.scale.trace_x[i + 1] - this.scale.trace_x[i])
)))
.toEqual(this.scale.left[i] + 0.25 * (this.scale.left[i + 1] - this.scale.left[i]));
}
});
});
describe("DateFromPos", function () {
beforeAll(function () {
this._max_date = gantt._max_date;
this._min_date = gantt._min_date;
this.fullWidth = gantt._tasks.full_width;
this.scale = gantt._tasks;
});
it("should return min_date if 0", function () {
expect(gantt.dateFromPos(0)).toEqual(this._min_date);
});
it("should return max_date if fullWidth", function () {
expect(gantt.dateFromPos(this.fullWidth)).toEqual(this._max_date);
});
it("should work for all columns", function () {
for (var i = 0; i < this.scale.count; i++) {
expect(gantt.dateFromPos(this.scale.left[i])).toEqual(this.scale.trace_x[i]);
}
});
it("should work for quarter-column", function () {
for (var i = 0; i < this.scale.count - 1; i++) {
expect(gantt.dateFromPos(this.scale.left[i] + 0.25 * (this.scale.left[i + 1] - this.scale.left[i])))
.toEqual(moment(
+this.scale.trace_x[i] + 0.25 * (this.scale.trace_x[i + 1] - this.scale.trace_x[i])
).toDate());
}
});
});
describe("DateFromPos => PosFromDate", function () {
beforeAll(function () {
this._max_date = gantt._max_date;
this._min_date = gantt._min_date;
this.fullWidth = gantt._tasks.full_width;
this.scale = gantt._tasks;
});
it("should work for 300 random values from 0-fullWidth", function () {
for (var i = 0; i < 300; i++) {
var value = Math.random() * this.fullWidth;
expect(gantt.posFromDate(gantt.dateFromPos(value))).toBeCloseTo(value, 0.00001);
}
});
it("should work for 300 random values from min_date-max_date", function () {
for (var i = 0; i < 300; i++) {
var value = gantt.date.Date(this._min_date.valueOf() + Math.random() * (this._max_date - this._min_date));
expect(gantt.dateFromPos(gantt.posFromDate(value))).toEqual(value);
}
});
});
\ No newline at end of file
describe("Working time helper", function () {
var helper;
var defHours;
var workingDays;
var events;
var iso = "YYYY-MM-DD";
beforeAll(function () {
helper = gantt._working_time_helper;
defHours = helper.defHours;
helper.defHours = 8;
workingDays = helper.days;
helper.days = {0: false, 1: true, 2: true, 3: true, 4: true, 5: true, 6: false};
events = helper.dates;
helper.dates = [];
});
afterAll(function () {
helper.defHours = defHours;
helper.days = workingDays;
helper.dates = events;
});
beforeEach(function () {
helper._cache = {};
});
it("should mark 29.6.2016 as weekend", function () {
expect(helper.is_weekend(moment("2016-05-29"))).toBe(true);
});
it("should mark 30.6.2016 as non-weekend", function () {
expect(helper.is_weekend(moment("2016-05-30"))).toBe(false);
});
it("should return 8h for working day", function () {
expect(helper.get_working_hours(moment("2016-06-03"))).toBe(8);
});
it("should return 0h for weekend", function () {
expect(helper.get_working_hours(moment("2016-06-04"))).toBe(0);
});
describe("date ranges", function () {
var start = moment("2016-06-01");
var end = moment("2016-06-30");
it("should return 21d for June", function () {
expect(helper.get_work_units_between(start, end, "day")).toBe(21);
});
it("should return 168h for June", function () {
expect(helper.get_work_units_between(start, end, "hour")).toBe(168);
});
});
describe("date ranges with _end", function () {
var start = moment("2016-06-01");
var end = moment("2016-06-30");
end._isEndDate = true;
it("should return 22d for June", function () {
expect(helper.get_work_units_between(start, end, "day")).toBe(22);
});
it("should return 176h for June", function () {
expect(helper.get_work_units_between(start, end, "hour")).toBe(176);
});
});
describe("inverted date ranges with _end", function () {
var start = moment("2016-07-15");
var end = moment("2016-06-30");
end._isEndDate = true;
it("should return -10d for half July", function () {
expect(helper.get_work_units_between(start, end, "day")).toBe(-10);
});
it("should return -80h for half July", function () {
expect(helper.get_work_units_between(start, end, "hour")).toBe(-80);
});
});
describe("float date ranges with _end", function () {
var start = moment("2016-06-01").add(8, "hours");
var end = moment("2016-06-30").add(8, "hours");
end._isEndDate = true;
it("should return 22d for June", function () {
expect(helper.get_work_units_between(start, end, "day")).toBe(22);
});
it("should return 176h for June", function () {
expect(helper.get_work_units_between(start, end, "hour")).toBe(176);
});
});
describe("float date ranges on weekend", function () {
var start = moment("2016-05-22").add(6, "hours");
var end = moment("2016-05-30").add(6, "hours");
it("should return 5.25d", function () {
expect(helper.get_work_units_between(start, end, "day")).toBe(5.25);
});
it("should return 42h", function () {
expect(helper.get_work_units_between(start, end, "hour")).toBe(42);
});
});
describe("get_closest_worktime", function () {
it("should return Monday from middle of Saturday", function () {
var start_date = moment("2017-03-25 06:00");
var end_date = gantt._working_time_helper.get_closest_worktime({date: start_date, unit: "day", dir: "future"});
var compareDate = moment("2017-03-27 00:00");
expect(end_date.toISOString()).toBe(compareDate.toISOString());
});
it("should return same from middle of Friday", function () {
var start_date = moment("2017-03-24 06:00");
var end_date = gantt._working_time_helper.get_closest_worktime({date: start_date, unit: "day", dir: "future"});
var compareDate = moment("2017-03-24 06:00");
expect(end_date.toISOString()).toBe(compareDate.toISOString());
});
});
});
describe("Working time helper", function () {
var helper;
var defHours;
var workingDays;
var events;
var iso = "YYYY-MM-DD";
beforeAll(function () {
helper = gantt._working_time_helper;
defHours = helper.defHours;
helper.defHours = 8;
workingDays = helper.days;
helper.days = {0: false, 1: true, 2: true, 3: true, 4: true, 5: true, 6: false};
events = helper.dates;
helper.dates = [];
});
afterAll(function () {
helper.defHours = defHours;
helper.days = workingDays;
helper.dates = events;
});
beforeEach(function () {
helper._cache = {};
});
describe("addWorkingTime()", function () {
var start = moment("2016-05-20");
var start2 = moment(start);
start2._isEndDate = true;
it("should return 2016-05-24 after adding 2 days", function () {
expect(helper.add_worktime(start, 2, "day", false).format(iso)).toBe("2016-05-24");
});
it("should return 2016-05-23 after adding 2 days with _end", function () {
expect(helper.add_worktime(start, 2, "day", true).format(iso)).toBe("2016-05-23");
});
it("should return 2016-05-13 after subtracting 5 days", function () {
expect(helper.add_worktime(start, -5, "day", false).format(iso)).toBe("2016-05-13");
});
it("should return 2016-05-12 after subtracting 5 days", function () {
expect(helper.add_worktime(start, -5, "day", true).format(iso)).toBe("2016-05-12");
});
it("should return 2016-05-25 after adding 2 days with start._end", function () {
expect(helper.add_worktime(start2, 2, "day", false).format(iso)).toBe("2016-05-25");
});
it("should return 2016-05-16 after subtracting 5 days with start._end", function () {
expect(helper.add_worktime(start2, -5, "day", false).format(iso)).toBe("2016-05-16");
});
it("should return 2016-05-24+12 after adding 2 days", function () {
var start12 = moment(start).add(12, "hours");
expect(helper.add_worktime(start12, 2, "day", false).format()).toBe("2016-05-24T12:00:00+02:00");
});
it("should return 2017-04-05 23:00 after subtracting 2 days (end)", function () {
var end_date = moment("2017-04-06 23:00");
end_date._isEndDate = true;
var start_date = gantt._working_time_helper.add_worktime(end_date, -2, "day", false);
var compareDate = moment("2017-04-05 23:00");
expect(start_date.toISOString()).toBe(compareDate.toISOString());
});
it("should return 2017-03-30 23:00 after subtracting 2 days (end)", function () {
var end_date = moment("2017-04-02 23:00");
end_date._isEndDate = true;
var start_date = gantt._working_time_helper.add_worktime(end_date, -2, "day", false);
var compareDate = moment("2017-03-30 23:00");
expect(start_date.toISOString()).toBe(compareDate.toISOString());
});
it("should return", function () {
var start_date = moment("2017-04-05 23:00");
var end_date = gantt._working_time_helper.add_worktime(start_date, 2, "day", true);
var compareDate = moment("2017-04-06 23:00");
expect(end_date.toISOString()).toBe(compareDate.toISOString());
});
it("should return", function () {
var start_date = moment("2017-03-30 23:00");
var end_date = gantt._working_time_helper.add_worktime(start_date, 2, "day", true);
var compareDate = moment("2017-04-02 23:00");
expect(end_date.toISOString()).toBe(compareDate.toISOString());
});
it("should return same date in middle of Friday", function () {
var start_date = moment("2017-03-24 06:00");
var end_date = gantt._working_time_helper.add_worktime(start_date, -0, "day", false);
var compareDate = moment("2017-03-24 06:00");
expect(end_date.toISOString()).toBe(compareDate.toISOString());
});
it("should return Monday in middle of Saturday", function () {
var start_date = moment("2017-03-25 06:00");
var end_date = gantt._working_time_helper.add_worktime(start_date, -0, "day", false);
var compareDate = moment("2017-03-27 00:00");
expect(end_date.toISOString()).toBe(compareDate.toISOString());
});
});
});
This source diff could not be displayed because it is too large. You can view the blob instead.
/* loader.js */
/* global ysy */
window.ysy = window.ysy || {};
ysy.data = ysy.data || {};
ysy.data.loader = ysy.data.loader || {};
$.extend(ysy.data.loader, {
/*
* this object is responsible for downloading and preparing data from server
*/
_name: "Loader",
loaded: false,
inited: false,
issueIdListMap: {},
_onChange: [],
init: function () {
var settings = ysy.settings;
var data = ysy.data;
if (settings.paths.rootPath.substr(-1) !== "/") {
settings.paths.rootPath += "/";
}
if (!settings.project) {
settings.global = true;
}
if (settings.project) {
settings.projectID = parseInt(settings.project.id);
}
settings.zoom = new ysy.data.Data();
settings.zoom.init({zoom: data.storage.getSavedZoom() || ysy.settings.defaultZoom || "day", _name: "Zoom"});
settings.controls = new ysy.data.Data();
settings.controls.init({controls: true, _name: "Task controls"});
settings.baseline = new ysy.data.Data();
settings.baseline.init({open: false, _name: "Baselines"});
settings.critical = new ysy.data.Data();
settings.critical.init({open: false, active: false, _name: "Critical path"});
settings.addTask = new ysy.data.Data();
settings.addTask.init({open: false, type: "issue", _name: "Add Task"});
settings.resource = new ysy.data.Data();
settings.resource.init({open: false, _name: "Resource Management"});
settings.scheme = new ysy.data.Data();
settings.scheme.init({by: ysy.settings.schemeBy, _name: "Schema switch"});
settings.sumRow = new ysy.data.Data();
settings.sumRow.init({_name: "SumRow"});
settings.sample = new data.Data();
data.limits = new data.Data();
data.limits.init({_name: "Limits", openings: {}});
data.relations = new data.Array().init({_name: "RelationArray"});
data.issues = new data.Array().init({_name: "IssueArray"});
data.milestones = new data.Array().init({_name: "MilestoneArray"});
data.projects = new data.Array().init({_name: "ProjectArray"});
data.baselines = new data.Array().init({_name: "BaselineArray"});
ysy.view.patch();
ysy.proManager.patch();
settings.sample.init();
this.inited = true;
},
load: function () {
// second part of program initialization
this.loaded = false;
//this.projects=new ysy.data.Array;
//var data=ysy.availableProjects;
ysy.log.debug("load()", "load");
if (ysy.settings.sample.active) {
ysy.pro.sample.loadSample(ysy.settings.sample.active);
} else {
ysy.gateway.loadGanttdata(
$.proxy(this._handleMainGantt, this),
function () {
ysy.log.error("Error: Unable to load data");
}
);
}
},
loadSubEntity: function (type, id) {
if (type === "project") {
return this.loadProject(id);
}
},
register: function (func, ctx) {
this._onChange.push({func: func, ctx: ctx});
},
/**
*
* @param {Array.<{id:int}>} array
* @param {Array.<int>} oldIds
* @return {Array.<{id:int}>}
*/
reorderArray: function (array, oldIds) {
var newArray = [];
var arrayPointer = 0;
for (var i = 0; i < array.length; i++) {
if (oldIds.length === i || oldIds[i] !== array[i].id) {
if (i > 0) {
newArray = array.slice(0, i);
arrayPointer = i;
oldIds = oldIds.slice(i);
}
break;
}
}
var banned = [];
for (i = 0; i < oldIds.length; i++) {
for (var j = arrayPointer; j < array.length; j++) {
if (array[j].id === oldIds[i]) {
newArray.push(array[j]);
banned.push(array[j]);
break;
}
}
}
for (i = arrayPointer; i < array.length; i++) {
if (banned.indexOf(array[i]) > -1) continue;
newArray.push(array[i]);
}
return newArray;
},
_fireChanges: function (who, reason) {
for (var i = 0; i < this._onChange.length; i++) {
var ctx = this._onChange[i].ctx;
if (!ctx || ctx.deleted) {
this._onChange.splice(i, 1);
continue;
}
//this.onChangeNew[i].func();
ysy.log.log("-- changes to " + (ctx.name ? ctx.name : ctx._name) + " widget");
$.proxy(this._onChange[i].func, ctx)();
}
},
_handleMainGantt: function (data) {
if (!data.easy_gantt_data) return;
var json = data.easy_gantt_data;
ysy.log.debug("_handleGantt()", "load");
// -- LIMITS --
//ysy.data.limits.set({ // TODO
// start_date: moment(json.start_date, "YYYY-MM-DD"),
// end_date: moment(json.end_date, "YYYY-MM-DD")
//});
// -- COLUMNS --
ysy.data.columns = json.columns;
// ARRAY INITIALIZATION
// -- RELATIONS --
ysy.data.relations.clear();
// -- ISSUES --
ysy.data.issues.clear();
// -- MILESTONES --
ysy.data.milestones.clear();
// -- PROJECTS --
ysy.data.projects.clear();
// ARRAY FILLING
// -- PROJECTS --
this._loadProjects(json.projects);
// -- ISSUES --
this._loadIssues(json.issues, "root");
// -- MILESTONES --
this._loadMilestones(json.versions); // after issue loading because of shared milestones
// -- RELATIONS --
this._loadRelations(json.relations);
this._loadHolidays(json.holidays);
// -- SCHEMES --
if (this._loadSchemes) this._loadSchemes(json.schemes);
ysy.log.debug("data loaded", "load");
ysy.log.message("JSON loaded");
this._fireChanges();
ysy.history.clear();
this.loaded = true;
},
_loadIssues: function (json, rootId) {
if (!json) return;
if (rootId) {
if (this.issueIdListMap[rootId]) {
json = ysy.data.loader.reorderArray(json, this.issueIdListMap[rootId]);
}
this.issueIdListMap[rootId] = json.map(function (item) {
return item.id;
});
}
var issues = ysy.data.issues;
for (var i = 0; i < json.length; i++) {
var issue = new ysy.data.Issue();
issue.init(json[i]);
issues.pushSilent(issue);
}
issues._fireChanges(this, "load");
},
_loadRelations: function (json) {
if (!json) return;
var relations = ysy.data.relations;
var allowedTypes = {
precedes: true,
finish_to_finish: true,
start_to_start: true,
start_to_finish: true
};
for (var i = 0; i < json.length; i++) {
// TODO enable other relation types
if (allowedTypes[json[i].type]) {
var rela = new ysy.data.Relation();
} else {
rela = new ysy.data.SimpleRelation();
}
rela.init(json[i]);
relations.pushSilent(rela);
}
relations._fireChanges(this, "load");
},
_loadMilestones: function (json) {
if (!json) return;
var milestones = ysy.data.milestones;
for (var i = 0; i < json.length; i++) {
var mile = new ysy.data.Milestone();
mile.init(json[i]);
milestones.pushSilent(mile);
var issues = mile.getIssues();
var projectIds = {};
for (var j = 0; j < issues.length; j++) {
projectIds[issues[j].project_id] = true;
}
delete projectIds[mile.project_id];
var sharedForIds = Object.getOwnPropertyNames(projectIds);
if (sharedForIds.length === 0) continue;
var realProjectId = json[i].project_id;
delete json[i].project_id;
for (j = 0; j < sharedForIds.length; j++) {
var sharedMile = new ysy.data.SharedMilestone();
$.extend(sharedMile, {
project_id: parseInt(sharedForIds[j]),
real_project_id: realProjectId,
real_milestone: milestones.getByID(mile.id)
});
sharedMile.init(json[i]);
milestones.pushSilent(sharedMile);
}
}
milestones._fireChanges(this, "load");
},
_loadProjects: function (json) {
if (!json) return;
var projects = ysy.data.projects;
//var main_id = ysy.settings.projectID;
for (var i = 0; i < json.length; i++) {
//if (json[i].id === main_id) continue;
var project = new ysy.data.Project();
project.init(json[i]);
projects.pushSilent(project);
}
projects._fireChanges(this, "load");
var openings = ysy.data.limits.openings;
for (var id in openings) {
if (!openings.hasOwnProperty(id)) continue;
if (ysy.main.startsWith(id, "p")) {
var realId = id.substring(1);
project = projects.getByID(realId);
if (!project) continue;
if (!project.needLoad) continue;
project.needLoad = false;
this.loadProject(realId);
} else if (this.openIssuesOfProject && ysy.main.startsWith(id, "s")) {
realId = id.substring(1);
if (!openings["p" + realId]) continue;
this.openIssuesOfProject(realId);
}
}
},
_loadHolidays: function (json) {
if (!json) return;
ysy.settings.holidays = json;
ysy.view.initNonworkingDays();
},
loadProject: function () {
}
});
if (!ysy.gateway) ysy.gateway = {};
$.extend(ysy.gateway, {
loadGanttdata: function (callback, fail) {
$.getJSON(ysy.settings.paths.mainGantt)
.done(callback)
.fail(fail);
}
});
/* logger.js */
/* global ysy */
window.ysy = window.ysy || {}; window.ysy = window.ysy || {};
ysy.log = { ysy.log = {
logLevel: 2, logLevel: 2,
mainDebug: "", mainDebug: "",
debugTypes: [ debugTypes: [
//"refresher", // "refresher",
//"critical", // "critical",
//"canvas_bg", // "canvas_bg",
//"set", // "set",
//"baseline", // "baseline",
//"baseline_render", // "baseline_render",
//"inline", // "inline",
//"print", // "print",
//"move_task", // "move_task",
//"add_task", // "add_task",
//"add_task_marker", // "add_task_marker",
//"taskModal", // "taskModal",
//"date_format", // "date_format",
//"date_helper", // "date_helper",
//"date", // "date",
//"tooltip", // "tooltip",
//"send", // "send",
//"load", // "load",
//"supersend", // "supersend",
//"scroll", // "scroll",
//"grid_resize", // "scrollRender",
//"task_drag", // "grid_resize",
//"task_push", // "task_drag",
//"link_render", // "asc",
//"outer", // "task_push",
//"sort", // "link_render",
//"link_config", // "outer",
//"link_drag", // "sort",
//"empty_field", // "link_config",
//"widget_destroy", // "link_drag",
//"resource", // "empty_field",
// "task_drag_milestone",
// "widget_destroy",
// "resource",
// "summer",
"nothing" "nothing"
], ],
log: function (text) { log: function (text) {
...@@ -83,4 +89,4 @@ ysy.log = { ...@@ -83,4 +89,4 @@ ysy.log = {
console.log(text); console.log(text);
} }
} }
}; };
\ No newline at end of file
/* main.js */
/* global ysy */
window.ysy = window.ysy || {};
ysy.main = ysy.main || {};
ysy.initGantt = function () {
$("p.nodata").remove();
ysy.data.loader.init();
ysy.data.loader.load();
ysy.data.storage.init();
if (!ysy.settings.easyRedmine) {
moment.locale(ysy.settings.language || "en");
}
ysy.view.start();
//ysy.main.start();
};
/* pro_manager.js */
/* global ysy */
window.ysy = window.ysy || {};
ysy.proManager = ysy.proManager || {};
ysy.pro = ysy.pro || {};
$.extend(ysy.proManager, {
proFunctionsMap: {},
name:"proManager",
patch: function () {
window.ysy = window.ysy || {};
ysy.settings = ysy.settings || {};
for (var key in ysy.pro) {
if (!ysy.pro.hasOwnProperty(key)) continue;
if (ysy.pro[key].patch) {
ysy.pro[key].patch();
}
}
},
forEachPro: function (wrapperFunc, event) {
var proFunctions = this.proFunctionsMap[event];
if (!proFunctions) return;
for (var i = 0; i < proFunctions.length; i++) {
wrapperFunc.call(this, proFunctions[i]);
}
},
fireEvent: function (event) {
var proFunctions = this.proFunctionsMap[event];
if (!proFunctions) return;
var slicedArgs = Array.prototype.slice.call(arguments, 1);
for (var i = 0; i < proFunctions.length; i++) {
proFunctions[i].apply(this, slicedArgs);
}
},
register: function (event, func) {
if (!func) throw "missing call function";
var eventList = this.proFunctionsMap[event];
if (!eventList) this.proFunctionsMap[event] = eventList = [];
for (var i = 0; i < eventList.length; i++) {
if (eventList[i] === func) {
return;
}
}
eventList.push(func);
},
unregister: function (event, func) {
var eventList = this.proFunctionsMap[event];
if (!eventList) return;
for (var i = 0; i < eventList.length; i++) {
if (eventList[i] === func) {
eventList.splice(i, 1);
return;
}
}
},
showHelp: function () {
var div = $(this).next();
var x = div.clone().attr({"id": div[0].id + "_popup"}).appendTo($("body"));
showModal(x[0].id);
},
closeAll: function (except) {
this.forEachPro(function (func) {
if (except.close !== func) func.call(this,except);
}, "close");
},
eventFilterTask: function (id, task) {
var ret = true;
this.forEachPro(function (func) {
if (ret) ret = func(id, task);
}, "filterTask");
return ret;
}
});
/* problem_finder.js */
/* global ysy */
window.ysy = window.ysy || {};
ysy.pro = ysy.pro || {};
ysy.pro.problemFinder = $.extend(ysy.pro.problemFinder, {
patch: function () {
var setting = new ysy.data.Data();
setting.init({
_name: "Problem finder",
opened: false,
issueProblems: [],
relationProblems: []
});
ysy.settings.problemFinder = setting;
ysy.data.issues.childRegister(function () {
ysy.pro.problemFinder.recalculateProblemsInIssues.call(this);
ysy.pro.problemFinder.recalculateProblemsInRelations.call(this);
}, setting);
ysy.data.relations.childRegister(this.recalculateProblemsInRelations, setting);
ysy.data.issues.register(this.recalculateProblemsInIssues, setting);
ysy.data.projects.register(this.recalculateProblemsInIssues, setting);
ysy.data.relations.register(this.recalculateProblemsInRelations, setting);
ysy.proManager.register("close", this.close);
ysy.view.AllButtons.prototype.extendees.problem_finder = {
widget: ysy.view.ProblemFinder,
bind: function () {
this.model = setting;
},
func: function () {
this.model.setSilent({opened: !this.model.opened});
this.model._fireChanges(this, "open problem list");
},
isOn: function () {
return this.model.opened;
},
isHidden: function () {
return this.problemCount() === 0;
}
};
},
recalculateProblemsInIssues: function () {
var problems = [];
var array = ysy.data.issues.getArray();
if (ysy.settings.projectProgress) {
array = array.concat(ysy.data.projects.getArray());
}
for (var i = 0; i < array.length; i++) {
var entity = array[i];
var entityProblems = entity.getProblems();
if (!entityProblems) continue;
for (var j = 0; j < entityProblems.length; j++) {
problems.push({
entity: entity,
text: entityProblems[j]
})
}
}
this.issueProblems = problems;
this._fireChanges(this, "issues problems recalculated");
},
recalculateProblemsInRelations: function () {
var problems = [];
var array = ysy.data.relations.getArray();
for (var i = 0; +i < array.length; i++) {
var entity = array[i];
var entityProblems = entity.getProblems();
if (!entityProblems) continue;
for (var j = 0; j < entityProblems.length; j++) {
problems.push({
relation: entity,
text: entityProblems[j]
})
}
}
this.relationProblems = problems;
this._fireChanges(this, "relations problems recalculated");
},
close: function (who) {
ysy.settings.problemFinder.setSilent({opened: false});
ysy.settings.problemFinder._fireChanges(who, "event close");
},
scrollToEntity: function (entityId) {
var task = gantt._pull[entityId];
if (!task) return;
gantt.selectTask(entityId);
gantt.showTask(entityId);
}
});
ysy.view.ProblemFinder = function () {
ysy.view.Widget.call(this);
this.listIsHidden = true;
};
ysy.main.extender(ysy.view.Button, ysy.view.ProblemFinder, {
templateName: "ProblemFinder",
elementPrefix: "button_",
outerClickBind: false,
specialRepaint: function (hidden) {
if (hidden && this.listIsHidden) return;
var $problemList = $("#gantt_problem_list");
if (hidden) {
$problemList.hide();
this.listIsHidden = true;
return;
}
var rendered = Mustache.render(ysy.view.getTemplate(this.templateName), {
count: this.problemCount()
});
this.$target.html(rendered);
if (this.model.opened) {
if (!$problemList.length) {
var $button = $("#button_problem_finder");
var offset = $button.position().top;
var viewHeight = Math.min($(window).height() - 105 - offset, $("#easy_gantt").height() - 95);
$problemList = $('<div id="gantt_problem_list" class="gantt-menu-problems-list"></div>').insertAfter($button)
.css({maxHeight: viewHeight + "px"})
}
this.bindOuterClick();
rendered = Mustache.render(ysy.view.getTemplate(this.templateName + "List"), this.out());
$problemList.html(rendered).show();
this.listIsHidden = false;
} else {
$problemList.hide();
this.listIsHidden = true;
}
},
out: function () {
var issueProblems = [];
var relationProblems = [];
var problems = this.model.issueProblems;
for (var i = 0; i < problems.length; i++) {
var problem = problems[i];
issueProblems.push({
isProject: problem.entity.isProject,
name: problem.entity.name,
text: problem.text,
entityId: problem.entity.getID()
});
}
var issues = ysy.data.issues;
problems = this.model.relationProblems;
for (i = 0; i < problems.length; i++) {
problem = problems[i];
var source = issues.getByID(problem.relation.source_id);
var target = issues.getByID(problem.relation.target_id);
relationProblems.push({
sourceName: (source ? source.name : "#" + problem.relation.source_id),
targetName: (target ? target.name : "#" + problem.relation.target_id),
text: problem.text,
entityId: problem.relation.target_id
});
}
return {"entities": issueProblems, "relations": relationProblems};
},
problemCount: function () {
return this.model.issueProblems.length + this.model.relationProblems.length;
},
bindOuterClick: function () {
if (this.outerClickBind) return;
var self = this;
$(document).on("click.problem_finder", function (e) {
//is inside ProblemList
if (e && $(e.target).closest("#gantt_problem_list").length) return;
$(document).off("click.problem_finder");
self.outerClickBind = false;
ysy.pro.problemFinder.close(self);
});
this.outerClickBind = true;
}
});
window.ysy = window.ysy || {};
ysy.pro = ysy.pro || {};
ysy.pro.sample = {
patch: function () {
var setting = ysy.settings.sample;
$.extend(ysy.view.AllButtons.prototype.extendees, {
sample: {
bind: function () {
this.model = ysy.settings.sample;
},
func: function () {
if (ysy.data.loader.loaded) {
this.model.toggle();
ysy.data.loader.load();
}
},
isOn: function () {
return this.model.active;
}
//icon:"zoom-in icon-day"
}
});
$.extend(setting, {
init: function () {
if (ysy.settings.easyRedmine || this.isViewed()) {
this.prevented = true;
}
this.active = this.getSampleVersion();
},
getSampleVersion: function (turnOn) {
if (ysy.settings.global) return "global";
if (turnOn === false) return 0;
if (turnOn === true) return 1;
return this.prevented ? 0 : 1;
},
toggle: function (turnOn) {
if (turnOn === undefined) {
turnOn = !this.active;
}
this.setSilent("active", this.getSampleVersion(turnOn));
this._fireChanges(this, "toggle");
},
storageKey: "sample_viewed",
setViewed: function () {
ysy.data.storage.savePersistentData(this.storageKey, true);
},
isViewed: function () {
return ysy.data.storage.getPersistentData(this.storageKey);
}
});
},
_loadSampleData: function (data) {
if (!data.easy_gantt_data) return;
var json = data.easy_gantt_data;
var projects = json.projects;
for (var i = 0; i < projects.length; i++) {
projects[i].needLoad = false;
projects[i].permissions = {editable: true};
}
var issues = json.issues;
for (i = 0; i < issues.length; i++) {
issues[i].permissions = {editable: true};
}
var versions = json.versions;
for (i = 0; i < versions.length; i++) {
versions[i].permissions = {editable: true};
}
ysy.data.loader._handleMainGantt(data);
},
loadSample:function (sampleVersion) {
ysy.gateway.polymorficGetJSON(
ysy.settings.paths.sample_data.replace("{{version}}", sampleVersion), null,
$.proxy(this._loadSampleData, this),
function () {
ysy.log.error("Error: Example data fetch failed");
}
);
}
};
//##############################################################################
ysy.view.SuperPanel = function () {
ysy.view.Widget.call(this);
};
ysy.main.extender(ysy.view.Widget, ysy.view.SuperPanel, {
name: "SuperPanelWidget",
templateName: "SuperPanel",
_repaintCore: function () {
if (!this.template) {
var templ = ysy.view.getTemplate(this.templateName);
if (templ) {
this.template = templ;
} else {
return true;
}
}
var rendered = Mustache.render(this.template, this.out()); // REPAINT
var $easygantt = $("#easy_gantt");
$easygantt.find(".flash").remove();
this.$target = $(rendered);
$easygantt.prepend(this.$target);
//window.showFlashMessage("notice",rendered);
this.tideFunctionality();
},
out: function () {
var obj, label;
var free = !!ysy.settings.sample.getSampleVersion(false);
if (free) {
label = ysy.view.getLabel("sample_global_free_text");
obj = {global_free: true};
} else {
label = ysy.view.getLabel("sample_text");
obj = {};
}
return $.extend({}, {text: label}, {sample: this.model.active}, obj);
},
tideFunctionality: function () {
this.$target.find("#sample_close_button").click($.proxy(function () {
if (ysy.data.loader.loaded) {
this.model.setViewed();
this.model.setSilent("active", false);
this.model._fireChanges(this, "toggle");
ysy.data.loader.load();
}
}, this));
this.$target.find("#sample_video_button").click($.proxy(function () {
if (ysy.settings.global) {
var template = ysy.view.getTemplate("video_modal_global");
} else {
template = ysy.view.getTemplate("video_modal");
}
var $modal = ysy.main.getModal("video-modal", "850px");
$modal.html(template); // REPAINT
$modal.off("dialogclose");
window.showModal("video-modal", 850);
$modal.on("dialogclose", function () {
$modal.empty();
});
}));
}
});
\ No newline at end of file
/* storage.js */
/* global ysy */
window.ysy = window.ysy || {};
ysy.data = ysy.data || {};
ysy.data.storage = ysy.data.storage || {};
$.extend(ysy.data.storage, {
_scope: "easy-gantt-",
zoomKey: "zoom",
init: function () {
// ZOOM saving
ysy.settings.zoom.register(function () {
this.saveSessionData(this.zoomKey, ysy.settings.zoom.zoom);
}, this);
},
getSavedZoom: function () {
return this.getSessionData(this.zoomKey);
},
saveCookie: function (key, value) {
$.cookie(this._scope + key, value, {expires: 3650, path: '/'});
},
getCookie: function (key) {
return $.cookie(this._scope + key);
},
saveSessionData: function (key, value) {
window.sessionStorage.setItem(this._scope + key, value);
},
getSessionData: function (key) {
return window.sessionStorage.getItem(this._scope + key);
},
savePersistentData: function (key, value) {
window.localStorage.setItem(this._scope + key, value);
},
getPersistentData: function (key) {
return window.localStorage.getItem(this._scope + key);
}
});
/* sumrow.js */
/* global ysy */
window.ysy = window.ysy || {};
ysy.pro = ysy.pro || {};
ysy.pro.sumRow = ysy.pro.sumRow || {};
$.extend(ysy.pro.sumRow, {
summers: {
count: {
day: function (date, issue) {
if (issue._start_date.isAfter(date)) return 0;
if (issue._end_date.isBefore(date)) return 0;
return 1;
},
week: function (first_date, last_date, issue) {
if (issue._start_date.isAfter(last_date)) return 0;
if (issue._end_date.isBefore(first_date)) return 0;
return 1;
},
//formatter:function(value){return value},
entities: ["issues"],
title: "Count"
}
},
patch: function () {
$.extend(ysy.settings.sumRow, {
summerArray: [],
setSummer: function (summer) {
if (!ysy.pro.sumRow.summers[summer]) return;
var index = this.summerArray.indexOf(summer);
if (index !== -1) {
this.summerArray.splice(index, 1);
}
this.summerArray.push(summer);
this.setSilent({
active: true,
summer: summer
});
this._fireChanges(this, "setSummer to " + summer);
},
removeSummer: function (summer) {
var index = this.summerArray.indexOf(summer);
if (index !== -1) {
this.summerArray.splice(index, 1);
}
if (this.summerArray.length === 0) {
this.setSilent({
active: false,
summer: false
});
} else {
this.setSilent({
active: true,
summer: this.summerArray[this.summerArray.length - 1]
});
}
this._fireChanges(this, "removeSummer " + summer);
}
});
// ysy.settings.sumRow.setSummer("count");
}
});
ysy.view = ysy.view || {};
ysy.view.SumRow = function () {
ysy.view.Widget.call(this);
this.temper = {
values: []
};
};
ysy.main.extender(ysy.view.Widget, ysy.view.SumRow, {
name: "SumRowWidget",
_postInit: function () {
ysy.data.issues.childRegister(this.invalidateIssues, this);
ysy.data.projects.childRegister(this.invalidateProjects, this);
ysy.data.issues.register(this.invalidateIssues, this);
ysy.data.projects.register(this.invalidateProjects, this);
gantt.attachEvent("onGanttRender", $.proxy(this.requestRepaint, this));
},
nopeFormatter: function () {
return ""
},
invalidateProjects: function () {
if (this.summer && this.summer.entities && this.summer.entities.indexOf("projects") > -1)
this.requestRepaint();
},
invalidateIssues: function () {
if (this.summer && (!this.summer.entities || this.summer.entities.indexOf("issues") > -1))
this.requestRepaint();
},
getSubScale: function (zoom) {
var summer = ysy.pro.sumRow.summers[ysy.settings.sumRow.summer];
if (!summer) return;
ysy.log.debug("getSubScale for " + summer.title, "summer");
this.summer = summer;
this.updateTemper();
var temper = this.temper;
// var max = Math.max.apply(null, values);
// var min = Math.min.apply(null, values);
var template = this.nopeFormatter;
if (temper.values) {
if (summer.formatter) {
template = function (date) {
var index = -temper.minDate.diff(date, zoom);
if (index < 0 || index > temper.valuesLength) return "";
return summer.formatter(temper.values[index], temper.widths[index]);
}
} else {
template = function (date) {
var index = -temper.minDate.diff(date, zoom);
if (index < 0 || index > temper.valuesLength) return "";
return temper.values[index];
}
}
}
return {
step: 1,
className: "gantt-sum-row",
unit: zoom,
template: template
};
},
getSummerFunction: function (zoom) {
return zoom === "day" ? this.summer.day : this.summer.week;
},
getEntities: function () {
if (!this.summer) return null;
var types = this.summer.entities || ["issues"];
var entities = [];
for (var i = 0; i < types.length; i++) {
entities = entities.concat(ysy.data[types[i]].getArray());
}
return entities;
},
_repaintCore: function () {
if (!ysy.settings.sumRow.active) return;
ysy.log.debug("sumRow repaintCore", "summer");
var summer = ysy.pro.sumRow.summers[ysy.settings.sumRow.summer];
if (!summer) return;
this.summer = summer;
this.updateTemper(this.calculateSums(ysy.settings.zoom.zoom));
var $target = $(".gantt_scale_line.gantt-sum-row");
var config = gantt.config;
var resize = gantt._get_resize_options();
var avail_width = resize.x ? Math.max(config.autosize_min_width, 0) : gantt.$task.offsetWidth;
var cfgs = gantt._scale_helpers.prepareConfigs([this.getSubScale(ysy.settings.zoom.zoom)], config.min_column_width, avail_width, config.scale_height - 1);
var html = gantt._prepare_scale_html(cfgs[0]);
$target.html(html);
},
updateTemper: function (values) {
if (values) {
this.temper.values = values;
this.temper.valuesLength = values.length;
}
//this.temper.fullRange = moment(gantt._max_date).diff(gantt._min_date, ysy.settings.zoom.zoom);
this.temper.minDate = moment(gantt._min_date);
this.temper.widths = gantt._tasks.width;
},
calculateSums: function (unit) {
var summerFunction = this.getSummerFunction(unit);
var values = [];
var mover = moment(gantt._min_date);
var maxDate = moment(gantt._max_date);
var issues = this.getEntities();
// var lastValue = 0;
if (unit === "day") {
while (mover.isBefore(maxDate)) {
var count = 0;
for (var i = 0; i < issues.length; i++) {
count += summerFunction(mover, issues[i]);
}
// if (summer.cumulative) {
// count += lastValue;
// }
values.push(count);
// lastValue = count;
mover.add(1, unit);
}
} else {
var nextMover = moment(mover).add(1, unit);
while (mover.isBefore(maxDate)) {
count = 0;
for (i = 0; i < issues.length; i++) {
count += summerFunction(mover, nextMover, issues[i]);
}
// if (summer.cumulative) {
// count += lastValue;
// }
values.push(count);
// lastValue = count;
mover.add(2, unit);
var tempMover = mover;
mover = nextMover;
nextMover = tempMover;
}
}
return values;
}
});
\ No newline at end of file
/* toolpanel.js */
/* global ysy */
window.ysy = window.ysy || {};
ysy.pro = ysy.pro || {};
ysy.pro.toolPanel = ysy.pro.toolPanel || {};
$.extend(ysy.pro.toolPanel, {
_name: "ToolPanel",
extendees: [],
initToolbar: function (ctx) {
var toolPanel = new ysy.view.ToolPanel();
toolPanel.init(ysy.settings.toolPanel);
ctx.children.push(toolPanel);
},
patch: function () {
var toolSetting = ysy.settings.toolPanel = new ysy.data.Data();
toolSetting.init({
_name: "ToolPanel", buttonIds: [], buttons: {},
registerButtonSilent: function (button) {
if (button.id === undefined) throw("Missing id for button");
this.buttons[button.id] = button;
this.buttonIds.push(button.id);
}
});
ysy.proManager.register("initToolbar", this.initToolbar);
for (var i = 0; i < this.extendees.length; i++) {
var button = this.extendees[i];
if (button.isRemoved && button.isRemoved()) continue;
toolSetting.registerButtonSilent(button);
}
toolSetting._fireChanges(this, "delayed registerButton");
delete this.extendees;
},
registerButton: function (button) {
if (button.isRemoved && button.isRemoved()) return;
if (ysy.settings.toolPanel) {
ysy.settings.toolPanel.registerButtonSilent(button);
ysy.settings.toolPanel._fireChanges(this, "direct registerButton");
} else {
this.extendees.push(button);
}
}
});
ysy.view.ToolPanel = function () {
ysy.view.Widget.call(this);
};
ysy.main.extender(ysy.view.Widget, ysy.view.ToolPanel, {
name: "ToolPanelWidget",
templateName: "ToolButtons",
_postInit: function () {
var $toolPanel = $("#easy_gantt_tool_panel");
$toolPanel.find("a:not([href])").attr("href", "javascript:void(0)");
$toolPanel.find("li > *").hide();
},
_updateChildren: function () {
var model = this.model;
var children = [];
// this.$target = $("#content");
for (var i = 0; i < model.buttonIds.length; i++) {
var elid = model.buttonIds[i];
var extendee = model.buttons[elid];
// if (!this.getChildTarget(extendee).length) continue;
var button;
if (extendee.widget) {
button = new extendee.widget();
} else {
button = new ysy.view.Button();
}
$.extend(button, extendee);
button.init();
children.push(button);
}
this.children = children;
},
_repaintCore: function () {
for (var i = 0; i < this.children.length; i++) {
var child = this.children[i];
this.setChildTarget(child, i);
child.repaint(true);
}
},
setChildTarget: function (child /*,i*/) {
var selector = "#" + child.elementPrefix + child.id;
if (["#select_scheme_select"].indexOf(selector) > -1) {
child.$target = $("<div>");
return;
}
var target = this.$target.find(selector);
if (target.length === 0) throw("element #" + child.elementPrefix + child.id + " missing");
child.$target = target;
}
});
/* tooltip.js */
/* global ysy */
window.ysy = window.ysy || {};
ysy.view = ysy.view || {};
ysy.view.tooltip = $.extend(ysy.view.tooltip, {
instance: null,
show: function (className, event, template, out) {
if (!this.instance) {
this.instance = new ysy.view.ToolTip().init();
}
return this.instance.set(className, event, template, out);
},
hide: function () {
if (this.instance)
return this.instance.hide();
return false;
},
changePos: function (event) {
if (this.instance)
return this.instance.changePos(event);
}
});
ysy.view.taskTooltip = $.extend(ysy.view.taskTooltip, {
timeout: 0,
timeoutTime: 1000,
phase: 1,
/**
* phase 1 mouse is out
* phase 2 mouse on, start timer
* phase 3 tooltip display
*/
taskTooltipInit: function () {
var self = this;
/**
* phase 2 mouse on, start timer 1s
*/
$("#content")
.on("mouseenter", ".gantt_task_content, .gantt_task_progress, .gantt-task-tooltip-area", function (e) {
if (self.phase !== 1) return;
ysy.log.debug("mouseenter", "tooltip");
if (e.buttons !== 0) return;
self.phase = 2;
// ysy.log.debug("e.which = "+e.which+" e.button = "+ e.button+" e.buttons = "+ e.buttons);
self.bindHiders(e.target);
self.updatePos(e);
});
},
bindHiders: function (target) {
target.addEventListener("mouseleave", this.hideTooltip);
target.addEventListener("mousedown", this.hideTooltip);
target.addEventListener("mousemove", this.updatePos);
},
hideTooltip: function (event) {
var self = ysy.view.taskTooltip;
/**
* set phase 1 mouse out
*/
self.phase = 1;
self.lastPos = null;
if (self.timeout) {
clearTimeout(self.timeout);
}
if (ysy.view.tooltip.hide()) {
event.target.removeEventListener("mouseleave", this.hideTooltip);
event.target.removeEventListener("mousedown", this.hideTooltip);
event.target.removeEventListener("mousemove", this.updatePos);
}
},
/**
* @param {MouseEvent} event
*/
updatePos: function (event) {
var self = ysy.view.taskTooltip;
if (self.phase === 1) return;
var changed = false;
if (self.lastPos) {
if (Math.abs(self.lastPos.clientX - event.clientX) > 5) {
self.lastPos.clientX = event.clientX;
changed = true;
}
if (Math.abs(self.lastPos.clientY - event.clientY) > 5) {
self.lastPos.clientY = event.clientY;
changed = true;
}
} else {
self.lastPos = {clientX: event.clientX, clientY: event.clientY};
changed = true;
}
if (self.phase === 3 && changed) {
self.hideTooltip(event);
return;
}
self.lastPos.target = event.target;
if (changed) {
if (self.timeout) {
window.clearTimeout(self.timeout);
}
self.timeout = window.setTimeout(function () {
self.showTaskTooltip(self.lastPos)
}, self.timeoutTime);
}
},
showTaskTooltip: function (event) {
var self = this;
/**
* phase 3 tooltip display
*/
if (self.phase !== 2) return;
var task = gantt._pull[gantt.locate(event)];
if (!task) return;
self.phase = 3;
if (event.target.parentElement.parentElement === null) {
self.phase = 1;
return;
}
var taskPos = $(event.target).offset();
return ysy.view.tooltip.show("gantt-task-tooltip",
{clientX: event.clientX, clientY: event.clientY, top: taskPos.top + gantt.config.row_height},
ysy.view.templates.TaskTooltip,
self.taskTooltipOut(task));
},
taskTooltipOut: function (task) {
var issue = task.widget.model;
var problemList = issue.getProblems();
var columns = [];
if (issue.milestone) {
if (issue.isShared) {
columns = [{
name: "shared-from",
label: "Shared from project",
value: '#' + issue.real_project_id
}]
}
return {
name: issue.name,
end_date: issue.start_date.format(gantt.config.date_format),
columns: columns
};
}
var columnHeads = gantt.config.columns;
var banned = ["subject", "start_date", "end_date", "due_date"];
for (var i = 0; i < columnHeads.length; i++) {
var columnHead = columnHeads[i];
if (banned.indexOf(columnHead.name) < 0) {
var html = columnHead.template(task);
if (!html) continue;
if (html.indexOf("<") === 0) {
html = $(html).html();
}
columns.push({name: columnHead.name, label: columnHead.label, value: html});
}
}
if (issue.fixed_version_id) {
var milestone = ysy.data.milestones.getByID(issue.fixed_version_id);
}
return {
name: issue.name,
start_date: issue.start_date ? issue.start_date.format(gantt.config.date_format) : moment(null),
end_date: issue.end_date ? issue.end_date.format(gantt.config.date_format) : moment(null),
milestone: milestone,
columns: columns,
problems: problemList
};
}
});
ysy.view.ToolTip = function () {
ysy.view.Widget.call(this);
};
ysy.main.extender(ysy.view.Widget, ysy.view.ToolTip, {
name: "ToolTip",
init: function () {
var $target = this.$target = $("<div id='gantt_tooltip' style='display: none'></div>").appendTo("#content");
$target.on("mouseleave", function () {
$target[0].style.display = "none";
});
ysy.view.onRepaint.push($.proxy(this.repaint, this));
return this;
},
set: function (className, event, template, out) {
this.className = className || this.className;
this.event = event || this.event;
this.template = template || this.template;
this.outed = out;
this.repaintRequested = true;
return this.$target;
},
hide: function () {
this.$target[0].style.display = "none";//.hide();
return true;
},
changePos: function (event) {
var left = event.clientX;
var top = event.top;
if (event.clientY + this.elementHeight > window.innerHeight) {
top -= this.elementHeight + gantt.config.row_height + 4;
}
if (event.clientX + this.elementWidth > window.innerWidth) {
left -= this.elementWidth;
}
this.$target[0].style.cssText = "display: block; left: " + left + "px; top: " + top + "px";
},
_repaintCore: function () {
this.$target.html(Mustache.render(this.template, this.outed)); // REPAINT
this.$target[0].style.display = "block";
this.$target[0].className = 'gantt-tooltip ' + this.className;
this.elementWidth = this.$target.outerWidth();
this.elementHeight = this.$target.outerHeight();
var event = this.event;
if (event) {
this.changePos(event);
}
return false;
}
});
/* utils.js */
/* global ysy */
window.ysy = window.ysy || {};
ysy.main = ysy.main || {};
$.extend(ysy.main, {
extender: function (parent, child, proto) {
function ProtoCreator() {
}
ProtoCreator.prototype = parent.prototype;
child.prototype = new ProtoCreator();
child.prototype.constructor = child;
$.extend(child.prototype, proto);
},
getModal: function (id, width) {
var $target = $("#" + id);
if ($target.length === 0) {
$target = $("<div id=" + id + ">");
$target.dialog({
width: width,
appendTo: document.body,
modal: true,
resizable: false,
dialogClass: 'modal'
});
$target.dialog("close");
}
return $target;
},
startsWith: function (text, char) {
if (text.startsWith) {
return text.startsWith(char);
}
return text.toString().charAt(0) === char;
},
isSameMoment: function (date1, date2) {
if (!moment.isMoment(date1)) return false;
if (!moment.isMoment(date2)) return false;
return date1.isSame(date2);
},
/**
* Utility function for measuring performance of some code
* @example
* var perf = createPerformanceMeter("myFunction");
* perf("part 1");
* perf("part 2");
* perf.whole();
* @param {String} groupName
* @return {Function}
*/
createPerformanceMeter: function (groupName) {
var lastTime = window.performance.now();
var silence = false;
var initTime = lastTime;
var func = function (/** @param {String} name*/ name) {
if (silence) return;
var nowTime = window.performance.now();
var nameString = groupName + " " + name + ": ";
nameString = nameString.substr(0, 30);
var diffString = " " + (nowTime - lastTime).toFixed(3);
diffString = diffString.substr(diffString.length - 10);
console.debug(nameString + diffString + " ms");
lastTime = nowTime;
};
func.whole = function () {
if (silence) return;
var nowTime = window.performance.now();
var nameString = groupName + ": ";
nameString = nameString.substr(0, 30);
var diffString = " " + (nowTime - initTime).toFixed(3);
diffString = diffString.substr(diffString.length - 10);
console.debug(nameString + diffString + " ms");
};
func.silence = function (verbose) {
silence = !verbose;
};
return func;
},
/**
*
* @param {Array.<{name:String,value:String}>} formData
* @return {Object}
*/
formToJson: function (formData) {
var result = {};
var prolong = function (result, split, value) {
var key = split.shift();
if (key === "") {
result.push(value);
} else {
if (split.length > 0) {
var next = split[0];
if (!result[key]) {
if (next === "") {
result[key] = [];
} else {
result[key] = {};
}
}
prolong(result[key], split, value);
} else {
result[key] = value;
}
}
};
for (var i = 0; i < formData.length; i++) {
var split = formData[i].name.split(/]\[|\[|]/);
if (split.length > 1) {
split.pop();
}
prolong(result, split, formData[i].value);
}
return result;
}
});
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment