summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.htaccess6
-rw-r--r--Bugzilla/Attachment.pm5
-rw-r--r--Bugzilla/Template.pm3
-rwxr-xr-xattachment.cgi1
-rw-r--r--extensions/BMO/web/js/edituser_menu.js75
-rw-r--r--extensions/BugModal/Config.pm21
-rw-r--r--extensions/BugModal/Extension.pm242
-rw-r--r--extensions/BugModal/lib/ActivityStream.pm284
-rw-r--r--extensions/BugModal/lib/MonkeyPatches.pm38
-rw-r--r--extensions/BugModal/lib/WebService.pm138
-rw-r--r--extensions/BugModal/template/en/default/bug/show-modal.html.tmpl16
-rw-r--r--extensions/BugModal/template/en/default/bug_modal/activity_stream.html.tmpl284
-rw-r--r--extensions/BugModal/template/en/default/bug_modal/attachments.html.tmpl60
-rw-r--r--extensions/BugModal/template/en/default/bug_modal/cc_list.html.tmpl16
-rw-r--r--extensions/BugModal/template/en/default/bug_modal/edit.html.tmpl918
-rw-r--r--extensions/BugModal/template/en/default/bug_modal/field.html.tmpl275
-rw-r--r--extensions/BugModal/template/en/default/bug_modal/flags.html.tmpl162
-rw-r--r--extensions/BugModal/template/en/default/bug_modal/groups.html.tmpl68
-rw-r--r--extensions/BugModal/template/en/default/bug_modal/header.html.tmpl100
-rw-r--r--extensions/BugModal/template/en/default/bug_modal/module.html.tmpl38
-rw-r--r--extensions/BugModal/template/en/default/bug_modal/new_comment.html.tmpl29
-rw-r--r--extensions/BugModal/template/en/default/bug_modal/rel_time.html.tmpl21
-rw-r--r--extensions/BugModal/template/en/default/bug_modal/tracking_flags.html.tmpl96
-rw-r--r--extensions/BugModal/template/en/default/bug_modal/user.html.tmpl53
-rw-r--r--extensions/BugModal/template/en/default/hook/global/setting-descs-settings.none.tmpl11
-rw-r--r--extensions/BugModal/template/en/default/hook/global/variables-end.none.tmpl13
-rw-r--r--extensions/BugModal/web/ZeroClipboard/ZeroClipboard.min.js9
-rw-r--r--extensions/BugModal/web/ZeroClipboard/ZeroClipboard.swfbin0 -> 6580 bytes
-rw-r--r--extensions/BugModal/web/bug_modal.css666
-rw-r--r--extensions/BugModal/web/bug_modal.js730
-rw-r--r--extensions/BugModal/web/calendar.pngbin0 -> 904 bytes
-rw-r--r--extensions/BugModal/web/throbber.gifbin0 -> 723 bytes
-rw-r--r--extensions/TrackingFlags/web/js/tracking_flags.js10
-rw-r--r--js/jquery/plugins/datetimepicker/datetimepicker-min.js33
-rw-r--r--js/jquery/plugins/datetimepicker/datetimepicker.css473
-rwxr-xr-xprocess_bug.cgi12
-rwxr-xr-xshow_bug.cgi15
-rw-r--r--skins/contrib/Mozilla-OpenSans/global.css134
-rw-r--r--skins/contrib/Mozilla/global.css134
-rw-r--r--t/008filter.t2
-rw-r--r--template/en/default/attachment/created.html.tmpl16
-rw-r--r--template/en/default/attachment/updated.html.tmpl15
-rw-r--r--template/en/default/bug/create/created.html.tmpl26
-rw-r--r--template/en/default/bug/process/header.html.tmpl7
44 files changed, 4980 insertions, 275 deletions
diff --git a/.htaccess b/.htaccess
index 796d1a5af..5de1c259f 100644
--- a/.htaccess
+++ b/.htaccess
@@ -24,6 +24,11 @@
</IfModule>
</IfModule>
+# Allow ZeroClipboard for access to the clipboard
+<Files "ZeroClipboard.swf">
+ allow from all
+</Files>
+
AddType image/x-icon .ico
AddType application/font-woff .woff
@@ -82,3 +87,4 @@ RewriteRule ^form[\.:]third[\.\-:]party$ enter_bug.cgi?product=Marketing&format=
RewriteRule ^rest/(.*)$ rest.cgi/$1 [NE]
RewriteRule ^(?:latest|1\.2|1\.3)/(.*)$ extensions/BzAPI/bin/rest.cgi/$1 [NE]
RewriteRule ^bzapi/(.*)$ extensions/BzAPI/bin/rest.cgi/$1 [NE]
+RewriteRule ^data/assets/ZeroClipboard.swf(.*)$ extensions/BugModal/web/ZeroClipboard/ZeroClipboard.swf$1 [NE]
diff --git a/Bugzilla/Attachment.pm b/Bugzilla/Attachment.pm
index f5929c4a7..625d1cccf 100644
--- a/Bugzilla/Attachment.pm
+++ b/Bugzilla/Attachment.pm
@@ -664,6 +664,11 @@ sub get_attachments_by_bug {
push(@values, $user->id);
}
+ # BMO - allow loading of just non-obsolete attachments
+ if ($vars->{exclude_obsolete}) {
+ $and_restriction .= ' AND (isobsolete = 0)';
+ }
+
my $attach_ids = $dbh->selectcol_arrayref("SELECT attach_id FROM attachments
WHERE bug_id = ? $and_restriction",
undef, @values);
diff --git a/Bugzilla/Template.pm b/Bugzilla/Template.pm
index efc2b5931..3664fca81 100644
--- a/Bugzilla/Template.pm
+++ b/Bugzilla/Template.pm
@@ -1125,6 +1125,9 @@ sub create {
my $template = $class->new($config)
|| die("Template creation failed: " . $class->error());
+ # BMO - hook for defining new vmethods, etc
+ Bugzilla::Hook::process('template_after_create', { template => $template });
+
# Pass on our current language to any template hooks or inner templates
# called by this Template object.
$template->context->{bz_language} = $opts{language} || '';
diff --git a/attachment.cgi b/attachment.cgi
index d797c366d..94d43462d 100755
--- a/attachment.cgi
+++ b/attachment.cgi
@@ -535,6 +535,7 @@ sub insert {
# Detect if the user already used the same form to submit an attachment
my $token = trim($cgi->param('token'));
check_token_data($token, 'create_attachment', 'index.cgi');
+ print STDERR "here\n";
# Check attachments the user tries to mark as obsolete.
my @obsolete_attachments;
diff --git a/extensions/BMO/web/js/edituser_menu.js b/extensions/BMO/web/js/edituser_menu.js
index 85d933220..707e35b6e 100644
--- a/extensions/BMO/web/js/edituser_menu.js
+++ b/extensions/BMO/web/js/edituser_menu.js
@@ -6,43 +6,42 @@
* defined by the Mozilla Public License, v. 2.0. */
function show_usermenu(id, email, show_edit) {
- var items = {
- profile: {
- name: "Profile",
- callback: function () {
- var href = "user_profile?login=" + encodeURIComponent(email);
- window.open(href, "_blank");
- }
- },
- activity: {
- name: "Activity",
- callback: function () {
- var href = "page.cgi?id=user_activity.html&action=run&from=-14d&who="
- + encodeURIComponent(email);
- window.open(href, "_blank");
- }
- },
- mail: {
- name: "Mail",
- callback: function () {
- var href = "mailto:" + encodeURIComponent(email);
- window.open(href, "_blank");
- }
- },
- };
- if (show_edit) {
- items.edit = {
- name: "Edit",
- callback: function () {
- var href = "editusers.cgi?action=edit&userid=" + id;
- window.open(href, "_blank");
- }
- };
- }
- $.contextMenu({
- selector: ".vcard_" + id,
- trigger: "left",
- items: items
- });
+ var items = [
+ {
+ name: "Profile",
+ callback: function () {
+ var href = "user_profile?login=" + encodeURIComponent(email);
+ window.open(href, "_blank");
+ }
+ },
+ {
+ name: "Activity",
+ callback: function () {
+ var href = "page.cgi?id=user_activity.html&action=run&from=-14d&who=" + encodeURIComponent(email);
+ window.open(href, "_blank");
+ }
+ },
+ {
+ name: "Mail",
+ callback: function () {
+ var href = "mailto:" + encodeURIComponent(email);
+ window.open(href, "_blank");
+ }
+ }
+ ];
+ if (show_edit) {
+ items.push({
+ name: "Edit",
+ callback: function () {
+ var href = "editusers.cgi?action=edit&userid=" + id;
+ window.open(href, "_blank");
+ }
+ });
+ }
+ $.contextMenu({
+ selector: ".vcard_" + id,
+ trigger: "left",
+ items: items
+ });
}
diff --git a/extensions/BugModal/Config.pm b/extensions/BugModal/Config.pm
new file mode 100644
index 000000000..b4242753a
--- /dev/null
+++ b/extensions/BugModal/Config.pm
@@ -0,0 +1,21 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+package Bugzilla::Extension::BugModal;
+use strict;
+
+use constant NAME => 'BugModal';
+use constant REQUIRED_MODULES => [
+ {
+ package => 'Time-Duration',
+ module => 'Time::Duration',
+ version => 0
+ },
+];
+use constant OPTIONAL_MODULES => [ ];
+
+__PACKAGE__->NAME;
diff --git a/extensions/BugModal/Extension.pm b/extensions/BugModal/Extension.pm
new file mode 100644
index 000000000..d3dac3976
--- /dev/null
+++ b/extensions/BugModal/Extension.pm
@@ -0,0 +1,242 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+package Bugzilla::Extension::BugModal;
+
+use strict;
+use warnings;
+
+use base qw(Bugzilla::Extension);
+
+use Bugzilla::Extension::BugModal::ActivityStream;
+use Bugzilla::Extension::BugModal::MonkeyPatches;
+use Bugzilla::Constants;
+use Bugzilla::User::Setting;
+use Bugzilla::Util qw(trick_taint datetime_from html_quote);
+use List::MoreUtils qw(any);
+use Template::Stash;
+use Time::Duration;
+
+our $VERSION = '1';
+
+# force skin to mozilla
+sub setting_set_value {
+ my ($self, $args) = @_;
+ return unless $args->{setting} eq 'ui_experiments' && $args->{value} ne 'on';
+ my $settings = Bugzilla->user->settings;
+ return if $settings->{skin}->{value} =~ /^Mozilla/;
+ $settings->{skin}->set('Mozilla');
+}
+
+sub show_bug_format {
+ my ($self, $args) = @_;
+ $args->{format} = _alternative_show_bug_format();
+}
+
+sub edit_bug_format {
+ my ($self, $args) = @_;
+ $args->{format} = _alternative_show_bug_format();
+}
+
+sub _alternative_show_bug_format {
+ my $user = Bugzilla->user;
+ if (my $format = Bugzilla->cgi->param('format')) {
+ return uc($format) eq '__DEFAULT__' ? undef : $format;
+ }
+ return $user->setting('ui_experiments') eq 'on' ? 'modal' : undef;
+}
+
+sub template_after_create {
+ my ($self, $args) = @_;
+ my $context = $args->{template}->context;
+
+ # wrapper around Time::Duration::ago()
+ $context->define_filter(
+ time_duration => sub {
+ my ($context) = @_;
+ return sub {
+ my ($timestamp) = @_;
+ my $datetime = datetime_from($timestamp)
+ // return $timestamp;
+ return ago(time() - $datetime->epoch);
+ };
+ }, 1
+ );
+
+ # morph a string into one which is suitable to use as an element's id
+ $context->define_filter(
+ id => sub {
+ my ($context) = @_;
+ return sub {
+ my ($id) = @_;
+ $id //= '';
+ $id = lc($id);
+ while ($id ne '' && $id !~ /^[a-z]/) {
+ $id = substr($id, 1);
+ }
+ $id =~ tr/ /-/;
+ $id =~ s/[^a-z\d\-_:\.]/_/g;
+ return $id;
+ };
+ }, 1
+ );
+
+ # flatten a list of hashrefs to a list of values
+ # eg. logins = users.pluck("login")
+ $context->define_vmethod(
+ list => pluck => sub {
+ my ($list, $field) = @_;
+ return [ map { $_->$field } @$list ];
+ }
+ );
+
+ # returns array where the value in $field does not equal $value
+ # opposite of "only"
+ # eg. not_byron = users.skip("name", "Byron")
+ $context->define_vmethod(
+ list => skip => sub {
+ my ($list, $field, $value) = @_;
+ return [ grep { $_->$field ne $value } @$list ];
+ }
+ );
+
+ # returns array where the value in $field equals $value
+ # opposite of "skip"
+ # eg. byrons_only = users.only("name", "Byron")
+ $context->define_vmethod(
+ list => only => sub {
+ my ($list, $field, $value) = @_;
+ return [ grep { $_->$field eq $value } @$list ];
+ }
+ );
+
+ # returns boolean indicating if the value exists in the list
+ # eg. has_byron = user_names.exists("byron")
+ $context->define_vmethod(
+ list => exists => sub {
+ my ($list, $value) = @_;
+ return any { $_ eq $value } @$list;
+ }
+ );
+
+ # ucfirst is only available in new template::toolkit versions
+ $context->define_vmethod(
+ item => ucfirst => sub {
+ my ($text) = @_;
+ return ucfirst($text);
+ }
+ );
+}
+
+sub template_before_process {
+ my ($self, $args) = @_;
+ my $file = $args->{file};
+ my $vars = $args->{vars};
+
+ if ($file eq 'bug/process/header.html.tmpl'
+ || $file eq 'bug/create/created.html.tmpl'
+ || $file eq 'attachment/created.html.tmpl'
+ || $file eq 'attachment/updated.html.tmpl')
+ {
+ if (_alternative_show_bug_format()) {
+ $vars->{alt_ui_header} = 'bug_modal/header.html.tmpl';
+ $vars->{alt_ui_show} = 'bug/show-modal.html.tmpl';
+ $vars->{alt_ui_edit} = 'bug_modal/edit.html.tmpl';
+ }
+ return;
+ }
+
+ return unless $file =~ m#^bug/show-([^\.]+)\.html\.tmpl$#;
+ my $format = $1;
+ my $alt = _alternative_show_bug_format() // return;
+ return unless $alt eq $format;
+
+ return unless
+ $vars->{bugs}
+ && ref($vars->{bugs}) eq 'ARRAY'
+ && scalar(@{ $vars->{bugs} }) == 1;
+ my $bug = $vars->{bugs}->[0];
+
+ # trigger loading of tracking flags
+ Bugzilla::Extension::TrackingFlags->template_before_process({
+ file => 'bug/edit.html.tmpl',
+ vars => $vars,
+ });
+
+ # bug->choices loads a lot of data that we want to lazy-load
+ # just load the status and resolutions and perform extra checks here
+ # upstream does these checks in the bug/fields template
+ my $perms = $bug->user;
+ my @resolutions;
+ foreach my $r (@{ Bugzilla::Field->new({ name => 'resolution', cache => 1 })->legal_values }) {
+ my $resolution = $r->name;
+ next unless $resolution;
+ next unless $r->is_active || $resolution eq $bug->resolution;
+
+ if ($perms->{canconfirm}
+ && !($perms->{canedit} || $perms->{isreporter}))
+ {
+ next if
+ $resolution ne 'WORKSFORME'
+ && $resolution ne 'INCOMPLETE'
+ && $resolution ne 'DUPLICATE';
+ }
+ if ($perms->{isreporter}
+ && !($perms->{canconfirm} || $perms->{canedit}))
+ {
+ next if $resolution eq 'INCOMPLETE';
+ }
+ next if $resolution eq 'EXPIRED';
+
+ push @resolutions, $r;
+ }
+ $bug->{choices} = {
+ bug_status => [
+ grep { $_->is_active || $_->name eq $bug->bug_status }
+ @{ $bug->statuses_available }
+ ],
+ resolution => \@resolutions,
+ };
+
+ # group tracking flags by version to allow for a better tabular output
+ my @tracking_table;
+ my $tracking_flags = $vars->{tracking_flags};
+ foreach my $flag (@$tracking_flags) {
+ my $flag_type = $flag->flag_type;
+ my $type = 'status';
+ my $name = $flag->description;
+ if ($flag_type eq 'tracking' && $name =~ /^(tracking|status)-(.+)/) {
+ ($type, $name) = ($1, $2);
+ }
+
+ my ($existing) = grep { $_->{type} eq $flag_type && $_->{name} eq $name } @tracking_table;
+ if ($existing) {
+ $existing->{$type} = $flag;
+ }
+ else {
+ push @tracking_table, {
+ $type => $flag,
+ name => $name,
+ type => $flag_type,
+ };
+ }
+ }
+ $vars->{tracking_flags_table} = \@tracking_table;
+}
+
+sub webservice {
+ my ($self, $args) = @_;
+ my $dispatch = $args->{dispatch};
+ $dispatch->{bug_modal} = 'Bugzilla::Extension::BugModal::WebService';
+}
+
+sub install_before_final_checks {
+ my ($self, $args) = @_;
+ add_setting('ui_experiments', ['on', 'off'], 'off');
+}
+
+__PACKAGE__->NAME;
diff --git a/extensions/BugModal/lib/ActivityStream.pm b/extensions/BugModal/lib/ActivityStream.pm
new file mode 100644
index 000000000..dae6b8ba2
--- /dev/null
+++ b/extensions/BugModal/lib/ActivityStream.pm
@@ -0,0 +1,284 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+package Bugzilla::Extension::BugModal::ActivityStream;
+1;
+
+package Bugzilla::Bug;
+use strict;
+use warnings;
+
+use Bugzilla::User;
+use Bugzilla::Constants;
+use Time::Local;
+
+# returns an arrayref containing all changes to the bug - comments, field
+# changes, and duplicates
+# [
+# {
+# time => $unix_timestamp,
+# user_id => actor user-id
+# comment => optional, comment added
+# id => unique identifier for this change-set
+# activty => [
+# {
+# who => user object
+# when => time (string)
+# changes => [
+# {
+# fieldname => field name :)
+# added => string
+# removed => string
+# }
+# ...
+# ]
+# }
+# ...
+# ]
+# },
+# ...
+# ]
+
+sub activity_stream {
+ my ($self) = @_;
+ if (!$self->{activity_stream}) {
+ my $stream = [];
+ _add_comments_to_stream($self, $stream);
+ _add_activities_to_stream($self, $stream);
+ _add_duplicates_to_stream($self, $stream);
+
+ my $base_time = _sql_date_to_time($self->creation_ts);
+ foreach my $change_set (@$stream) {
+ $change_set->{id} = $change_set->{comment}
+ ? 'c' . $change_set->{comment}->count
+ : 'a' . ($change_set->{time} - $base_time) . '.' . $change_set->{user_id};
+ $change_set->{activity} = [
+ sort { $a->{fieldname} cmp $b->{fieldname} }
+ @{ $change_set->{activity} }
+ ];
+ }
+ $self->{activity_stream} = [ sort { $a->{time} <=> $b->{time} } @$stream ];
+ }
+ return $self->{activity_stream};
+}
+
+# comments are processed first, so there's no need to merge into existing entries
+sub _add_comment_to_stream {
+ my ($stream, $time, $user_id, $comment) = @_;
+ push @$stream, {
+ time => $time,
+ user_id => $user_id,
+ comment => $comment,
+ activity => [],
+ };
+}
+
+sub _add_activity_to_stream {
+ my ($stream, $time, $user_id, $data) = @_;
+ foreach my $entry (@$stream) {
+ next unless $entry->{time} == $time && $entry->{user_id} == $user_id;
+ push @{ $entry->{activity} }, $data;
+ return;
+ }
+ push @$stream, {
+ time => $time,
+ user_id => $user_id,
+ comment => undef,
+ activity => [ $data ],
+ };
+}
+
+sub _add_comments_to_stream {
+ my ($bug, $stream) = @_;
+ my $user = Bugzilla->user;
+
+ my $raw_comments = $bug->comments();
+ foreach my $comment (@$raw_comments) {
+ next if $comment->type == CMT_HAS_DUPE;
+ next if $comment->is_private && !($user->is_insider || $user->id == $comment->author->id);
+ next if $comment->body eq '' && ($comment->work_time - 0) != 0 && !$user->is_timetracker;
+ _add_comment_to_stream($stream, _sql_date_to_time($comment->creation_ts), $comment->author->id, $comment);
+ }
+}
+
+sub _add_activities_to_stream {
+ my ($bug, $stream) = @_;
+ my $dbh = Bugzilla->dbh;
+ my $user = Bugzilla->user;
+
+ # build bug activity
+ my ($raw_activity) = $bug->can('get_activity')
+ ? $bug->get_activity()
+ : Bugzilla::Bug::GetBugActivity($bug->id);
+
+ # allow other extensions to alter history
+ Bugzilla::Hook::process('inline_history_activitiy', { activity => $raw_activity });
+
+ my %attachment_cache;
+ foreach my $attachment (@{$bug->attachments}) {
+ $attachment_cache{$attachment->id} = $attachment;
+ }
+
+ # build a list of bugs we need to check visibility of, so we can check with a single query
+ my %visible_bug_ids;
+
+ # envelope, augment and tweak
+ foreach my $operation (@$raw_activity) {
+ # until we can toggle their visibility, skip CC changes
+ $operation->{changes} = [ grep { $_->{fieldname} ne 'cc' } @{ $operation->{changes} } ];
+ next unless @{ $operation->{changes} };
+
+ # make operation.who an object
+ $operation->{who} = Bugzilla::User->new({ name => $operation->{who}, cache => 1 });
+
+ for (my $i = 0; $i < scalar(@{$operation->{changes}}); $i++) {
+ my $change = $operation->{changes}->[$i];
+
+ # make an attachment object
+ if ($change->{attachid}) {
+ $change->{attach} = $attachment_cache{$change->{attachid}};
+ }
+
+ # empty resolutions are displayed as --- by default
+ # make it explicit here to enable correct display of the change
+ if ($change->{fieldname} eq 'resolution') {
+ $change->{removed} = '---' if $change->{removed} eq '';
+ $change->{added} = '---' if $change->{added} eq '';
+ }
+
+ # make boolean fields true/false instead of 1/0
+ my ($table, $field) = ('bugs', $change->{fieldname});
+ if ($field =~ /^([^\.]+)\.(.+)$/) {
+ ($table, $field) = ($1, $2);
+ }
+ my $column = $dbh->bz_column_info($table, $field);
+ if ($column && $column->{TYPE} eq 'BOOLEAN') {
+ $change->{removed} = '';
+ $change->{added} = $change->{added} ? 'true' : 'false';
+ }
+
+ # load field object (only required for custom fields), and set the
+ # field type for custom fields
+ my $field_obj;
+ if ($change->{fieldname} =~ /^cf_/) {
+ $field_obj = Bugzilla::Field->new({ name => $change->{fieldname}, cache => 1 });
+ $change->{fieldtype} = $field_obj->type;
+ }
+
+ # identify buglist changes
+ if ($change->{fieldname} eq 'blocked' ||
+ $change->{fieldname} eq 'dependson' ||
+ $change->{fieldname} eq 'dupe' ||
+ ($field_obj && $field_obj->type == FIELD_TYPE_BUG_ID)
+ ) {
+ $change->{buglist} = 1;
+ foreach my $what (qw(removed added)) {
+ my @buglist = split(/[\s,]+/, $change->{$what});
+ foreach my $id (@buglist) {
+ if ($id && $id =~ /^\d+$/) {
+ $visible_bug_ids{$id} = 1;
+ }
+ }
+ }
+ }
+
+ # split see-also
+ if ($change->{fieldname} eq 'see_also') {
+ my $url_base = correct_urlbase();
+ foreach my $f (qw( added removed )) {
+ my @values;
+ foreach my $value (split(/, /, $change->{$f})) {
+ my ($bug_id) = substr($value, 0, length($url_base)) eq $url_base
+ ? $value =~ /id=(\d+)$/
+ : undef;
+ push @values, {
+ url => $value,
+ bug_id => $bug_id,
+ };
+ }
+ $change->{$f} = \@values;
+ }
+ }
+
+ # split multiple flag changes (must be processed last)
+ if ($change->{fieldname} eq 'flagtypes.name') {
+ my @added = split(/, /, $change->{added});
+ my @removed = split(/, /, $change->{removed});
+ next if scalar(@added) <= 1 && scalar(@removed) <= 1;
+ # remove current change
+ splice(@{$operation->{changes}}, $i, 1);
+ # restructure into added/removed for each flag
+ my %flags;
+ foreach my $added (@added) {
+ my ($value, $name) = $added =~ /^((.+).)$/;
+ $flags{$name}{added} = $value;
+ $flags{$name}{removed} |= '';
+ }
+ foreach my $removed (@removed) {
+ my ($value, $name) = $removed =~ /^((.+).)$/;
+ $flags{$name}{added} |= '';
+ $flags{$name}{removed} = $value;
+ }
+ # clone current change, modify and insert
+ foreach my $flag (sort keys %flags) {
+ my $flag_change = {};
+ foreach my $key (keys %$change) {
+ $flag_change->{$key} = $change->{$key};
+ }
+ $flag_change->{removed} = $flags{$flag}{removed};
+ $flag_change->{added} = $flags{$flag}{added};
+ splice(@{$operation->{changes}}, $i, 0, $flag_change);
+ }
+ $i--;
+ }
+ }
+
+ _add_activity_to_stream($stream, _sql_date_to_time($operation->{when}), $operation->{who}->id, $operation);
+ }
+
+ # prime the visible-bugs cache
+ $user->visible_bugs([keys %visible_bug_ids]);
+}
+
+# display 'duplicate of this bug' as an activity entry, not a comment
+sub _add_duplicates_to_stream {
+ my ($bug, $stream) = @_;
+ my $dbh = Bugzilla->dbh;
+
+ my $sth = $dbh->prepare("
+ SELECT longdescs.who,
+ UNIX_TIMESTAMP(bug_when), " .
+ $dbh->sql_date_format('bug_when') . ",
+ extra_data
+ FROM longdescs
+ INNER JOIN profiles ON profiles.userid = longdescs.who
+ WHERE bug_id = ? AND type = ?
+ ORDER BY bug_when
+ ");
+ $sth->execute($bug->id, CMT_HAS_DUPE);
+
+ while (my($who, $time, $when, $dupe_id) = $sth->fetchrow_array) {
+ _add_activity_to_stream($stream, $time, $who, {
+ who => Bugzilla::User->new({ id => $who, cache => 1 }),
+ when => $when,
+ changes => [{
+ fieldname => 'duplicate',
+ added => $dupe_id,
+ buglist => 1,
+ }],
+ });
+ }
+}
+
+sub _sql_date_to_time {
+ my ($date) = @_;
+ $date =~ /^(\d{4})[\.\-](\d{2})[\.\-](\d{2}) (\d{2}):(\d{2}):(\d{2})$/
+ or die "internal error: invalid date '$date'";
+ return timelocal($6, $5, $4, $3, $2 - 1, $1 - 1900);
+}
+
+1;
diff --git a/extensions/BugModal/lib/MonkeyPatches.pm b/extensions/BugModal/lib/MonkeyPatches.pm
new file mode 100644
index 000000000..1d902b2a9
--- /dev/null
+++ b/extensions/BugModal/lib/MonkeyPatches.pm
@@ -0,0 +1,38 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+package Bugzilla::Extension::BugModal::MonkeyPatches;
+1;
+
+package Bugzilla::Bug;
+use strict;
+use warnings;
+
+use Bugzilla::Attachment;
+
+sub active_attachments {
+ my ($self) = @_;
+ return [] if $self->{error};
+ return $self->{active_attachments} //= Bugzilla::Attachment->get_attachments_by_bug(
+ $self, { exclude_obsolete => 1, preload => 1 });
+}
+
+1;
+
+package Bugzilla::User;
+use strict;
+use warnings;
+
+sub moz_nick {
+ my ($self) = @_;
+ return $1 if $self->name =~ /:(.+?)\b/;
+ return $self->name if $self->name;
+ $self->login =~ /^([^\@]+)\@/;
+ return $1;
+}
+
+1;
diff --git a/extensions/BugModal/lib/WebService.pm b/extensions/BugModal/lib/WebService.pm
new file mode 100644
index 000000000..4c8b6b001
--- /dev/null
+++ b/extensions/BugModal/lib/WebService.pm
@@ -0,0 +1,138 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+package Bugzilla::Extension::BugModal::WebService;
+use strict;
+use warnings;
+
+use base qw(Bugzilla::WebService);
+
+use Bugzilla::Bug;
+use Bugzilla::Constants;
+use Bugzilla::Error;
+use Bugzilla::Field;
+use Bugzilla::Keyword;
+use Bugzilla::Milestone;
+use Bugzilla::Version;
+
+# these methods are much lighter than our public API calls
+
+sub rest_resources {
+ return [
+ # return all the lazy-loaded data; kept in sync with the UI's
+ # requirements.
+ qr{^/bug_modal/edit/(\d+)$}, {
+ GET => {
+ method => 'edit',
+ params => sub {
+ return { id => $_[0] }
+ },
+ },
+ },
+ # returns pre-formatted html, enabling reuse of the user template
+ qr{^/bug_modal/cc/(\d+)$}, {
+ GET => {
+ method => 'cc',
+ params => sub {
+ return { id => $_[0] }
+ },
+ },
+ },
+ ]
+}
+
+# everything we need for edit mode in a single call, returning just the fields
+# that the ui requires.
+sub edit {
+ my ($self, $params) = @_;
+ my $user = Bugzilla->user;
+ my $bug = Bugzilla::Bug->check({ id => $params->{id} });
+
+ # the keys of the options hash must match the field id in the ui
+ my %options;
+
+ my @products = @{ $user->get_enterable_products };
+ unless (grep { $_->id == $bug->product_id } @products) {
+ unshift @products, $bug->product_obj;
+ }
+ $options{product} = [ map { { name => $_->name, description => $_->description } } @products ];
+
+ $options{component} = _name_desc($bug->component, $bug->product_obj->components);
+ $options{version} = _name($bug->version, $bug->product_obj->versions);
+ $options{target_milestone} = _name($bug->target_milestone, $bug->product_obj->milestones);
+ $options{priority} = _name($bug->priority, 'priority');
+ $options{bug_severity} = _name($bug->bug_severity, 'bug_severity');
+ $options{rep_platform} = _name($bug->rep_platform, 'rep_platform');
+ $options{op_sys} = _name($bug->op_sys, 'op_sys');
+
+ # custom select fields
+ my @custom_fields =
+ grep { $_->type == FIELD_TYPE_SINGLE_SELECT || $_->type == FIELD_TYPE_MULTI_SELECT }
+ Bugzilla->active_custom_fields({ product => $bug->product_obj, component => $bug->component_obj });
+ foreach my $field (@custom_fields) {
+ my $field_name = $field->name;
+ $options{$field_name} = [
+ map { { name => $_->name } }
+ grep { $bug->$field_name eq $_->name || $_->is_active }
+ @{ $field->legal_values }
+ ];
+ }
+
+ # keywords
+ my @keywords = Bugzilla::Keyword->get_all();
+
+ # results
+ return {
+ options => \%options,
+ keywords => [ map { $_->name } @keywords ],
+ };
+}
+
+sub _name {
+ my ($current, $values) = @_;
+ # values can either be an array-ref of values, or a field name, which
+ # result in that field's legal-values being used.
+ if (!ref($values)) {
+ $values = Bugzilla::Field->new({ name => $values, cache => 1 })->legal_values;
+ }
+ return [
+ map { { name => $_->name } }
+ grep { $_->name eq $current || $_->is_active }
+ @$values
+ ];
+}
+
+sub _name_desc {
+ my ($current, $values) = @_;
+ if (!ref($values)) {
+ $values = Bugzilla::Field->new({ name => $values, cache => 1 })->legal_values;
+ }
+ return [
+ map { { name => $_->name, description => $_->description } }
+ grep { $_->name eq $current || $_->is_active }
+ @$values
+ ];
+}
+
+sub cc {
+ my ($self, $params) = @_;
+ my $template = Bugzilla->template;
+ my $bug = Bugzilla::Bug->check({ id => $params->{id} });
+ my $vars = {
+ cc_list => [
+ sort { lc($a->moz_nick) cmp lc($b->moz_nick) }
+ @{ $bug->cc_users }
+ ]
+ };
+
+ my $html = '';
+ $template->process('bug_modal/cc_list.html.tmpl', $vars, \$html)
+ || ThrowTemplateError($template->error);
+ return { html => $html };
+}
+
+1;
diff --git a/extensions/BugModal/template/en/default/bug/show-modal.html.tmpl b/extensions/BugModal/template/en/default/bug/show-modal.html.tmpl
new file mode 100644
index 000000000..f49b87435
--- /dev/null
+++ b/extensions/BugModal/template/en/default/bug/show-modal.html.tmpl
@@ -0,0 +1,16 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[% PROCESS global/variables.none.tmpl %]
+[% IF !header_done %]
+ [% PROCESS bug_modal/header.html.tmpl %]
+ [% PROCESS global/header.html.tmpl %]
+ [% header_done = 1 %]
+[% END %]
+[% INCLUDE bug_modal/edit.html.tmpl %]
+[% PROCESS global/footer.html.tmpl %]
diff --git a/extensions/BugModal/template/en/default/bug_modal/activity_stream.html.tmpl b/extensions/BugModal/template/en/default/bug_modal/activity_stream.html.tmpl
new file mode 100644
index 000000000..881ed927f
--- /dev/null
+++ b/extensions/BugModal/template/en/default/bug_modal/activity_stream.html.tmpl
@@ -0,0 +1,284 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[%
+ FOREACH change_set IN bug.activity_stream;
+ '<div class="change-set" id="' _ change_set.id _ '">';
+
+ extra_class = "";
+ IF change_set.user_id == bug.assigned_to.id;
+ extra_class = "assignee";
+ ELSIF change_set.user_id == bug.reporter.id;
+ extra_class = "reporter";
+ END;
+
+ IF change_set.comment;
+ PROCESS comment_header comment=change_set.comment;
+ ELSE;
+ PROCESS activity_header activities=change_set.activity id=change_set.id;
+ END;
+
+ IF change_set.comment;
+ PROCESS comment_body comment=change_set.comment;
+ END;
+ FOREACH activity IN change_set.activity;
+ PROCESS activity_body activity=activity;
+ END;
+
+ '</div>';
+ END;
+%]
+
+[% BLOCK comment_header %]
+ <div class="comment">
+ [%# normal comment header %]
+ <table class="layout-table change-head [% extra_class FILTER none %]" id="ch-[% comment.count FILTER none %]"
+ [% IF comment.collapsed +%] style="display:none"[% END %]>
+ <tr>
+ <td rowspan="2" class="change-gravatar">
+ [% INCLUDE bug_modal/user.html.tmpl
+ u = comment.author
+ gravatar_size = 32
+ gravatar_only = 1
+ %]
+ </td>
+ <td class="change-author">
+ [% INCLUDE bug_modal/user.html.tmpl
+ u = comment.author
+ %]
+ [% IF extra_class %]
+ <span class="user-role">([% extra_class.ucfirst FILTER none %])</span>
+ [% END %]
+ [% Hook.process('user', 'bug/comments.html.tmpl') %]
+ </td>
+ <td class="comment-actions">
+ [% IF user.is_insider && bug.check_can_change_field('longdesc', 0, 1) %]
+ [% IF comment.is_private %]
+ <div class="comment-private edit-hide bz_private">
+ Private
+ </div>
+ [% END %]
+ <div class="comment-private edit-show" style="display:none">
+ <input type="hidden" value="1" name="defined_isprivate_[% comment.id FILTER none %]">
+ <input type="checkbox" name="isprivate_[% comment.id FILTER none %]"
+ id="is-private-[% comment.id FILTER none %]"
+ class="is-private" value="1" [%= "checked" IF comment.is_private %]>
+ <label for="is-private-[% comment.id FILTER none %]">Private</label>
+ </div>
+ [% END %]
+ [% IF user.id %]
+ <button class="reply-btn in-page"
+ data-reply-id="[% comment.count FILTER none %]"
+ data-reply-name="[% comment.author.name || comment.author.moz_nick FILTER html %]"
+ >Reply</button>
+ [% END %]
+ <button class="comment-spinner in-page" id="cs-[% comment.count FILTER none%]">-</button>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="change-name">
+ <a href="show_bug.cgi?id=[% bug.bug_id FILTER none %]#c[% comment.count FILTER none %]">
+ [% comment.count == 0 ? "Description" : "Comment " _ comment.count ~%]
+ </a>
+ </div>
+ &bull;
+ <div class="change-time">
+ [% INCLUDE bug_modal/rel_time.html.tmpl ts=comment.creation_ts %]
+ </div>
+ </td>
+ </tr>
+ [% IF comment.tags.size %]
+ <tr>
+ <td colspan="2" class="comment-tags">
+ [% FOREACH tag IN comment.tags %]
+ <span class="comment-tag">[% tag FILTER html %]</span>
+ [% END %]
+ </td>
+ </tr>
+ [% END %]
+ </table>
+
+ [%# default-collapsed comment header %]
+ [% IF comment.collapsed %]
+ <table class="layout-table change-head default-collapsed" id="cc-[% comment.count FILTER none %]">
+ <tr>
+ <td class="comment-collapse-reason"
+ title="[% comment.author.moz_nick FILTER html %] [[% comment.creation_ts FILTER time_duration FILTER html %]]">
+ Comment hidden ([% comment.tags.join(', ') FILTER html %])
+ </td>
+ <td class="comment-actions">
+ <button class="comment-spinner in-page" id="ccs-[% comment.count FILTER none%]">
+ [%~ comment.collapsed ? "+" : "-" ~%]
+ </button>
+ </td>
+ </tr>
+ </table>
+ [% END %]
+ </div>
+[% END %]
+
+[% BLOCK activity_header %]
+ [% action = activities.0 %]
+ <div id="[% id FILTER none %]" class="change">
+ <table class="layout-table change-head [% extra_class FILTER none %]">
+ <tr>
+ <td rowspan="2" class="change-gravatar">
+ [% INCLUDE bug_modal/user.html.tmpl
+ u = action.who
+ gravatar_size = 32
+ gravatar_only = 1
+ %]
+ </td>
+ <td class="change-author">
+ [% INCLUDE bug_modal/user.html.tmpl
+ u = action.who
+ %]
+ [% IF extra_class %]
+ <span class="user-role">([% extra_class.ucfirst FILTER none %])</span>
+ [% END %]
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <div class="change-name">
+ <a href="show_bug.cgi?id=[% bug.bug_id FILTER none %]#[% id FILTER none %]">Updated</a>
+ </div>
+ &bull;
+ <div class="change-time">
+ [% INCLUDE bug_modal/rel_time.html.tmpl ts=action.when %]
+ </div>
+ </td>
+ </tr>
+ </table>
+ </div>
+[% END %]
+
+[% BLOCK comment_body %]
+ <pre class="comment-text [%= "bz_private" IF comment.is_private %]" id="ct-[% comment.count FILTER none %]"
+ [% IF comment.collapsed +%] style="display:none"[% END ~%]
+ >[% comment.body_full FILTER quoteUrls(bug, comment) %]</pre>
+[% END %]
+
+[%
+ BLOCK activity_body;
+ '<div class="activity">';
+ has_cc = 0;
+
+ FOREACH change IN activity.changes;
+ '<div class="change">';
+ class = "";
+
+ IF change.fieldname == 'cc';
+ has_cc = 1;
+ class = "activity-cc";
+ END;
+
+ IF change.attachid;
+ %]
+ <a href="attachment.cgi?id=[% change.attachid FILTER none %]&amp;action=edit"
+ title="[% change.attach.description FILTER html %]"
+ class="[% "bz_obsolete" IF change.attach.isobsoletee %]"
+ >Attachment #[% change.attachid FILTER none %]</a> -
+ [%+
+ END;
+
+ IF change.buglist;
+ IF change.fieldname == 'duplicate';
+ label = "Duplicate of this " _ terms.bug;
+ ELSE;
+ label = field_descs.${change.fieldname};
+ END;
+ IF change.added != '';
+ label _ ": " FILTER html;
+ PROCESS add_change value=change.added;
+ END;
+ IF change.removed != '';
+ IF change.added != '';
+ "<br>";
+ END;
+ "No longer ";
+ label FILTER lcfirst;
+ ": ";
+ PROCESS add_change value=change.removed;
+ END;
+
+ ELSE;
+ IF change.fieldname == 'longdescs.isprivate';
+ # reference the comment that was made private/public in the field label
+ %]
+ <a href="#c[% change.comment.count FILTER none %]">
+ Comment [% change.comment.count FILTER none %]</a> is private:
+ [%+
+ ELSE;
+ field_descs.${change.fieldname} _ ": " FILTER html;
+ END;
+
+ IF change.removed != '';
+ IF change.added == '';
+ '<span class="activity-deleted">';
+ END;
+ PROCESS add_change value=change.removed;
+ IF change.added == '';
+ '</span>';
+ ELSE;
+ ' &rarr; ';
+ END;
+ END;
+ PROCESS add_change value=change.added;
+ END;
+
+ '</div>';
+ END;
+ '</div>';
+ END;
+
+ BLOCK add_change;
+ SWITCH change.fieldname;
+
+ CASE [ 'estimated_time', 'remaining_time', 'work_time' ];
+ PROCESS formattimeunit time_unit=value;
+
+ CASE 'bug_file_loc';
+ %]
+ <a href="[% value FILTER html %]" target="_blank" rel="noreferrer"
+ [% UNLESS is_safe_url(value) %]
+ onclick="return confirmUnsafeURL(this.href)"
+ [% END %]
+ >[% value FILTER truncate(256, '&hellip;') FILTER html %]</a>
+ [%
+
+ CASE 'see_also';
+ FOREACH see_also IN value;
+ IF see_also.bug_id;
+ "$terms.bug $see_also.bug_id" FILTER bug_link(see_also.bug_id);
+ ELSE;
+ %]
+ <a href="[% see_also.url FILTER html %]" target="_blank">[% see_also.url FILTER html %]</a>
+ [%
+ END;
+ ", " UNLESS loop.last;
+ END;
+
+ CASE [ 'assigned_to', 'reporter', 'qa_contact', 'cc', 'flagtypes.name' ];
+ value FILTER email;
+
+ CASE;
+ IF change.fieldtype == constants.FIELD_TYPE_DATETIME;
+ INCLUDE bug_modal/rel_time.html.tmpl ts=value;
+
+ ELSIF change.buglist;
+ value FILTER bug_list_link;
+
+ ELSE;
+ value FILTER truncate(256, '&hellip;') FILTER html;
+
+ END;
+ END;
+ END;
+%]
diff --git a/extensions/BugModal/template/en/default/bug_modal/attachments.html.tmpl b/extensions/BugModal/template/en/default/bug_modal/attachments.html.tmpl
new file mode 100644
index 000000000..3055cc861
--- /dev/null
+++ b/extensions/BugModal/template/en/default/bug_modal/attachments.html.tmpl
@@ -0,0 +1,60 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[%#
+ # bug: (bug object) the main bug object
+ #%]
+
+<table class="layout-table" id="attachments">
+ [% FOREACH attachment IN bug.attachments %]
+ [% NEXT IF attachment.isprivate && !(user.is_insider || attachment.attacher.id == user.id) %]
+ <tr class="
+ [%~ " bz_private" IF attachment.isprivate %]
+ [%~ " attach-obsolete" IF attachment.isobsolete %]
+ [%~ " attach-patch" IF attachment.ispatch %]
+ " [% IF attachment.isobsolete %]style="display:none"[% END %]>
+ <td class="attach-desc-td">
+ <div class="attach-desc">
+ <a href="attachment.cgi?id=[% attachment.id FILTER none %]">
+ [%~ attachment.description FILTER html %]</a>
+ </div>
+ <div>
+ <span class="attach-time">[% INCLUDE bug_modal/rel_time.html.tmpl ts=attachment.attached %]</span>
+ <span class="attach-author">[% INCLUDE bug_modal/user.html.tmpl u=attachment.attacher %]</span>
+ </div>
+ <div class="attach-info">
+ [% IF attachment.datasize %]
+ [%- attachment.datasize FILTER unitconvert %]
+ [% ELSE %]
+ (deleted)
+ [% END %],
+ [%+ attachment.ispatch ? "patch" : attachment.contenttype FILTER html -%]
+ </div>
+ </td>
+ <td>
+ [% FOREACH flag IN attachment.flags %]
+ <div class="attach-flag">
+ [% INCLUDE bug_modal/user.html.tmpl u=flag.setter simple=1 %]:
+ <span class="flag-name-status">
+ [%+ flag.type.name FILTER html %][% flag.status FILTER none %]
+ </span>
+ [% IF flag.requestee %]
+ [%+ INCLUDE bug_modal/user.html.tmpl u=flag.requestee simple=1 %]
+ [% END %]
+ </div>
+ [% END %]
+ </td>
+ <td class="attach-actions">
+ <a href="attachment.cgi?id=[% attachment.id FILTER none %]&amp;action=edit">Details</a>
+ [% IF attachment.ispatch %]
+ | <a href="attachment.cgi?id=[% attachment.id FILTER none %]&amp;action=diff">Diff</a>
+ [% END %]
+ [% Hook.process("action", "attachment/list.html.tmpl") %]
+ </tr>
+ [% END %]
+</table>
diff --git a/extensions/BugModal/template/en/default/bug_modal/cc_list.html.tmpl b/extensions/BugModal/template/en/default/bug_modal/cc_list.html.tmpl
new file mode 100644
index 000000000..37f582e0e
--- /dev/null
+++ b/extensions/BugModal/template/en/default/bug_modal/cc_list.html.tmpl
@@ -0,0 +1,16 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[%
+ UNLESS cc_list.size;
+ 'None';
+ END;
+ FOREACH cc IN cc_list;
+ INCLUDE bug_modal/user.html.tmpl u=cc;
+ END;
+%]
diff --git a/extensions/BugModal/template/en/default/bug_modal/edit.html.tmpl b/extensions/BugModal/template/en/default/bug_modal/edit.html.tmpl
new file mode 100644
index 000000000..a16bfdd9b
--- /dev/null
+++ b/extensions/BugModal/template/en/default/bug_modal/edit.html.tmpl
@@ -0,0 +1,918 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[%
+ USE Bugzilla;
+
+ # only edit one bug
+ UNLESS bug.defined;
+ bug = bugs.0;
+ END;
+ bugid = bug.id;
+
+ # this is used in a few places
+ is_cced = bug.cc.contains(user.login);
+
+ # custom fields that have custom rendering, or should not be rendered
+ rendered_custom_fields = [
+ 'cf_user_story',
+ 'cf_last_resolved',
+ ];
+
+ # all custom fields
+ custom_fields = Bugzilla.active_custom_fields(product => bug.product_obj, component => bug.component_obj, bug_id => bug.id);
+
+ # extract needinfo flags
+ needinfo = [];
+ FOREACH flag_type IN bug.flag_types;
+ IF flag_type.name == 'needinfo';
+ needinfo_flag_type = flag_type;
+ FOREACH flag IN flag_type.flags;
+ IF flag.status == '?';
+ needinfo.push(flag);
+ END;
+ END;
+ END;
+ END;
+
+ # count attachments
+ active_attachments = 0;
+ obsolete_attachments = 0;
+ FOREACH attachment IN bug.attachments;
+ NEXT IF attachment.isprivate && !(user.is_insider || attachment.attacher.id == user.id);
+ IF attachment.isobsolete;
+ obsolete_attachments = obsolete_attachments + 1;
+ ELSE;
+ active_attachments = active_attachments + 1;
+ END;
+ END;
+
+ # count set bug flags (excluding needinfo)
+ has_bug_flags = 0;
+ FOREACH flag IN bug.flags;
+ NEXT IF flag.name == 'needinfo';
+ has_bug_flags = 1;
+ LAST;
+ END;
+
+ # count set project/tracking flags
+ set_project_flags = [];
+ set_tracking_flags = [];
+ FOREACH flag IN tracking_flags;
+ NEXT IF flag.bug_flag(bug.id).value == "---";
+ IF flag.flag_type == "project";
+ set_project_flags.push(flag);
+ END;
+ IF flag.flag_type == "tracking";
+ set_tracking_flags.push(flag);
+ END;
+ END;
+
+ # build firefox flags subtitle
+ firefox_flags = [];
+ firefox_fixed_version = "";
+ tracking_flags_title = "Firefox Tracking Flags";
+ # project flags
+ FOREACH row IN tracking_flags_table;
+ NEXT UNLESS row.type == "project";
+ status_value = row.status.bug_flag(bug.id).value;
+ NEXT IF status_value == "---";
+ firefox_flags.push(row.name _ ":" _ status_value);
+ END;
+ # tracking flags title and subtitle
+ FOREACH row IN tracking_flags_table;
+ NEXT UNLESS row.type == "tracking";
+ tracking_value = row.tracking ? row.tracking.bug_flag(bug.id).value : "---";
+ status_value = row.status.bug_flag(bug.id).value || "---";
+ NEXT IF tracking_value == "---" && status_value == "---";
+ blurb = row.name;
+ IF tracking_value != "---";
+ blurb = blurb _ tracking_value;
+ END;
+ IF status_value != "---";
+ blurb = blurb _ " " _ status_value;
+ IF firefox_fixed_version == "" && status_value == "fixed";
+ firefox_fixed_version = row.name.ucfirst.replace('^(\D+)(\d)', '$1 $2');
+ END;
+ END;
+ firefox_flags.push(blurb);
+ IF row.name.search("^thunderbird");
+ tracking_flags_title = "Thunderbird Tracking Flags";
+ ELSIF row.name.search("^seamonkey");
+ tracking_flags_title = "SeaMonkey Tracking Flags";
+ END;
+ END;
+ IF firefox_flags.size;
+ firefox_flags_subtitle = firefox_flags.join(", ");
+ ELSE;
+ firefox_flags_subtitle = "Not tracked";
+ END;
+%]
+
+[% IF user.id %]
+ <form name="changeform" id="changeform" method="post" action="process_bug.cgi">
+ <input type="hidden" name="delta_ts" value="[% bug.delta_ts FILTER html %]">
+ <input type="hidden" name="longdesclength" value="[% bug.comments.size FILTER html %]">
+ <input type="hidden" name="id" id="bug_id" value="[% bug.bug_id FILTER html %]">
+ <input type="hidden" name="token" value="[% issue_hash_token([bug.id, bug.delta_ts]) FILTER html %]">
+[% END %]
+
+[%# === header === %]
+
+<div id="xhr-error" style="display:none"></div>
+
+[% WRAPPER bug_modal/module.html.tmpl
+ title = ""
+%]
+ <div id="summary-container">
+ [%# bug id, alias, and summary %]
+ [% WRAPPER bug_modal/field.html.tmpl
+ container = 1
+ no_label = 1
+ view_only = 1
+ %]
+ <div id="field-value-bug_id">
+ <a id="this-bug" href="show_bug.cgi?id=[% bug.id FILTER none %]
+ [%~ '&amp;format=' _ cgi.param("format") IF cgi.param("format") %]"
+ >
+ [%~ terms.Bug _ " " _ bug.id FILTER none ~%]
+ </a>
+ [% IF bug.alias %]
+ <span class="edit-hide">
+ ([% bug.alias FILTER html %])
+ </span>
+ [% END %]
+ </div>
+ [% END %]
+ [% WRAPPER bug_modal/field.html.tmpl
+ container = 1
+ no_label = 1
+ hide_on_edit = 1
+ %]
+ <div id="field-value-short_desc">[% bug.short_desc FILTER html %]</div>
+ [% END %]
+
+ [%# alias %]
+ [% INCLUDE bug_modal/field.html.tmpl
+ field = bug_fields.alias
+ field_type = constants.FIELD_TYPE_FREETEXT
+ hide_on_view = 1
+ short_width = 1
+ %]
+
+ [%# summary %]
+ [% INCLUDE bug_modal/field.html.tmpl
+ field = bug_fields.short_desc
+ field_type = constants.FIELD_TYPE_FREETEXT
+ hide_on_view = 1
+ %]
+
+ [%# status summary %]
+ [% WRAPPER bug_modal/field.html.tmpl
+ name = "status_summary"
+ no_label = 1
+ hide_on_edit = 1
+ %]
+ <b>
+ [% bug.bug_status FILTER html %]
+ [%+ bug.resolution FILTER html IF bug.resolution %]
+ </b>
+ [% IF bug.resolution == "FIXED"
+ && bug.target_milestone
+ && bug.target_milestone != "---"
+ %]
+ in [% firefox_fixed_version || bug.target_milestone FILTER html %]
+ [% ELSIF bug.dup_id %]
+ of [% terms.bug _ " $bug.dup_id" FILTER bug_link(bug.dup_id) FILTER none %]
+ [% END %]
+ [% IF needinfo.size %]
+ <div id="status-needinfo">
+ (NeedInfo from
+ [%+
+ IF needinfo.size == 1;
+ INCLUDE bug_modal/user.html.tmpl u=needinfo.0.requestee nick_only=1;
+ ELSE;
+ " " _ needinfo.size _ " people";
+ END;
+ ~%]
+ )
+ </div>
+ [% END %]
+ [% END %]
+ </div>
+
+ [%# buttons %]
+
+ <div id="mode-container">
+ [% IF user.id %]
+ <div>
+ <button type="button" class="minor" id="cc-btn" data-is-cced="[% is_cced ? 1 : 0 %]">
+ [% is_cced ? "Stop following" : "Follow" %]
+ </button>
+ <button type="button" id="cancel-btn" class="minor" style="display:none">Cancel</button>
+ <button type="button" id="mode-btn">
+ <span id="mode-btn-readonly">Edit</span>
+ <span id="mode-btn-loading">
+ <img id="edit-throbber" src="extensions/BugModal/web/throbber.gif" width="16" height="11">
+ Fetching
+ </span>
+ </button>
+ <button type="submit" id="commit-btn" style="display:none">Save Changes</button>
+ </div>
+ [% END %]
+ <div class="button-row">
+ [% IF user.id %]
+ <button type="button" class="comment-btn in-page">Add Comment</button>
+ [% END %]
+ <button type="button" id="last-comment-btn" class="in-page">Last Comment &darr;</button>
+ </div>
+ <div class="button-row">
+ [% IF bug.assigned_to.id == user.id || user.in_group("editbugs") %]
+ <button type="button" id="copy-summary" class="in-page"
+ title="Copy [% terms.bug %] number and summary to your clipboard">Copy Summary</button>
+ [% END %]
+ <button type="button" id="expand-all-btn" class="in-page">Expand All</button>
+ </div>
+ </div>
+[% END %]
+
+[%# === status === %]
+
+[% WRAPPER bug_modal/module.html.tmpl
+ title = "Status"
+%]
+ [% WRAPPER fields_lhs %]
+
+ [%# product %]
+ [% WRAPPER bug_modal/field.html.tmpl
+ field = bug_fields.product
+ field_type = constants.FIELD_TYPE_SINGLE_SELECT
+ %]
+ <div class="spin-toggle" data-latch="#product-latch" data-for="#product-info">
+ <span class="spin-latch" id="product-latch">&#9656;</span>
+ [% bug.product FILTER html %]
+ </div>
+ <div id="product-info" style="display:none">
+ [% bug.product_obj.description FILTER html_light %]
+ </div>
+ [% END %]
+
+ [%# component %]
+ [% WRAPPER bug_modal/field.html.tmpl
+ field = bug_fields.component
+ field_type = constants.FIELD_TYPE_SINGLE_SELECT
+ %]
+ <div class="spin-toggle" data-latch="#component-latch" data-for="#component-info">
+ <span class="spin-latch" id="component-latch">&#9656;</span>
+ [% bug.component FILTER html %]
+ </div>
+ <div id="component-info" style="display:none">
+ <div>[% bug.component_obj.description FILTER html_light %]</div>
+ <a href="buglist.cgi?component=[% bug.component FILTER uri %]&amp;
+ [%~ %]product=[% bug.product FILTER uri %]&amp;
+ [%~ %]bug_status=__open__" target="_blank">Other [% terms.Bugs %]</a>
+ </div>
+ [% END %]
+
+ [%# importance %]
+ [% WRAPPER bug_modal/field.html.tmpl
+ label = "Importance"
+ container = 1
+ hide_on_view = bug.priority == "--" && bug.bug_severity == "normal"
+ %]
+ [% INCLUDE bug_modal/field.html.tmpl
+ field = bug_fields.priority
+ field_type = constants.FIELD_TYPE_SINGLE_SELECT
+ no_indent = 1
+ inline = 1
+ %]
+ [% INCLUDE bug_modal/field.html.tmpl
+ field = bug_fields.bug_severity
+ field_type = constants.FIELD_TYPE_SINGLE_SELECT
+ inline = 1
+ %]
+ [% UNLESS cf_hidden_in_product('cf_rank', bug.product, bug.component) %]
+ [% rendered_custom_fields.push('cf_rank') %]
+ [% INCLUDE bug_modal/field.html.tmpl
+ field = bug_fields.cf_rank
+ field_type = constants.FIELD_TYPE_INTEGER
+ inline = 1
+ label = "Rank"
+ hide_on_view = bug.cf_rank == ""
+ %]
+ [% END %]
+ [% END %]
+
+ [%# status, resolution %]
+ [% IF bug.assigned_to.id != user.id %]
+ [% WRAPPER bug_modal/field.html.tmpl
+ name = "status-view"
+ container = 1
+ label = "Status"
+ hide_on_edit = 1
+ %]
+ [% bug.bug_status FILTER html %]
+ [%+ bug.resolution FILTER html IF bug.resolution %]
+ [% IF bug.dup_id %]
+ of [% terms.bug _ " $bug.dup_id" FILTER bug_link(bug.dup_id) FILTER none %]
+ [% END %]
+ [% END %]
+ [% END %]
+
+ [% END %]
+ [% WRAPPER fields_rhs %]
+
+ [%# creation time %]
+ [% WRAPPER bug_modal/field.html.tmpl
+ field = bug_fields.creation_ts
+ label = "Reported"
+ view_only = 1
+ %]
+ [% INCLUDE bug_modal/rel_time.html.tmpl ts=bug.creation_ts %]
+ [% END %]
+
+ [%# last modified %]
+ [% WRAPPER bug_modal/field.html.tmpl
+ field = bug_fields.delta_ts
+ label = "Modified"
+ view_only = 1
+ %]
+ [% INCLUDE bug_modal/rel_time.html.tmpl ts=bug.delta_ts %]
+ [% END %]
+
+ [% END %]
+
+ [%# status/resolution knob %]
+ [% WRAPPER bug_modal/field.html.tmpl
+ name = "status-edit"
+ container = 1
+ label = "Status"
+ hide_on_view = bug.assigned_to.id != user.id
+ %]
+ [% INCLUDE bug_modal/field.html.tmpl
+ field = bug_fields.bug_status
+ field_type = constants.FIELD_TYPE_SINGLE_SELECT
+ values = bug.choices.bug_status
+ inline = 1
+ no_indent = 1
+ edit_only = 1
+ %]
+ [% INCLUDE bug_modal/field.html.tmpl
+ field = bug_fields.resolution
+ field_type = constants.FIELD_TYPE_SINGLE_SELECT
+ values = bug.choices.resolution
+ inline = 1
+ edit_only = 1
+ %]
+ [% IF bug.choices.resolution.only("name", "DUPLICATE").size %]
+ <div id="duplicate-container">
+ of
+ <input id="dup_id" name="dup_id" size="6" value="[% bug.dup_id FILTER html %]">
+ </div>
+ <div id="duplicate-actions">
+ <button type="button" class="in-page" id="mark-as-dup-btn">
+ Mark as Duplicate
+ </button>
+ </div>
+ [% END %]
+ [% END %]
+[% END %]
+
+[%# === people === %]
+
+[%
+ unassigned = (bug.assigned_to.login == "nobody@mozilla.org")
+ || (bug.assigned_to.login.search("\.bugs$"));
+ sub =
+ "Reporter: " _ bug.reporter.moz_nick
+ _ (unassigned ? ", Unassigned" : ", Assigned: " _ bug.assigned_to.moz_nick)
+ _ (bug.mentors.size ? ", Mentored" : "")
+ _ (needinfo.size ? ", NeedInfo" : "")
+%]
+[% WRAPPER bug_modal/module.html.tmpl
+ title = "People"
+ subtitle = sub
+ collapsed = 1
+%]
+ [% WRAPPER fields_lhs %]
+
+ [%# assignee %]
+ [% WRAPPER bug_modal/field.html.tmpl
+ field = bug_fields.assigned_to
+ field_type = constants.FIELD_TYPE_USER
+ %]
+ [% IF unassigned %]
+ <i>Unassigned</i>
+ [% IF bug.check_can_change_field("assigned_to", 0, 1) %]
+ <button type="button" id="take-btn" class="in-page">Take</button>
+ [% END %]
+ [% ELSE %]
+ [% INCLUDE bug_modal/user.html.tmpl u=bug.assigned_to %]
+ [% END %]
+ [% END %]
+
+ [%# mentors %]
+ [% WRAPPER bug_modal/field.html.tmpl
+ field = bug_fields.bug_mentor
+ field_type = constants.FIELD_TYPE_USERS
+ label = "Mentors"
+ value = bug.mentors.pluck("login")
+ hide_on_view = bug.mentors.size == 0
+ %]
+ [%
+ FOREACH mentor IN bug.mentors;
+ INCLUDE bug_modal/user.html.tmpl u=mentor;
+ END;
+ %]
+ [% END %]
+
+ [%# qa contact %]
+ [% WRAPPER bug_modal/field.html.tmpl
+ field = bug_fields.qa_contact
+ field_type = constants.FIELD_TYPE_USER
+ hide_on_view = bug.qa_contact == ""
+ %]
+ [% INCLUDE bug_modal/user.html.tmpl u=bug.qa_contact %]
+ [% END %]
+
+ [% END %]
+ [% WRAPPER fields_rhs %]
+
+ [%# reporter %]
+ [% INCLUDE bug_modal/field.html.tmpl
+ field = bug_fields.reporter
+ field_type = constants.FIELD_TYPE_USER
+ view_only = 1
+ %]
+
+ [%# needinfo %]
+ [% WRAPPER bug_modal/field.html.tmpl
+ container = 1
+ label = "NeedInfo"
+ hide_on_view = needinfo.size == 0
+ hide_on_edit = 1
+ %]
+ [% INCLUDE bug_modal/flags.html.tmpl
+ types = bug.flag_types.only("name", "needinfo")
+ no_label = 1
+ view_only = 1
+ %]
+ [% END %]
+ [% IF needinfo.size %]
+ [% WRAPPER bug_modal/field.html.tmpl
+ container = 1
+ label = "NeedInfo"
+ hide_on_view = 1
+ %]
+ <button type="button" id="needinfo-scroll" class="in-page">Update</button>
+ [% END %]
+ [% END %]
+
+ [%# cc %]
+ [% WRAPPER bug_modal/field.html.tmpl
+ container = 1
+ label = "CC"
+ hide_on_view = bug.cc.size == 0
+ %]
+ [% IF bug.cc && bug.cc.size %]
+ <span id="cc-latch">&#9656;</span>
+ <span id="cc-summary">
+ [%
+ IF bug.cc.size == 1;
+ is_cced ? "Just you" : "1 person";
+ ELSE;
+ bug.cc.size _ " people";
+ END;
+ %]
+ </span>
+ <div id="cc-list" style="display:none"></div>
+ [% ELSE %]
+ <i>Nobody</i>
+ [% END %]
+ [% END %]
+
+ [% END %]
+[% END %]
+
+[%# === tracking === %]
+
+[%
+ col =
+ (bug.version.lower == "unspecified" || bug.version.lower == "other")
+ && bug.target_milestone == "---"
+ && !has_bug_flags
+ && !set_project_flags.size
+ && !set_tracking_flags.size;
+ sub = [];
+ IF col;
+ sub.push("Not tracked");
+ END;
+ open_deps = bug.depends_on_obj.only("resolution", "").size;
+ IF open_deps;
+ sub.push("Depends on: " _ open_deps _ " bug" _ (open_deps == 1 ? "" : "s"));
+ END;
+ open_deps = bug.blocks_obj.only("resolution", "").size;
+ IF open_deps;
+ sub.push("Blocks: " _ open_deps _ " bug" _ (open_deps == 1 ? "" : "s"));
+ END;
+%]
+[% WRAPPER bug_modal/module.html.tmpl
+ title = "Tracking"
+ subtitle = sub.join(", ")
+ collapsed = col
+%]
+ [% WRAPPER fields_lhs %]
+
+ [%# version %]
+ [% INCLUDE bug_modal/field.html.tmpl
+ field = bug_fields.version
+ field_type = constants.FIELD_TYPE_SINGLE_SELECT
+ %]
+
+ [%# milestone %]
+ [% INCLUDE bug_modal/field.html.tmpl
+ field = bug_fields.target_milestone
+ field_type = constants.FIELD_TYPE_SINGLE_SELECT
+ label = "Target"
+ %]
+
+ [%# platform, op-sys %]
+ [% WRAPPER bug_modal/field.html.tmpl
+ container = 1
+ label = "Platform"
+ hide_on_view = bug.rep_platform == 'All' && bug.op_sys == 'All'
+ %]
+ [% INCLUDE bug_modal/field.html.tmpl
+ field = bug_fields.rep_platform
+ field_type = constants.FIELD_TYPE_SINGLE_SELECT
+ inline = 1
+ no_indent = 1
+ %]
+ [% INCLUDE bug_modal/field.html.tmpl
+ field = bug_fields.op_sys
+ field_type = constants.FIELD_TYPE_SINGLE_SELECT
+ inline = 1
+ %]
+ [% END %]
+
+ [%# keywords %]
+ [% WRAPPER bug_modal/field.html.tmpl
+ field = bug_fields.keywords
+ field_type = constants.FIELD_TYPE_KEYWORDS
+ hide_on_view = bug.keyword_objects.size == 0
+ %]
+ [% bug.keyword_objects.pluck("name").join(", ") FILTER html %]
+ [% END %]
+
+ [% UNLESS cf_hidden_in_product('cf_fx_iteration', bug.product, bug.component) %]
+ [% rendered_custom_fields.push('cf_fx_iteration') %]
+ [% INCLUDE bug_modal/field.html.tmpl
+ field = bug_fields.cf_fx_iteration
+ field_type = bug_fields.cf_fx_iteration.type
+ hide_on_view = bug.cf_iteration == ""
+ %]
+ [% END %]
+
+ [% UNLESS cf_hidden_in_product('cf_fx_points', bug.product, bug.component) %]
+ [% rendered_custom_fields.push('cf_fx_points') %]
+ [% INCLUDE bug_modal/field.html.tmpl
+ field = bug_fields.cf_fx_points
+ field_type = bug_fields.cf_fx_points.type
+ hide_on_view = bug.cf_points == ""
+ %]
+ [% END %]
+
+ [% END %]
+ [% WRAPPER fields_rhs %]
+
+ [%# depends on %]
+ [% INCLUDE bug_modal/field.html.tmpl
+ field = bug_fields.dependson
+ field_type = constants.FIELD_TYPE_BUG_LIST
+ values = bug.depends_on_obj
+ hide_on_view = bug.dependson.size == 0
+ %]
+
+ [%# blocks %]
+ [% INCLUDE bug_modal/field.html.tmpl
+ field = bug_fields.blocked
+ field_type = constants.FIELD_TYPE_BUG_LIST
+ values = bug.blocks_obj
+ hide_on_view = bug.blocked.size == 0
+ %]
+
+ [% IF bug.dependson.size + bug.blocked.size > 1 %]
+ [% WRAPPER bug_modal/field.html.tmpl
+ container = 1
+ label = ""
+ hide_on_edit = 1
+ %]
+ Dependency <a href="showdependencytree.cgi?id=[% bug.bug_id FILTER none %]&amp;hide_resolved=1">tree</a>
+ / <a href="showdependencygraph.cgi?id=[% bug.bug_id FILTER none %]">graph</a>
+ [% END %]
+ [% END %]
+
+ [%# duplicates %]
+ [% IF bug.duplicates.size %]
+ [% INCLUDE bug_modal/field.html.tmpl
+ label = "Duplicates"
+ values = bug.duplicates
+ field_type = constants.FIELD_TYPE_BUG_LIST
+ view_only = 1
+ %]
+ [% END %]
+
+ [%# flags %]
+ [% WRAPPER bug_modal/field.html.tmpl
+ name = "bug_flags"
+ container = 1
+ label = terms.Bug _ " Flags"
+ hide_on_view = !has_bug_flags
+ %]
+ [% INCLUDE bug_modal/flags.html.tmpl
+ types = bug.flag_types.skip("name", "needinfo")
+ %]
+ [% END %]
+
+ [% END %]
+[% END %]
+
+[% IF tracking_flags.size %]
+
+ [%# === tracking flags === %]
+
+ [% WRAPPER bug_modal/module.html.tmpl
+ title = tracking_flags_title
+ collapsed = 1
+ subtitle = firefox_flags_subtitle
+ %]
+ [% WRAPPER fields_lhs %]
+
+ [% UNLESS set_tracking_flags.size || set_project_flags.size %]
+ <p class="edit-hide">
+ This [% terms.bug %] is not currently tracked.
+ </p>
+ [% END %]
+
+ [%# tracking flags %]
+ [% WRAPPER bug_modal/field.html.tmpl
+ container = 1
+ label = "Tracking Flags"
+ hide_on_view = set_tracking_flags.size == 0
+ %]
+ [% INCLUDE bug_modal/tracking_flags.html.tmpl
+ type = "tracking"
+ %]
+ [% END %]
+
+ [% END %]
+ [% WRAPPER fields_rhs %]
+
+ [%# project flags %]
+ [% WRAPPER bug_modal/field.html.tmpl
+ container = 1
+ label = "Project Flags"
+ hide_on_view = set_project_flags.size == 0
+ %]
+ [% INCLUDE bug_modal/tracking_flags.html.tmpl
+ type = "project"
+ %]
+ [% END %]
+
+ [% END %]
+ [% END %]
+
+[% END %]
+
+[%# === details === %]
+
+[%
+ sub = [];
+ IF bug.status_whiteboard != "";
+ sub.push(bug.status_whiteboard.truncate(256, '&hellip;'));
+ END;
+ IF bug.cf_crash_signature != "";
+ sub.push("crash signature");
+ END;
+%]
+[% WRAPPER bug_modal/module.html.tmpl
+ title = "Details"
+ collapsed = 1
+ subtitle = sub.join(", ")
+%]
+ [% WRAPPER fields_lhs %]
+
+ [%# whiteboard %]
+ [% WRAPPER bug_modal/field.html.tmpl
+ field = bug_fields.status_whiteboard
+ field_type = constants.FIELD_TYPE_FREETEXT
+ %]
+ [% bug.status_whiteboard == "" ? "---" : bug.status_whiteboard FILTER html %]
+ [% END %]
+
+ [%# votes %]
+ [% IF bug.product_obj.votesperuser %]
+ [% WRAPPER bug_modal/field.html.tmpl
+ container = 1
+ label = "Votes"
+ %]
+ [% bug.votes FILTER html %]
+ vote[% "s" IF bug.votes != 1 %]
+ [% IF user.id %]
+ <button type="button" class="minor" id="vote-btn">
+ [% bug.user_votes ? "Remove vote" : "Vote" %]
+ </button>
+ [% END %]
+ [% END %]
+ [% END %]
+
+ [%# custom fields (except textarea) %]
+ [%
+ FOREACH field = custom_fields;
+ NEXT IF field.type == constants.FIELD_TYPE_EXTENSION || field.type == constants.FIELD_TYPE_TEXTAREA;
+ NEXT IF rendered_custom_fields.exists(field.name);
+ NEXT IF cf_hidden_in_product(field.name, bug.product, bug.component);
+ cf_value = bug.${field.name};
+ IF field.type == constants.FIELD_TYPE_SINGLE_SELECT;
+ has_value = cf_value != "---";
+ ELSIF field.type == constants.FIELD_TYPE_MULTI_SELECT;
+ has_value = cf_value.size != 0;
+ ELSE;
+ has_value = cf_value != "";
+ END;
+ INCLUDE bug_modal/field.html.tmpl
+ field = field
+ field_type = field.type
+ hide_on_view = !has_value;
+ END;
+ %]
+
+ [% END %]
+ [% WRAPPER fields_rhs %]
+
+ [%# url %]
+ [% WRAPPER bug_modal/field.html.tmpl
+ field = bug_fields.bug_file_loc
+ field_type = constants.FIELD_TYPE_FREETEXT
+ hide_on_view = bug.bug_file_loc == ""
+ %]
+ <a href="[% bug.bug_file_loc FILTER html %]" target="_blank"
+ rel="noreferrer" title="[% bug.bug_file_loc FILTER html %]"
+ [% UNLESS is_safe_url(bug.bug_file_loc) +%] class="unsafe-url"[% END %]
+ >[% bug.bug_file_loc FILTER truncate(40) FILTER html %]</a>
+ [% END %]
+
+ [%# see also %]
+ [% INCLUDE bug_modal/field.html.tmpl
+ field = bug_fields.see_also
+ field_type = constants.FIELD_TYPE_BUG_URLS
+ values = bug.see_also
+ hide_on_view = bug.see_also.size == 0
+ %]
+
+ [% END %]
+
+ [%# custom fields (textarea) %]
+ [%
+ FOREACH field IN custom_fields;
+ NEXT IF field.type != constants.FIELD_TYPE_TEXTAREA;
+ NEXT IF rendered_custom_fields.exists(field.name);
+ INCLUDE bug_modal/field.html.tmpl
+ field = field
+ field_type = field.type
+ hide_on_view = bug.${field.name} == "" || bug.${field.name} == "---";
+ END;
+ %]
+[% END %]
+
+[%# === groups === %]
+
+[% WRAPPER bug_modal/module.html.tmpl
+ title = "Security"
+ collapsed = 1
+ subtitle = bug.groups_in.size ? bug.groups_in.pluck("description").join(", ") : "public"
+ hide_on_view = bug.groups_in.size == 0
+ hide_on_edit = bug.groups.size == 0
+%]
+ [% INCLUDE bug_modal/groups.html.tmpl %]
+[% END %]
+
+[%# === user story === %]
+
+[% IF bug.user_story_visible.0 %]
+ [% WRAPPER bug_modal/module.html.tmpl
+ title = "User Story"
+ hide_on_view = bug.cf_user_story == ""
+ collapsed = bug.cf_user_story == ""
+ %]
+ [% IF user.id %]
+ <div id="user-story-actions">
+ [% IF bug.check_can_change_field('cf_user_story', 0, 1) %]
+ <button type="button" class="in-page" id="user-story-edit-btn">Edit</button>
+ [% END %]
+ [% IF bug.cf_user_story != "" && bug.check_can_change_field('longdesc', 0, 1) %]
+ <button type="button" class="in-page" id="user-story-reply-btn">Reply</button>
+ [% END %]
+ </div>
+ [% END %]
+ <pre id="user-story">[% bug.cf_user_story FILTER html %]</pre>
+ [% IF user.id %]
+ <textarea id="cf_user_story" name="cf_user_story" style="display:none" rows="10" cols="80">
+ [%~ bug.cf_user_story FILTER html ~%]
+ </textarea>
+ [% END %]
+ [% END %]
+[% END %]
+
+[%# === attachments === %]
+
+[% IF active_attachments || obsolete_attachments %]
+ [%
+ sub = [];
+ IF active_attachments;
+ sub.push(active_attachments _ " attachment" _ (active_attachments == 1 ? "" : "s"));
+ END;
+ IF obsolete_attachments;
+ sub.push(obsolete_attachments _ " obsolete attachment" _ (obsolete_attachments == 1 ? "" : "s"));
+ END;
+ %]
+ [% WRAPPER bug_modal/module.html.tmpl
+ title = "Attachments"
+ subtitle = sub.join(", ")
+ collapsed = active_attachments == 0
+ %]
+ [% INCLUDE bug_modal/attachments.html.tmpl %]
+ [% IF obsolete_attachments %]
+ <div id="attachments-actions">
+ <button type="button" id="attachments-obsolete-btn" class="in-page">Show Obsolete Attachments</button>
+ </div>
+ [% END %]
+ [% END %]
+[% END %]
+
+[%# === top (between modules and comments) actions === %]
+
+[% IF user.id %]
+ <div id="top-actions">
+ <button type="button" id="attachments-add-btn" class="minor">Attach File</button>
+ <button type="button" class="comment-btn in-page">Add Comment</button>
+ <button type="submit" class="save-btn" id="top-save-btn" style="display:none">Save Changes</button>
+ </div>
+[% END %]
+
+[%# === comments === %]
+
+[%
+ INCLUDE bug_modal/activity_stream.html.tmpl stream=bug.activity_stream;
+ IF user.id;
+ INCLUDE bug_modal/new_comment.html.tmpl;
+ END;
+%]
+
+[%# === bottom actions === %]
+
+[% IF user.id %]
+ <div id="bottom-actions">
+ <button type="submit" class="save-btn" id="bottom-save-btn">Save Changes</button>
+ [%
+ IF bug.resolution == "";
+ seen_header = 0;
+ FOREACH resolution IN ["FIXED", "INVALID", "DUPLICATE"];
+ NEXT UNLESS bug.choices.resolution.only("name", resolution).size;
+ IF NOT seen_header;
+ seen_header = 1;
+ " Resolve as ";
+ END;
+ %] <button type="button" class="in-page resolution-btn">[% resolution FILTER html %]</button> [%
+ END;
+ ELSIF bug.choices.bug_status.only("name", "REOPENED").size;
+ %] <button type="button" class="in-page status-btn" data-status="REOPENED">REOPEN</button> [%
+ END;
+ %]
+ <div id="bottom-right-actions">
+ <button type="button" id="top-btn" class="in-page">Top &uarr;</button>
+ <button type="button" id="new-bug-btn" class="minor">New [% terms.Bug %] &#9662;</button>
+ </div>
+ </div>
+ </form>
+[% ELSE %]
+ <div id="login-required">
+ You need to <a href="show_bug.cgi?id=[% bug.bug_id FILTER none %]&amp;GoAheadAndLogIn=1">log in</a>
+ before you can comment on or make changes to this [% terms.bug %].
+ </div>
+[% END %]
+
+[%# === blocks === %]
+
+[% BLOCK fields_lhs %]
+ <div class="fields-lhs">[% content FILTER none %]</div>
+[% END %]
+
+[% BLOCK fields_rhs %]
+ <div class="fields-rhs">[% content FILTER none %]</div>
+[% END %]
diff --git a/extensions/BugModal/template/en/default/bug_modal/field.html.tmpl b/extensions/BugModal/template/en/default/bug_modal/field.html.tmpl
new file mode 100644
index 000000000..d9b5873d9
--- /dev/null
+++ b/extensions/BugModal/template/en/default/bug_modal/field.html.tmpl
@@ -0,0 +1,275 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[%#
+ # field: (field object) bug_fields.$field_name object
+ # field_type: (const) constants.FIELD_TYPE_*
+ # no_label: (boolean) don't output label
+ # label: (string) field label text (default: field_descs.${$field.name}
+ # view_only: (boolean) don't allow editing (default: determined from bug.check_can_change_field)
+ # edit_only: (boolean) always render the edit ui
+ # container: (boolean) output just a label and the content (eg. for multiple fields next to one label)
+ # value: (string) visible value (default: bug.$name)
+ # values: (array of string) list of value objects (FIELD_TYPE_SINGLE_SELECT and _BUG_URLS only) (default: lazy-load on edit)
+ # inline: (boolean) output field as a table-cell instead of as a stand-alone div (default: false)
+ # no_indent: (boolean) don't indent the field (left-padding) (default: false)
+ # full_width: (boolean) the field takes up the complete page width (default: false)
+ # short_width: (boolean) the field shouldn't take up much space at all (default: false)
+ # hide_on_view: (boolean) hide field from read-only view (default: false)
+ # hide_on_edit: (boolean) hide content when in edit mode (default: false)
+ #%]
+
+[%
+IF field_type.defined && !field;
+ RETURN;
+END;
+IF !name.defined;
+ name = field.name;
+END;
+IF !value.defined;
+ value = bug.$name;
+END;
+IF hide_on_edit;
+ view_only = 1;
+END;
+IF view_only || container;
+ editable = 0;
+END;
+IF !editable.defined;
+ editable = bug.check_can_change_field(name, 0, 1);
+END;
+IF inline && !label.defined;
+ no_label = 1;
+END;
+IF !no_label && !label.defined;
+ label = field_descs.${field.name};
+END;
+IF field_type == "";
+ field_type = -1;
+END;
+IF field_type == constants.FIELD_TYPE_DATE
+ || field_type == constants.FIELD_TYPE_DATETIME;
+ short_width = 1;
+END;
+%]
+
+<div class="field
+ [%~ " indent" IF no_label && !no_indent %]
+ [%~ " inline" IF inline %]
+ [%~ " edit-hide" IF hide_on_edit %]
+ [%~ " edit-show" IF hide_on_view && !hide_on_edit %]"
+ [% IF name %] id="field-[% name FILTER html %]"[% END %]
+ [% IF hide_on_view %] style="display:none"[% END %]
+>
+ [% IF label.defined && !no_label %]
+ <div class="name">[% label _ ":" FILTER html IF label %]</div>
+ [% END %]
+
+ [%# read-only html %]
+ [% UNLESS edit_only %]
+ <div class="[% "value" IF !container %][% " edit-hide" IF editable %][% " container" IF container %]">
+ [% IF name %]
+ <span id="field-value-[% name FILTER html %]">
+ [% END %]
+ [% IF content.defined %]
+ [% content FILTER none %]
+ [% ELSE %]
+ [% SWITCH field_type %]
+
+ [% CASE constants.FIELD_TYPE_USER %]
+ [%# users %]
+ [% INCLUDE bug_modal/user.html.tmpl u=value %]
+
+ [% CASE constants.FIELD_TYPE_BUG_URLS %]
+ [%# see also %]
+ [% INCLUDE bug_urls values=values edit=0 %]
+
+ [% CASE constants.FIELD_TYPE_BUG_LIST %]
+ [%# bug lists (generally dependancies) %]
+ [% INCLUDE bug_list values=values edit=0 %]
+
+ [% CASE constants.FIELD_TYPE_TEXTAREA %]
+ [%# text areas %]
+ <span class="multiline-value">[% value FILTER html FILTER html_line_break %]</span>
+
+ [% CASE constants.FIELD_TYPE_MULTI_SELECT %]
+ [%# multi-select %]
+ [% value.join(", ") FILTER html %]
+
+ [% CASE constants.FIELD_TYPE_DATETIME %]
+ [%# datetime %]
+ [% value FILTER time %]
+
+ [% CASE constants.FIELD_TYPE_DATE %]
+ [%# date %]
+ [% value FILTER time("%Y-%m-%d") %]
+
+ [% CASE constants.FIELD_TYPE_BUG_ID %]
+ [%# bug id %]
+ [% value FILTER bug_link(value, use_alias => 1) FILTER none %]
+
+ [% CASE %]
+ [%# every else %]
+ [% value FILTER html %]
+
+ [% END %]
+ [% END %]
+ [% IF name %]
+ </span>
+ [% END %]
+ </div>
+ [% END %]
+
+ [%# edit html %]
+ [% IF editable %]
+ [% "MISSING NAME" UNLESS name %]
+ <div class="value edit edit-show [% " wide" IF full_width %][% " short" IF short_width %]"
+ [% UNLESS edit_only +%] style="display:none"[% END %]>
+ [% SWITCH field_type %]
+
+ [% CASE constants.FIELD_TYPE_SINGLE_SELECT %]
+ [%# single value select %]
+ <select name="[% name FILTER html %]" id="[% name FILTER html %]">
+ [% IF values.defined %]
+ [% FOREACH v IN values %]
+ [% NEXT IF NOT v.is_active AND NOT value.contains(v.name).size %]
+ <option value="[% v.name FILTER html %]"
+ id="v[% v.id FILTER html %]_[% name FILTER html %]"
+ [% " selected" IF value.contains(v.name).size %]
+ >[% v.name FILTER html %]</option>
+ [% END %]
+ [% ELSE %]
+ <option value="[% value FILTER html %]" selected>[% value FILTER html %]</option>
+ [% END %]
+ </select>
+
+ [% CASE constants.FIELD_TYPE_MULTI_SELECT %]
+ [%# multi value select %]
+ <select name="[% name FILTER html %]" id="[% name FILTER html %]" multiple size="5">
+ [% IF values.defined %]
+ [%# not implemented %]
+ [% ELSE %]
+ [% FOREACH v IN value %]
+ <option value="[% v FILTER html %]" selected>[% v FILTER html %]</option>
+ [% END %]
+ [% END %]
+ </select>
+
+ [% CASE constants.FIELD_TYPE_FREETEXT %]
+ [%# normal input field %]
+ <input name="[% name FILTER html %]" id="[% name FILTER html %]" value="[% value FILTER html %]">
+
+ [% CASE constants.FIELD_TYPE_USER %]
+ [%# single user %]
+ [% INCLUDE global/userselect.html.tmpl
+ id = name
+ name = name
+ value = value.login
+ classes = [ "bz_userfield" ]
+ %]
+
+ [% CASE constants.FIELD_TYPE_USERS %]
+ [%# multiple users %]
+ [% INCLUDE global/userselect.html.tmpl
+ id = name
+ name = name
+ value = value.join(", ")
+ classes = [ "bz_userfield" ]
+ multiple = 5
+ %]
+
+ [% CASE constants.FIELD_TYPE_KEYWORDS %]
+ [%# keywords %]
+ <input type="text" id="[% name FILTER html %]" name="[% name FILTER html %]"
+ value="[% value FILTER html %]">
+
+ [% CASE constants.FIELD_TYPE_BUG_URLS %]
+ [%# see also %]
+ [% INCLUDE bug_urls values=values edit=1 %]
+
+ [% CASE constants.FIELD_TYPE_BUG_LIST %]
+ [%# bug lists %]
+ [% INCLUDE bug_list values=values edit=1 %]
+
+ [% CASE constants.FIELD_TYPE_TEXTAREA %]
+ [%# text area %]
+ <button type="button" class="in-page edit-textarea-btn [%= "edit-textarea-set-btn" IF value != "" %]"
+ id="[% name FILTER html %]-edit">Edit</button>
+ <span class="multiline-value" id="[% name FILTER html %]-view">[% value FILTER html FILTER html_line_break %]</span>
+ <textarea id="[% name FILTER html %]" name="[% name FILTER html %]"
+ rows="10" cols="10" style="display:none">[% value FILTER html %]</textarea>
+
+ [% CASE constants.FIELD_TYPE_DATETIME %]
+ [%# datetime %]
+ <input class="cf_datetime" name="[% name FILTER html %]" id="[% name FILTER html %]"
+ value="[% value FILTER html %]">
+ <img class="cf_datetime-img" id="[% name FILTER html %]-img"
+ src="extensions/BugModal/web/calendar.png" width="16" height="16">
+
+ [% CASE constants.FIELD_TYPE_DATE %]
+ [%# date %]
+ <input class="cf_date" name="[% name FILTER html %]" id="[% name FILTER html %]"
+ value="[% value FILTER html %]">
+ <img class="cf_date-img" id="[% name FILTER html %]-img"
+ src="extensions/BugModal/web/calendar.png" width="16" height="16">
+
+ [% CASE constants.FIELD_TYPE_INTEGER %]
+ [%# integer %]
+ <input type="number" name="[% name FILTER html %]" id="[% name FILTER html %]"
+ value="[% value FILTER html %]">
+
+ [% CASE constants.FIELD_TYPE_BUG_ID %]
+ [%# bug id %]
+ <input type="text" name="[% name FILTER html %]" id="[% name FILTER html %]"
+ value="[% value FILTER html %]">
+
+ [% CASE %]
+ [%# error %]
+ ('[% name FILTER html %]' [[% field_type FILTER html %])] not supported)
+
+ [% END %]
+ </div>
+ [% END %]
+</div>
+
+[%# bug-urls, currently just see-also %]
+[% BLOCK bug_urls %]
+ [% FOREACH url IN values %]
+ <div>
+ [% IF url.isa('Bugzilla::BugUrl::Bugzilla::Local') %]
+ [% url.target_bug_id FILTER bug_link(url.target_bug_id, use_alias => 1) FILTER none %]
+ [% ELSE %]
+ <a href="[% url.name FILTER html %]">[% url.name FILTER html %]</a>
+ [% END %]
+ [% IF edit %]
+ <label>
+ <input type="checkbox" name="remove_see_also" value="[% url.name FILTER html %]">
+ Remove
+ </label>
+ [% END %]
+ </div>
+ [% END %]
+
+ [% IF edit %]
+ <button id="[% name FILTER html %]-btn" class="bug-urls-btn in-page">Add</button>
+ <input id="[% name FILTER html %]" name="[% name FILTER html %]" style="display:none">
+ [% END %]
+[% END %]
+
+[%# bug lists, depencancies, blockers %]
+[% BLOCK bug_list %]
+ [% IF NOT edit %]
+ [% FOREACH b IN values %]
+ [% INCLUDE bug/link.html.tmpl bug=b link_text=b.id use_alias=1 %]
+ [% ", " UNLESS loop.last %]
+ [% END %]
+ [% ELSE %]
+ <input type="text" id="[% name FILTER html %]" name="[% name FILTER html %]"
+ value="[% values.pluck('id').join(", ") FILTER html %]">
+ [% END %]
+[% END %]
diff --git a/extensions/BugModal/template/en/default/bug_modal/flags.html.tmpl b/extensions/BugModal/template/en/default/bug_modal/flags.html.tmpl
new file mode 100644
index 000000000..4f2381913
--- /dev/null
+++ b/extensions/BugModal/template/en/default/bug_modal/flags.html.tmpl
@@ -0,0 +1,162 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[%#
+ # types: array of flag_type objects
+ # no_label: if set to a true value, flag name and status will not be outputted (default: false)
+ # read_only: if true, don't output edit ui (default: false)
+ #%]
+
+[% IF read_only %]
+ <div class="flags edit-hide">
+ [% FOREACH type IN types %]
+ [% FOREACH flag IN type.flags %]
+ <div class="flag">
+ [% UNLESS no_label %]
+ [% INCLUDE bug_modal/user.html.tmpl u=flag.setter nick_only=1 %]
+ [%+ flag.type.name FILTER html %][% flag.status FILTER none %]
+ [% END %]
+ [% IF flag.requestee %]
+ [%+ INCLUDE bug_modal/user.html.tmpl u=flag.requestee nick_only=1 %]
+ [% END %]
+ </div>
+ [% END %]
+ [% END %]
+ </div>
+ [% RETURN %]
+[% END %]
+
+<div id="bug-flags" class="flags">
+ <table class="layout-table">
+ [%
+ FOREACH type IN types;
+ FOREACH flag IN type.flags;
+ IF flag.requestee && flag.requestee.id == user.id;
+ INCLUDE edit_flag t=type f=flag;
+ ELSE;
+ %]
+ <tbody class="edit-hide">
+ [% INCLUDE view_flag t=type f=flag %]
+ </tbody>
+ <tbody class="edit-show" style="display:none">
+ [% INCLUDE edit_flag t=type f=flag %]
+ </tbody>
+ [%
+ END;
+ END;
+ END;
+ %]
+ <tbody class="edit-show" style="display:none">
+ [%
+ FOREACH type IN types;
+ NEXT IF !type.is_active || type.flags.size;
+ INCLUDE edit_flag t=type;
+ END;
+
+ FOREACH type IN types;
+ NEXT IF !type.is_active || !type.is_multiplicable;
+ INCLUDE edit_flag t=type;
+ END;
+ %]
+ </tbody>
+ </table>
+</div>
+
+[% BLOCK view_flag %]
+ <tr>
+ <td class="flag-setter">
+ [% INCLUDE bug_modal/user.html.tmpl u=f.setter nick_only=1 %]
+ </td>
+
+ <td class="flag-name">
+ <span class="rel-time" title="[% f.creation_date FILTER time_duration FILTER html %]">
+ [% f.type.name FILTER html %]
+ </span>
+ </td>
+
+ <td class="flag-value">
+ [% f.status FILTER html %]
+ </td>
+
+ [% IF f.requestee %]
+ <td class="flag-requestee">
+ [% INCLUDE bug_modal/user.html.tmpl u=f.requestee nick_only=1 %]
+ </td>
+ [% END %]
+ </tr>
+[% END %]
+
+[% BLOCK edit_flag %]
+[%
+ can_edit = !f || (f.setter_id == user.id || (f.requestee_id && f.requestee_id == user.id))
+ flag_id = f ? "flag-$f.id" : "flag_type-$t.id";
+%]
+ <tr>
+ <td class="flag-setter">
+ [% IF f %]
+ [% INCLUDE bug_modal/user.html.tmpl u=flag.setter nick_only=1 %]
+ [% ELSIF t.flags.size %]
+ addl.
+ [% END %]
+ </td>
+
+ <td class="flag-name">
+ <label title="[% t.description FILTER html %]" for="[% flag_id FILTER html %]">
+ [%~ t.name FILTER html FILTER no_break ~%]
+ </label>
+ </td>
+
+ <td class="flag-value">
+ <select id="[% flag_id FILTER html %]" name="[% flag_id FILTER html %]"
+ title="[% t.description FILTER html %]"
+ [% UNLESS (t.is_requestable && user.can_request_flag(t)) || user.can_set_flag(t) %]
+ disabled
+ [% END %]
+ class="bug-flag">
+ [% IF !f || (can_edit && user.can_request_flag(t)) || f.setter_id == user.id %]
+ <option value="X"></option>
+ [% END %]
+ [% IF t.is_active && can_edit %]
+ [% IF (t.is_requestable && user.can_request_flag(t)) || (f && f.status == "?") %]
+ <option value="?" [% "selected" IF f && f.status == "?" %]>?</option>
+ [% END %]
+ [% IF user.can_set_flag(t) || (f && f.status == "+") %]
+ <option value="+" [% "selected" IF f && f.status == "+" %]>+</option>
+ [% END %]
+ [% IF user.can_set_flag(t) || (f && f.status == "-") %]
+ <option value="-" [% "selected" IF f && f.status == "-" %]>-</option>
+ [% END %]
+ [% ELSE %]
+ <option value="[% f.status FILTER html %]" selected>[% f.status FILTER html %]</option>
+ [% END %]
+ </select>
+ </td>
+
+ [% IF (t.is_requestable && t.is_requesteeble) || (f && f.requestee) %]
+ <td class="flag-requestee">
+ [% flag_name = f ? "requestee-$f.id" : "requestee_type-$t.id" %]
+ <div id="[% flag_name FILTER none %]-container"
+ [% UNLESS f && f.requestee +%] style="display:none"[% END %]>
+ [%
+ flag_requestee = (f && f.requestee) ? f.requestee.login : '';
+ flag_multiple = f ? 0 : t.is_multiplicable * 3;
+ flag_empty_ok = f ? 1 : !t.is_multiplicable;
+ INCLUDE global/userselect.html.tmpl
+ name = flag_name
+ id = flag_name
+ value = flag_requestee
+ emptyok = flag_empty_ok
+ classes = [ "requestee" ]
+ disabled = !can_edit
+ %]
+ </div>
+ <td>
+ [% END %]
+
+ </tr>
+[% END %]
diff --git a/extensions/BugModal/template/en/default/bug_modal/groups.html.tmpl b/extensions/BugModal/template/en/default/bug_modal/groups.html.tmpl
new file mode 100644
index 000000000..b03db1e49
--- /dev/null
+++ b/extensions/BugModal/template/en/default/bug_modal/groups.html.tmpl
@@ -0,0 +1,68 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[%#
+ # bug: bug object
+ #%]
+
+[%
+ PROCESS global/variables.none.tmpl;
+
+ in_all_groups = 1;
+ in_a_group = 0;
+ FOREACH group IN bug.groups;
+ IF NOT group.ingroup;
+ in_all_groups = 0;
+ END;
+ IF group.ison;
+ in_a_group = 1;
+ END;
+ END
+%]
+
+<div class="groups edit-hide">
+ [% IF in_a_group %]
+ <div id="groups-description">
+ Only users in all of the following groups can view this [% terms.bug %]:
+ </div>
+ <ul>
+ [% FOREACH group IN bug.groups %]
+ [% NEXT UNLESS group.ison || group.mandatory %]
+ <li>[% group.description FILTER html %]</li>
+ [% END %]
+ </ul>
+ [% ELSE %]
+ This [% terms.bug %] is publicaly visible.
+ [% END %]
+</div>
+
+<div class="groups edit-show" style="display:none">
+ [% emitted_description = 0 %]
+ [% FOREACH group IN bug.groups %]
+ [% IF NOT emitted_description %]
+ [% emitted_description = 1 %]
+ <div id="groups-description">
+ Only users in all of the selected groups can view this [% terms.bug %]:
+ </div>
+ [% END %]
+
+ [% IF group.ingroup %]
+ <input type="hidden" name="defined_groups" value="[% group.name FILTER html %]">
+ [% END %]
+
+ <div class="group">
+ <input type="checkbox" value="[% group.name FILTER html %]"
+ name="groups" id="group_[% group.bit FILTER html %]"
+ [% " checked" IF group.ison %]
+ [% " disabled" IF NOT group.ingroup || group.mandatory %]>
+ <label for="group_[% group.bit FILTER html %]">
+ [%~ group.description FILTER html_light ~%]
+ </label>
+ </div>
+ [% END %]
+</div>
diff --git a/extensions/BugModal/template/en/default/bug_modal/header.html.tmpl b/extensions/BugModal/template/en/default/bug_modal/header.html.tmpl
new file mode 100644
index 000000000..c6dd8b74c
--- /dev/null
+++ b/extensions/BugModal/template/en/default/bug_modal/header.html.tmpl
@@ -0,0 +1,100 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[%
+ PROCESS global/variables.none.tmpl;
+
+ # <title>
+ IF bugs.defined;
+ bug = bugs.0;
+ END;
+ title = "$bug.bug_id - ";
+ IF bug.alias;
+ title = title _ "($bug.alias) ";
+ END;
+ unfiltered_title = title _ bug.short_desc;
+ filtered_desc = bug.short_desc FILTER html;
+ title = title _ filtered_desc;
+
+ generate_api_token = 1;
+
+ # these aren't always defined
+ UNLESS bodyclasses.defined;
+ bodyclasses = [];
+ END;
+ UNLESS javascript_urls.defined;
+ javascript_urls = [];
+ END;
+ UNLESS style_urls.defined;
+ style_urls = [];
+ END;
+ UNLESS jquery.defined;
+ jquery = [];
+ END;
+
+ # right now we need yui for the user fields
+ no_yui = 0;
+ yui = ['autocomplete'];
+
+ # add body classes for sec-groups, etc
+ FOREACH group IN bug.groups_in;
+ bodyclasses.push("bz_group_$group.name");
+ END;
+ bodyclasses.push("bug_modal");
+
+ # assets
+ javascript_urls.push(
+ "extensions/BugModal/web/bug_modal.js",
+ "extensions/BugModal/web/ZeroClipboard/ZeroClipboard.min.js",
+ "js/field.js",
+ "js/comments.js",
+ );
+ jquery.push(
+ "datetimepicker",
+ );
+ style_urls.push(
+ "extensions/BugModal/web/bug_modal.css",
+ "skins/custom/bug_groups.css",
+ "js/jquery/plugins/datetimepicker/datetimepicker.css",
+ );
+
+ IF user.in_group('canconfirm');
+ style_urls.push('extensions/TagNewUsers/web/style.css');
+ END;
+%]
+
+[% javascript = BLOCK %]
+ [%# add tracking flags json if available %]
+ [% IF tracking_flags %]
+ [% javascript_urls.push("extensions/TrackingFlags/web/js/tracking_flags.js") %]
+ TrackingFlags = [% tracking_flags_json FILTER none %];
+ [% END %]
+
+ [%# update last-visited %]
+ [% IF user.id && user.is_involved_in_bug(bug) %]
+ $(function() {
+ bugzilla_ajax({
+ url: 'rest/bug_user_last_visit/[% bug.id FILTER none %]',
+ type: 'POST'
+ });
+ });
+ [% END %]
+
+ [%# expose useful data to js %]
+ BUGZILLA.bug_id = [% bug.id FILTER none %];
+ BUGZILLA.bug_title = '[% unfiltered_title FILTER js %]';
+ BUGZILLA.user = {
+ id: [% user.id FILTER none %],
+ login: '[% user.login FILTER js %]',
+ is_insider: [% user.is_insider ? "true" : "false" %],
+ settings: {
+ quote_replies: '[% user.settings.quote_replies.value FILTER js %]',
+ zoom_textareas: [% user.settings.zoom_textareas.value == "on" ? "true" : "false" %]
+ }
+ };
+[% END %]
diff --git a/extensions/BugModal/template/en/default/bug_modal/module.html.tmpl b/extensions/BugModal/template/en/default/bug_modal/module.html.tmpl
new file mode 100644
index 000000000..838069a58
--- /dev/null
+++ b/extensions/BugModal/template/en/default/bug_modal/module.html.tmpl
@@ -0,0 +1,38 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[%#
+ # title: (string, optional) main title of module
+ # collapse: (boolean) if true, show as collapsed by default (default false)
+ # subtitle: (string, optional) sub-title
+ # content: (string, required) module's content (use WRAPPER module..)
+ # hide_on_view: (boolean) if true, the module won't be visible in view mode
+ # hide_on_edit: (boolean) if true, the module won't be visible in edit mode
+ #%]
+
+<div class="module
+ [%~ " edit-hide" IF hide_on_edit %]
+ [%~ " edit-show" IF hide_on_view && !hide_on_edit %]"
+ [% IF hide_on_view +%] style="display:none"[% END %]
+ [% IF title %] id="module-[% title.replace FILTER id %]"[% END %]
+>
+ [% IF title %]
+ <div class="module-header">
+ <div class="module-latch">
+ <div class="module-spinner">[% collapsed ? "&#9656;" : "&#9662;" %]</div>
+ <div class="module-title">[% title FILTER html %]</div>
+ [% IF subtitle %]
+ <div class="module-subtitle">([% subtitle FILTER html %])</div>
+ [% END %]
+ </div>
+ </div>
+ [% END %]
+ <div class="module-content" [% ' style="display:none"' IF collapsed %] >
+ [% content FILTER none %]
+ </div>
+</div>
diff --git a/extensions/BugModal/template/en/default/bug_modal/new_comment.html.tmpl b/extensions/BugModal/template/en/default/bug_modal/new_comment.html.tmpl
new file mode 100644
index 000000000..ff2562bf4
--- /dev/null
+++ b/extensions/BugModal/template/en/default/bug_modal/new_comment.html.tmpl
@@ -0,0 +1,29 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[%#
+ # comment: comment object
+ # bug: bug object
+ #%]
+
+<div id="add-comment">
+ <div id="add-comment-label">Add Comment:</div>
+ [% IF user.is_insider && bug.check_can_change_field('longdesc', 0, 1) %]
+ <div id="add-comment-private"
+ title="Make comment visible only to members of the '[% Param('insidergroup') FILTER html %]' group"
+ >
+ <input type="checkbox" name="comment_is_private" id="add-comment-private-cb"
+ value="1" comment_id="[% comment.count FILTER none %]">
+ <label for="add-comment-private-cb">Private</label>
+ </div>
+ [% END %]
+ <textarea rows="5" cols="80" name="comment" id="comment"></textarea>
+ <div id="after-comment-commit-button">
+ [% Hook.process("after_comment_commit_button", 'bug/edit.html.tmpl') %]
+ </div>
+</div>
diff --git a/extensions/BugModal/template/en/default/bug_modal/rel_time.html.tmpl b/extensions/BugModal/template/en/default/bug_modal/rel_time.html.tmpl
new file mode 100644
index 000000000..3b31dedeb
--- /dev/null
+++ b/extensions/BugModal/template/en/default/bug_modal/rel_time.html.tmpl
@@ -0,0 +1,21 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[%#
+ # ts: timestamp
+ #%]
+
+<span class="rel-time" title="[% ts FILTER time("%Y-%m-%d %H:%M %Z") %]">
+ [%~
+ IF content.defined;
+ content;
+ ELSE;
+ ts FILTER time_duration FILTER html;
+ END;
+ ~%]
+</span>
diff --git a/extensions/BugModal/template/en/default/bug_modal/tracking_flags.html.tmpl b/extensions/BugModal/template/en/default/bug_modal/tracking_flags.html.tmpl
new file mode 100644
index 000000000..5f22338cd
--- /dev/null
+++ b/extensions/BugModal/template/en/default/bug_modal/tracking_flags.html.tmpl
@@ -0,0 +1,96 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[%#
+ # type: tracking flag type (eg. "project", "tracking")
+ #%]
+
+[%
+ flags = [];
+ set_flags = [];
+ FOREACH flag IN tracking_flags;
+ NEXT UNLESS flag.flag_type == type;
+ flags.push(flag);
+ NEXT IF flag.bug_flag(bug.id).value == "---";
+ set_flags.push(flag);
+ END;
+ RETURN UNLESS flags.size;
+%]
+
+[%# view %]
+[% IF set_flags.size %]
+ <div class="flags edit-hide">
+ <table class="layout-table tracking-flags">
+ [% IF type == "tracking" %]
+ <tr>
+ <th></th>
+ <th>Tracking</th>
+ <th>Status</th>
+ </tr>
+ [% END %]
+ [% FOREACH row IN tracking_flags_table %]
+ [%
+ NEXT UNLESS row.type == type;
+ tracking_value = row.tracking.bug_flag(bug_id).value || "---";
+ status_value = row.status.bug_flag(bug_id).value || "---";
+ NEXT IF tracking_value == "---" && status_value == "---";
+ %]
+ <tr>
+ <td class="tracking-flag-name">[% row.name FILTER html %]</td>
+ [% IF type == "tracking" %]
+ <td class="tracking-flag-tracking">[% tracking_value FILTER html %]</td>
+ [% END %]
+ <td class="tracking-flag-status">[% status_value FILTER html %]</td>
+ </tr>
+ [% END %]
+ </table>
+ </div>
+[% END %]
+
+[%# edit %]
+<div class="flags edit-show" style="display:none">
+ <table class="layout-table tracking-flags">
+ [% IF type == "tracking" %]
+ <tr>
+ <th></th>
+ <th>Tracking</th>
+ <th>Status</th>
+ </tr>
+ [% END %]
+ [% FOREACH row IN tracking_flags_table %]
+ [% NEXT UNLESS row.type == type %]
+ <tr>
+ <td class="tracking-flag-name">[% row.name FILTER html %]</td>
+ [% IF type == "tracking" %]
+ <td class="tracking-flag-tracking">[% INCLUDE tf_select flag=row.tracking %]</td>
+ [% END %]
+ <td class="tracking-flag-status">[% INCLUDE tf_select flag=row.status %]</td>
+ </tr>
+ [% END %]
+ </table>
+</div>
+
+[% BLOCK tf_select %]
+ [% RETURN UNLESS flag %]
+ <select id="[% flag.name FILTER html %]" name="[% flag.name FILTER html %]">
+ [%
+ flag_bug_value = flag.bug_flag(bug_id).value;
+ FOREACH value IN flag.values;
+ IF value.name != flag_bug_value;
+ NEXT IF !value.is_active || !flag.can_set_value(value.name);
+ END;
+ %]
+ <option value="[% value.name FILTER html %]"
+ id="v[% value.id FILTER html %]_[% flag.name FILTER html %]"
+ [% " selected" IF flag_bug_value == value.name %]
+ >
+ [% value.name FILTER html %]
+ </option>
+ [% END %]
+ </select>
+[% END %]
diff --git a/extensions/BugModal/template/en/default/bug_modal/user.html.tmpl b/extensions/BugModal/template/en/default/bug_modal/user.html.tmpl
new file mode 100644
index 000000000..016017084
--- /dev/null
+++ b/extensions/BugModal/template/en/default/bug_modal/user.html.tmpl
@@ -0,0 +1,53 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[%#
+ # u : user object
+ # simple : boolean, if true an unadorned name will be displayed (no gravatar, no menu) (default: false)
+ # gravatar_size : size of the gravator icon (default 0, which disables the gravatar)
+ # gravatar_only : boolean, if true output just the gravatar (not-simple only)
+ # nick_only : boolean, if true, the nickname will be used instead of the full name
+ #%]
+
+[%
+RETURN UNLESS u.id;
+DEFAULT gravatar_size = 0;
+%]
+<div class="vcard vcard_[% u.id FILTER none %]">
+ [% FILTER collapse %]
+
+ [% IF simple %]
+ [% IF user.id %]
+ <span class="fn" title="[% u.identity FILTER html %]">[% u.moz_nick FILTER html %]</span>
+ [% ELSE %]
+ <span class="fn">[% u.moz_nick FILTER html %]</span>
+ [% END %]
+
+ [% ELSE %]
+ [% IF gravatar_size %]
+ <img src="[% u.gravatar(gravatar_size * 2) FILTER none %]" class="gravatar"
+ width="[% gravatar_size FILTER none %]" height="[% gravatar_size FILTER none %]">
+ [% END %]
+ [% UNLESS gravatar_only %]
+ <a class="email [%= "disabled" UNLESS u.is_enabled %]"
+ [% IF user.id %]
+ href="mailto:[% u.email FILTER html %]"
+ onclick="return show_usermenu([% u.id FILTER none %], '[% u.email FILTER js %]', true,
+ [% user.in_group('editusers') || user.bless_groups.size > 0 ? "true" : "false" %])"
+ title="[% u.identity FILTER html %]"
+ [% ELSE %]
+ href="user_profile?user_id=[% u.id FILTER none %]"
+ [% END %]
+ >
+ <span class="[% user.id ? 'fn' : 'fna' %]">[% nick_only ? u.moz_nick : (u.name || u.nick) FILTER html %]</span>
+ [%~~%]
+ </a>
+ [% END %]
+ [% END %]
+ [% END %]
+</div>
diff --git a/extensions/BugModal/template/en/default/hook/global/setting-descs-settings.none.tmpl b/extensions/BugModal/template/en/default/hook/global/setting-descs-settings.none.tmpl
new file mode 100644
index 000000000..7214977bb
--- /dev/null
+++ b/extensions/BugModal/template/en/default/hook/global/setting-descs-settings.none.tmpl
@@ -0,0 +1,11 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[%
+ setting_descs.ui_experiments = "Use experimental user interface"
+%]
diff --git a/extensions/BugModal/template/en/default/hook/global/variables-end.none.tmpl b/extensions/BugModal/template/en/default/hook/global/variables-end.none.tmpl
new file mode 100644
index 000000000..acb6ab870
--- /dev/null
+++ b/extensions/BugModal/template/en/default/hook/global/variables-end.none.tmpl
@@ -0,0 +1,13 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[%
+ constants.FIELD_TYPE_USER = 20;
+ constants.FIELD_TYPE_USERS = 21;
+ constants.FIELD_TYPE_BUG_LIST = 22;
+%]
diff --git a/extensions/BugModal/web/ZeroClipboard/ZeroClipboard.min.js b/extensions/BugModal/web/ZeroClipboard/ZeroClipboard.min.js
new file mode 100644
index 000000000..b9c734d01
--- /dev/null
+++ b/extensions/BugModal/web/ZeroClipboard/ZeroClipboard.min.js
@@ -0,0 +1,9 @@
+/*!
+ * ZeroClipboard
+ * The ZeroClipboard library provides an easy way to copy text to the clipboard using an invisible Adobe Flash movie and a JavaScript interface.
+ * Copyright (c) 2009-2014 Jon Rohan, James M. Greene
+ * Licensed MIT
+ * http://zeroclipboard.org/
+ * v2.2.0
+ */
+!function(a,b){"use strict";var c,d,e,f=a,g=f.document,h=f.navigator,i=f.setTimeout,j=f.clearTimeout,k=f.setInterval,l=f.clearInterval,m=f.getComputedStyle,n=f.encodeURIComponent,o=f.ActiveXObject,p=f.Error,q=f.Number.parseInt||f.parseInt,r=f.Number.parseFloat||f.parseFloat,s=f.Number.isNaN||f.isNaN,t=f.Date.now,u=f.Object.keys,v=f.Object.defineProperty,w=f.Object.prototype.hasOwnProperty,x=f.Array.prototype.slice,y=function(){var a=function(a){return a};if("function"==typeof f.wrap&&"function"==typeof f.unwrap)try{var b=g.createElement("div"),c=f.unwrap(b);1===b.nodeType&&c&&1===c.nodeType&&(a=f.unwrap)}catch(d){}return a}(),z=function(a){return x.call(a,0)},A=function(){var a,c,d,e,f,g,h=z(arguments),i=h[0]||{};for(a=1,c=h.length;c>a;a++)if(null!=(d=h[a]))for(e in d)w.call(d,e)&&(f=i[e],g=d[e],i!==g&&g!==b&&(i[e]=g));return i},B=function(a){var b,c,d,e;if("object"!=typeof a||null==a||"number"==typeof a.nodeType)b=a;else if("number"==typeof a.length)for(b=[],c=0,d=a.length;d>c;c++)w.call(a,c)&&(b[c]=B(a[c]));else{b={};for(e in a)w.call(a,e)&&(b[e]=B(a[e]))}return b},C=function(a,b){for(var c={},d=0,e=b.length;e>d;d++)b[d]in a&&(c[b[d]]=a[b[d]]);return c},D=function(a,b){var c={};for(var d in a)-1===b.indexOf(d)&&(c[d]=a[d]);return c},E=function(a){if(a)for(var b in a)w.call(a,b)&&delete a[b];return a},F=function(a,b){if(a&&1===a.nodeType&&a.ownerDocument&&b&&(1===b.nodeType&&b.ownerDocument&&b.ownerDocument===a.ownerDocument||9===b.nodeType&&!b.ownerDocument&&b===a.ownerDocument))do{if(a===b)return!0;a=a.parentNode}while(a);return!1},G=function(a){var b;return"string"==typeof a&&a&&(b=a.split("#")[0].split("?")[0],b=a.slice(0,a.lastIndexOf("/")+1)),b},H=function(a){var b,c;return"string"==typeof a&&a&&(c=a.match(/^(?:|[^:@]*@|.+\)@(?=http[s]?|file)|.+?\s+(?: at |@)(?:[^:\(]+ )*[\(]?)((?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?/),c&&c[1]?b=c[1]:(c=a.match(/\)@((?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?/),c&&c[1]&&(b=c[1]))),b},I=function(){var a,b;try{throw new p}catch(c){b=c}return b&&(a=b.sourceURL||b.fileName||H(b.stack)),a},J=function(){var a,c,d;if(g.currentScript&&(a=g.currentScript.src))return a;if(c=g.getElementsByTagName("script"),1===c.length)return c[0].src||b;if("readyState"in c[0])for(d=c.length;d--;)if("interactive"===c[d].readyState&&(a=c[d].src))return a;return"loading"===g.readyState&&(a=c[c.length-1].src)?a:(a=I())?a:b},K=function(){var a,c,d,e=g.getElementsByTagName("script");for(a=e.length;a--;){if(!(d=e[a].src)){c=null;break}if(d=G(d),null==c)c=d;else if(c!==d){c=null;break}}return c||b},L=function(){var a=G(J())||K()||"";return a+"ZeroClipboard.swf"},M=function(){return null==a.opener&&(!!a.top&&a!=a.top||!!a.parent&&a!=a.parent)}(),N={bridge:null,version:"0.0.0",pluginType:"unknown",disabled:null,outdated:null,sandboxed:null,unavailable:null,degraded:null,deactivated:null,overdue:null,ready:null},O="11.0.0",P={},Q={},R=null,S=0,T=0,U={ready:"Flash communication is established",error:{"flash-disabled":"Flash is disabled or not installed. May also be attempting to run Flash in a sandboxed iframe, which is impossible.","flash-outdated":"Flash is too outdated to support ZeroClipboard","flash-sandboxed":"Attempting to run Flash in a sandboxed iframe, which is impossible","flash-unavailable":"Flash is unable to communicate bidirectionally with JavaScript","flash-degraded":"Flash is unable to preserve data fidelity when communicating with JavaScript","flash-deactivated":"Flash is too outdated for your browser and/or is configured as click-to-activate.\nThis may also mean that the ZeroClipboard SWF object could not be loaded, so please check your `swfPath` configuration and/or network connectivity.\nMay also be attempting to run Flash in a sandboxed iframe, which is impossible.","flash-overdue":"Flash communication was established but NOT within the acceptable time limit","version-mismatch":"ZeroClipboard JS version number does not match ZeroClipboard SWF version number","clipboard-error":"At least one error was thrown while ZeroClipboard was attempting to inject your data into the clipboard","config-mismatch":"ZeroClipboard configuration does not match Flash's reality","swf-not-found":"The ZeroClipboard SWF object could not be loaded, so please check your `swfPath` configuration and/or network connectivity"}},V=["flash-unavailable","flash-degraded","flash-overdue","version-mismatch","config-mismatch","clipboard-error"],W=["flash-disabled","flash-outdated","flash-sandboxed","flash-unavailable","flash-degraded","flash-deactivated","flash-overdue"],X=new RegExp("^flash-("+W.map(function(a){return a.replace(/^flash-/,"")}).join("|")+")$"),Y=new RegExp("^flash-("+W.slice(1).map(function(a){return a.replace(/^flash-/,"")}).join("|")+")$"),Z={swfPath:L(),trustedDomains:a.location.host?[a.location.host]:[],cacheBust:!0,forceEnhancedClipboard:!1,flashLoadTimeout:3e4,autoActivate:!0,bubbleEvents:!0,containerId:"global-zeroclipboard-html-bridge",containerClass:"global-zeroclipboard-container",swfObjectId:"global-zeroclipboard-flash-bridge",hoverClass:"zeroclipboard-is-hover",activeClass:"zeroclipboard-is-active",forceHandCursor:!1,title:null,zIndex:999999999},$=function(a){if("object"==typeof a&&null!==a)for(var b in a)if(w.call(a,b))if(/^(?:forceHandCursor|title|zIndex|bubbleEvents)$/.test(b))Z[b]=a[b];else if(null==N.bridge)if("containerId"===b||"swfObjectId"===b){if(!nb(a[b]))throw new Error("The specified `"+b+"` value is not valid as an HTML4 Element ID");Z[b]=a[b]}else Z[b]=a[b];{if("string"!=typeof a||!a)return B(Z);if(w.call(Z,a))return Z[a]}},_=function(){return Tb(),{browser:C(h,["userAgent","platform","appName"]),flash:D(N,["bridge"]),zeroclipboard:{version:Vb.version,config:Vb.config()}}},ab=function(){return!!(N.disabled||N.outdated||N.sandboxed||N.unavailable||N.degraded||N.deactivated)},bb=function(a,d){var e,f,g,h={};if("string"==typeof a&&a)g=a.toLowerCase().split(/\s+/);else if("object"==typeof a&&a&&"undefined"==typeof d)for(e in a)w.call(a,e)&&"string"==typeof e&&e&&"function"==typeof a[e]&&Vb.on(e,a[e]);if(g&&g.length){for(e=0,f=g.length;f>e;e++)a=g[e].replace(/^on/,""),h[a]=!0,P[a]||(P[a]=[]),P[a].push(d);if(h.ready&&N.ready&&Vb.emit({type:"ready"}),h.error){for(e=0,f=W.length;f>e;e++)if(N[W[e].replace(/^flash-/,"")]===!0){Vb.emit({type:"error",name:W[e]});break}c!==b&&Vb.version!==c&&Vb.emit({type:"error",name:"version-mismatch",jsVersion:Vb.version,swfVersion:c})}}return Vb},cb=function(a,b){var c,d,e,f,g;if(0===arguments.length)f=u(P);else if("string"==typeof a&&a)f=a.split(/\s+/);else if("object"==typeof a&&a&&"undefined"==typeof b)for(c in a)w.call(a,c)&&"string"==typeof c&&c&&"function"==typeof a[c]&&Vb.off(c,a[c]);if(f&&f.length)for(c=0,d=f.length;d>c;c++)if(a=f[c].toLowerCase().replace(/^on/,""),g=P[a],g&&g.length)if(b)for(e=g.indexOf(b);-1!==e;)g.splice(e,1),e=g.indexOf(b,e);else g.length=0;return Vb},db=function(a){var b;return b="string"==typeof a&&a?B(P[a])||null:B(P)},eb=function(a){var b,c,d;return a=ob(a),a&&!vb(a)?"ready"===a.type&&N.overdue===!0?Vb.emit({type:"error",name:"flash-overdue"}):(b=A({},a),tb.call(this,b),"copy"===a.type&&(d=Db(Q),c=d.data,R=d.formatMap),c):void 0},fb=function(){var a=N.sandboxed;if(Tb(),"boolean"!=typeof N.ready&&(N.ready=!1),N.sandboxed!==a&&N.sandboxed===!0)N.ready=!1,Vb.emit({type:"error",name:"flash-sandboxed"});else if(!Vb.isFlashUnusable()&&null===N.bridge){var b=Z.flashLoadTimeout;"number"==typeof b&&b>=0&&(S=i(function(){"boolean"!=typeof N.deactivated&&(N.deactivated=!0),N.deactivated===!0&&Vb.emit({type:"error",name:"flash-deactivated"})},b)),N.overdue=!1,Bb()}},gb=function(){Vb.clearData(),Vb.blur(),Vb.emit("destroy"),Cb(),Vb.off()},hb=function(a,b){var c;if("object"==typeof a&&a&&"undefined"==typeof b)c=a,Vb.clearData();else{if("string"!=typeof a||!a)return;c={},c[a]=b}for(var d in c)"string"==typeof d&&d&&w.call(c,d)&&"string"==typeof c[d]&&c[d]&&(Q[d]=c[d])},ib=function(a){"undefined"==typeof a?(E(Q),R=null):"string"==typeof a&&w.call(Q,a)&&delete Q[a]},jb=function(a){return"undefined"==typeof a?B(Q):"string"==typeof a&&w.call(Q,a)?Q[a]:void 0},kb=function(a){if(a&&1===a.nodeType){d&&(Lb(d,Z.activeClass),d!==a&&Lb(d,Z.hoverClass)),d=a,Kb(a,Z.hoverClass);var b=a.getAttribute("title")||Z.title;if("string"==typeof b&&b){var c=Ab(N.bridge);c&&c.setAttribute("title",b)}var e=Z.forceHandCursor===!0||"pointer"===Mb(a,"cursor");Rb(e),Qb()}},lb=function(){var a=Ab(N.bridge);a&&(a.removeAttribute("title"),a.style.left="0px",a.style.top="-9999px",a.style.width="1px",a.style.height="1px"),d&&(Lb(d,Z.hoverClass),Lb(d,Z.activeClass),d=null)},mb=function(){return d||null},nb=function(a){return"string"==typeof a&&a&&/^[A-Za-z][A-Za-z0-9_:\-\.]*$/.test(a)},ob=function(a){var b;if("string"==typeof a&&a?(b=a,a={}):"object"==typeof a&&a&&"string"==typeof a.type&&a.type&&(b=a.type),b){b=b.toLowerCase(),!a.target&&(/^(copy|aftercopy|_click)$/.test(b)||"error"===b&&"clipboard-error"===a.name)&&(a.target=e),A(a,{type:b,target:a.target||d||null,relatedTarget:a.relatedTarget||null,currentTarget:N&&N.bridge||null,timeStamp:a.timeStamp||t()||null});var c=U[a.type];return"error"===a.type&&a.name&&c&&(c=c[a.name]),c&&(a.message=c),"ready"===a.type&&A(a,{target:null,version:N.version}),"error"===a.type&&(X.test(a.name)&&A(a,{target:null,minimumVersion:O}),Y.test(a.name)&&A(a,{version:N.version})),"copy"===a.type&&(a.clipboardData={setData:Vb.setData,clearData:Vb.clearData}),"aftercopy"===a.type&&(a=Eb(a,R)),a.target&&!a.relatedTarget&&(a.relatedTarget=pb(a.target)),qb(a)}},pb=function(a){var b=a&&a.getAttribute&&a.getAttribute("data-clipboard-target");return b?g.getElementById(b):null},qb=function(a){if(a&&/^_(?:click|mouse(?:over|out|down|up|move))$/.test(a.type)){var c=a.target,d="_mouseover"===a.type&&a.relatedTarget?a.relatedTarget:b,e="_mouseout"===a.type&&a.relatedTarget?a.relatedTarget:b,h=Nb(c),i=f.screenLeft||f.screenX||0,j=f.screenTop||f.screenY||0,k=g.body.scrollLeft+g.documentElement.scrollLeft,l=g.body.scrollTop+g.documentElement.scrollTop,m=h.left+("number"==typeof a._stageX?a._stageX:0),n=h.top+("number"==typeof a._stageY?a._stageY:0),o=m-k,p=n-l,q=i+o,r=j+p,s="number"==typeof a.movementX?a.movementX:0,t="number"==typeof a.movementY?a.movementY:0;delete a._stageX,delete a._stageY,A(a,{srcElement:c,fromElement:d,toElement:e,screenX:q,screenY:r,pageX:m,pageY:n,clientX:o,clientY:p,x:o,y:p,movementX:s,movementY:t,offsetX:0,offsetY:0,layerX:0,layerY:0})}return a},rb=function(a){var b=a&&"string"==typeof a.type&&a.type||"";return!/^(?:(?:before)?copy|destroy)$/.test(b)},sb=function(a,b,c,d){d?i(function(){a.apply(b,c)},0):a.apply(b,c)},tb=function(a){if("object"==typeof a&&a&&a.type){var b=rb(a),c=P["*"]||[],d=P[a.type]||[],e=c.concat(d);if(e&&e.length){var g,h,i,j,k,l=this;for(g=0,h=e.length;h>g;g++)i=e[g],j=l,"string"==typeof i&&"function"==typeof f[i]&&(i=f[i]),"object"==typeof i&&i&&"function"==typeof i.handleEvent&&(j=i,i=i.handleEvent),"function"==typeof i&&(k=A({},a),sb(i,j,[k],b))}return this}},ub=function(a){var b=null;return(M===!1||a&&"error"===a.type&&a.name&&-1!==V.indexOf(a.name))&&(b=!1),b},vb=function(a){var b=a.target||d||null,f="swf"===a._source;switch(delete a._source,a.type){case"error":var g="flash-sandboxed"===a.name||ub(a);"boolean"==typeof g&&(N.sandboxed=g),-1!==W.indexOf(a.name)?A(N,{disabled:"flash-disabled"===a.name,outdated:"flash-outdated"===a.name,unavailable:"flash-unavailable"===a.name,degraded:"flash-degraded"===a.name,deactivated:"flash-deactivated"===a.name,overdue:"flash-overdue"===a.name,ready:!1}):"version-mismatch"===a.name&&(c=a.swfVersion,A(N,{disabled:!1,outdated:!1,unavailable:!1,degraded:!1,deactivated:!1,overdue:!1,ready:!1})),Pb();break;case"ready":c=a.swfVersion;var h=N.deactivated===!0;A(N,{disabled:!1,outdated:!1,sandboxed:!1,unavailable:!1,degraded:!1,deactivated:!1,overdue:h,ready:!h}),Pb();break;case"beforecopy":e=b;break;case"copy":var i,j,k=a.relatedTarget;!Q["text/html"]&&!Q["text/plain"]&&k&&(j=k.value||k.outerHTML||k.innerHTML)&&(i=k.value||k.textContent||k.innerText)?(a.clipboardData.clearData(),a.clipboardData.setData("text/plain",i),j!==i&&a.clipboardData.setData("text/html",j)):!Q["text/plain"]&&a.target&&(i=a.target.getAttribute("data-clipboard-text"))&&(a.clipboardData.clearData(),a.clipboardData.setData("text/plain",i));break;case"aftercopy":wb(a),Vb.clearData(),b&&b!==Jb()&&b.focus&&b.focus();break;case"_mouseover":Vb.focus(b),Z.bubbleEvents===!0&&f&&(b&&b!==a.relatedTarget&&!F(a.relatedTarget,b)&&xb(A({},a,{type:"mouseenter",bubbles:!1,cancelable:!1})),xb(A({},a,{type:"mouseover"})));break;case"_mouseout":Vb.blur(),Z.bubbleEvents===!0&&f&&(b&&b!==a.relatedTarget&&!F(a.relatedTarget,b)&&xb(A({},a,{type:"mouseleave",bubbles:!1,cancelable:!1})),xb(A({},a,{type:"mouseout"})));break;case"_mousedown":Kb(b,Z.activeClass),Z.bubbleEvents===!0&&f&&xb(A({},a,{type:a.type.slice(1)}));break;case"_mouseup":Lb(b,Z.activeClass),Z.bubbleEvents===!0&&f&&xb(A({},a,{type:a.type.slice(1)}));break;case"_click":e=null,Z.bubbleEvents===!0&&f&&xb(A({},a,{type:a.type.slice(1)}));break;case"_mousemove":Z.bubbleEvents===!0&&f&&xb(A({},a,{type:a.type.slice(1)}))}return/^_(?:click|mouse(?:over|out|down|up|move))$/.test(a.type)?!0:void 0},wb=function(a){if(a.errors&&a.errors.length>0){var b=B(a);A(b,{type:"error",name:"clipboard-error"}),delete b.success,i(function(){Vb.emit(b)},0)}},xb=function(a){if(a&&"string"==typeof a.type&&a){var b,c=a.target||null,d=c&&c.ownerDocument||g,e={view:d.defaultView||f,canBubble:!0,cancelable:!0,detail:"click"===a.type?1:0,button:"number"==typeof a.which?a.which-1:"number"==typeof a.button?a.button:d.createEvent?0:1},h=A(e,a);c&&d.createEvent&&c.dispatchEvent&&(h=[h.type,h.canBubble,h.cancelable,h.view,h.detail,h.screenX,h.screenY,h.clientX,h.clientY,h.ctrlKey,h.altKey,h.shiftKey,h.metaKey,h.button,h.relatedTarget],b=d.createEvent("MouseEvents"),b.initMouseEvent&&(b.initMouseEvent.apply(b,h),b._source="js",c.dispatchEvent(b)))}},yb=function(){var a=Z.flashLoadTimeout;if("number"==typeof a&&a>=0){var b=Math.min(1e3,a/10),c=Z.swfObjectId+"_fallbackContent";T=k(function(){var a=g.getElementById(c);Ob(a)&&(Pb(),N.deactivated=null,Vb.emit({type:"error",name:"swf-not-found"}))},b)}},zb=function(){var a=g.createElement("div");return a.id=Z.containerId,a.className=Z.containerClass,a.style.position="absolute",a.style.left="0px",a.style.top="-9999px",a.style.width="1px",a.style.height="1px",a.style.zIndex=""+Sb(Z.zIndex),a},Ab=function(a){for(var b=a&&a.parentNode;b&&"OBJECT"===b.nodeName&&b.parentNode;)b=b.parentNode;return b||null},Bb=function(){var a,b=N.bridge,c=Ab(b);if(!b){var d=Ib(f.location.host,Z),e="never"===d?"none":"all",h=Gb(A({jsVersion:Vb.version},Z)),i=Z.swfPath+Fb(Z.swfPath,Z);c=zb();var j=g.createElement("div");c.appendChild(j),g.body.appendChild(c);var k=g.createElement("div"),l="activex"===N.pluginType;k.innerHTML='<object id="'+Z.swfObjectId+'" name="'+Z.swfObjectId+'" width="100%" height="100%" '+(l?'classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"':'type="application/x-shockwave-flash" data="'+i+'"')+">"+(l?'<param name="movie" value="'+i+'"/>':"")+'<param name="allowScriptAccess" value="'+d+'"/><param name="allowNetworking" value="'+e+'"/><param name="menu" value="false"/><param name="wmode" value="transparent"/><param name="flashvars" value="'+h+'"/><div id="'+Z.swfObjectId+'_fallbackContent">&nbsp;</div></object>',b=k.firstChild,k=null,y(b).ZeroClipboard=Vb,c.replaceChild(b,j),yb()}return b||(b=g[Z.swfObjectId],b&&(a=b.length)&&(b=b[a-1]),!b&&c&&(b=c.firstChild)),N.bridge=b||null,b},Cb=function(){var a=N.bridge;if(a){var d=Ab(a);d&&("activex"===N.pluginType&&"readyState"in a?(a.style.display="none",function e(){if(4===a.readyState){for(var b in a)"function"==typeof a[b]&&(a[b]=null);a.parentNode&&a.parentNode.removeChild(a),d.parentNode&&d.parentNode.removeChild(d)}else i(e,10)}()):(a.parentNode&&a.parentNode.removeChild(a),d.parentNode&&d.parentNode.removeChild(d))),Pb(),N.ready=null,N.bridge=null,N.deactivated=null,c=b}},Db=function(a){var b={},c={};if("object"==typeof a&&a){for(var d in a)if(d&&w.call(a,d)&&"string"==typeof a[d]&&a[d])switch(d.toLowerCase()){case"text/plain":case"text":case"air:text":case"flash:text":b.text=a[d],c.text=d;break;case"text/html":case"html":case"air:html":case"flash:html":b.html=a[d],c.html=d;break;case"application/rtf":case"text/rtf":case"rtf":case"richtext":case"air:rtf":case"flash:rtf":b.rtf=a[d],c.rtf=d}return{data:b,formatMap:c}}},Eb=function(a,b){if("object"!=typeof a||!a||"object"!=typeof b||!b)return a;var c={};for(var d in a)if(w.call(a,d))if("errors"===d){c[d]=a[d]?a[d].slice():[];for(var e=0,f=c[d].length;f>e;e++)c[d][e].format=b[c[d][e].format]}else if("success"!==d&&"data"!==d)c[d]=a[d];else{c[d]={};var g=a[d];for(var h in g)h&&w.call(g,h)&&w.call(b,h)&&(c[d][b[h]]=g[h])}return c},Fb=function(a,b){var c=null==b||b&&b.cacheBust===!0;return c?(-1===a.indexOf("?")?"?":"&")+"noCache="+t():""},Gb=function(a){var b,c,d,e,g="",h=[];if(a.trustedDomains&&("string"==typeof a.trustedDomains?e=[a.trustedDomains]:"object"==typeof a.trustedDomains&&"length"in a.trustedDomains&&(e=a.trustedDomains)),e&&e.length)for(b=0,c=e.length;c>b;b++)if(w.call(e,b)&&e[b]&&"string"==typeof e[b]){if(d=Hb(e[b]),!d)continue;if("*"===d){h.length=0,h.push(d);break}h.push.apply(h,[d,"//"+d,f.location.protocol+"//"+d])}return h.length&&(g+="trustedOrigins="+n(h.join(","))),a.forceEnhancedClipboard===!0&&(g+=(g?"&":"")+"forceEnhancedClipboard=true"),"string"==typeof a.swfObjectId&&a.swfObjectId&&(g+=(g?"&":"")+"swfObjectId="+n(a.swfObjectId)),"string"==typeof a.jsVersion&&a.jsVersion&&(g+=(g?"&":"")+"jsVersion="+n(a.jsVersion)),g},Hb=function(a){if(null==a||""===a)return null;if(a=a.replace(/^\s+|\s+$/g,""),""===a)return null;var b=a.indexOf("//");a=-1===b?a:a.slice(b+2);var c=a.indexOf("/");return a=-1===c?a:-1===b||0===c?null:a.slice(0,c),a&&".swf"===a.slice(-4).toLowerCase()?null:a||null},Ib=function(){var a=function(a){var b,c,d,e=[];if("string"==typeof a&&(a=[a]),"object"!=typeof a||!a||"number"!=typeof a.length)return e;for(b=0,c=a.length;c>b;b++)if(w.call(a,b)&&(d=Hb(a[b]))){if("*"===d){e.length=0,e.push("*");break}-1===e.indexOf(d)&&e.push(d)}return e};return function(b,c){var d=Hb(c.swfPath);null===d&&(d=b);var e=a(c.trustedDomains),f=e.length;if(f>0){if(1===f&&"*"===e[0])return"always";if(-1!==e.indexOf(b))return 1===f&&b===d?"sameDomain":"always"}return"never"}}(),Jb=function(){try{return g.activeElement}catch(a){return null}},Kb=function(a,b){var c,d,e,f=[];if("string"==typeof b&&b&&(f=b.split(/\s+/)),a&&1===a.nodeType&&f.length>0)if(a.classList)for(c=0,d=f.length;d>c;c++)a.classList.add(f[c]);else if(a.hasOwnProperty("className")){for(e=" "+a.className+" ",c=0,d=f.length;d>c;c++)-1===e.indexOf(" "+f[c]+" ")&&(e+=f[c]+" ");a.className=e.replace(/^\s+|\s+$/g,"")}return a},Lb=function(a,b){var c,d,e,f=[];if("string"==typeof b&&b&&(f=b.split(/\s+/)),a&&1===a.nodeType&&f.length>0)if(a.classList&&a.classList.length>0)for(c=0,d=f.length;d>c;c++)a.classList.remove(f[c]);else if(a.className){for(e=(" "+a.className+" ").replace(/[\r\n\t]/g," "),c=0,d=f.length;d>c;c++)e=e.replace(" "+f[c]+" "," ");a.className=e.replace(/^\s+|\s+$/g,"")}return a},Mb=function(a,b){var c=m(a,null).getPropertyValue(b);return"cursor"!==b||c&&"auto"!==c||"A"!==a.nodeName?c:"pointer"},Nb=function(a){var b={left:0,top:0,width:0,height:0};if(a.getBoundingClientRect){var c=a.getBoundingClientRect(),d=f.pageXOffset,e=f.pageYOffset,h=g.documentElement.clientLeft||0,i=g.documentElement.clientTop||0,j=0,k=0;if("relative"===Mb(g.body,"position")){var l=g.body.getBoundingClientRect(),m=g.documentElement.getBoundingClientRect();j=l.left-m.left||0,k=l.top-m.top||0}b.left=c.left+d-h-j,b.top=c.top+e-i-k,b.width="width"in c?c.width:c.right-c.left,b.height="height"in c?c.height:c.bottom-c.top}return b},Ob=function(a){if(!a)return!1;var b=m(a,null),c=r(b.height)>0,d=r(b.width)>0,e=r(b.top)>=0,f=r(b.left)>=0,g=c&&d&&e&&f,h=g?null:Nb(a),i="none"!==b.display&&"collapse"!==b.visibility&&(g||!!h&&(c||h.height>0)&&(d||h.width>0)&&(e||h.top>=0)&&(f||h.left>=0));return i},Pb=function(){j(S),S=0,l(T),T=0},Qb=function(){var a;if(d&&(a=Ab(N.bridge))){var b=Nb(d);A(a.style,{width:b.width+"px",height:b.height+"px",top:b.top+"px",left:b.left+"px",zIndex:""+Sb(Z.zIndex)})}},Rb=function(a){N.ready===!0&&(N.bridge&&"function"==typeof N.bridge.setHandCursor?N.bridge.setHandCursor(a):N.ready=!1)},Sb=function(a){if(/^(?:auto|inherit)$/.test(a))return a;var b;return"number"!=typeof a||s(a)?"string"==typeof a&&(b=Sb(q(a,10))):b=a,"number"==typeof b?b:"auto"},Tb=function(b){var c,d,e,f=N.sandboxed,g=null;if(b=b===!0,M===!1)g=!1;else{try{d=a.frameElement||null}catch(h){e={name:h.name,message:h.message}}if(d&&1===d.nodeType&&"IFRAME"===d.nodeName)try{g=d.hasAttribute("sandbox")}catch(h){g=null}else{try{c=document.domain||null}catch(h){c=null}(null===c||e&&"SecurityError"===e.name&&/(^|[\s\(\[@])sandbox(es|ed|ing|[\s\.,!\)\]@]|$)/.test(e.message.toLowerCase()))&&(g=!0)}}return N.sandboxed=g,f===g||b||Ub(o),g},Ub=function(a){function b(a){var b=a.match(/[\d]+/g);return b.length=3,b.join(".")}function c(a){return!!a&&(a=a.toLowerCase())&&(/^(pepflashplayer\.dll|libpepflashplayer\.so|pepperflashplayer\.plugin)$/.test(a)||"chrome.plugin"===a.slice(-13))}function d(a){a&&(i=!0,a.version&&(l=b(a.version)),!l&&a.description&&(l=b(a.description)),a.filename&&(k=c(a.filename)))}var e,f,g,i=!1,j=!1,k=!1,l="";if(h.plugins&&h.plugins.length)e=h.plugins["Shockwave Flash"],d(e),h.plugins["Shockwave Flash 2.0"]&&(i=!0,l="2.0.0.11");else if(h.mimeTypes&&h.mimeTypes.length)g=h.mimeTypes["application/x-shockwave-flash"],e=g&&g.enabledPlugin,d(e);else if("undefined"!=typeof a){j=!0;try{f=new a("ShockwaveFlash.ShockwaveFlash.7"),i=!0,l=b(f.GetVariable("$version"))}catch(m){try{f=new a("ShockwaveFlash.ShockwaveFlash.6"),i=!0,l="6.0.21"}catch(n){try{f=new a("ShockwaveFlash.ShockwaveFlash"),i=!0,l=b(f.GetVariable("$version"))}catch(o){j=!1}}}}N.disabled=i!==!0,N.outdated=l&&r(l)<r(O),N.version=l||"0.0.0",N.pluginType=k?"pepper":j?"activex":i?"netscape":"unknown"};Ub(o),Tb(!0);var Vb=function(){return this instanceof Vb?void("function"==typeof Vb._createClient&&Vb._createClient.apply(this,z(arguments))):new Vb};v(Vb,"version",{value:"2.2.0",writable:!1,configurable:!0,enumerable:!0}),Vb.config=function(){return $.apply(this,z(arguments))},Vb.state=function(){return _.apply(this,z(arguments))},Vb.isFlashUnusable=function(){return ab.apply(this,z(arguments))},Vb.on=function(){return bb.apply(this,z(arguments))},Vb.off=function(){return cb.apply(this,z(arguments))},Vb.handlers=function(){return db.apply(this,z(arguments))},Vb.emit=function(){return eb.apply(this,z(arguments))},Vb.create=function(){return fb.apply(this,z(arguments))},Vb.destroy=function(){return gb.apply(this,z(arguments))},Vb.setData=function(){return hb.apply(this,z(arguments))},Vb.clearData=function(){return ib.apply(this,z(arguments))},Vb.getData=function(){return jb.apply(this,z(arguments))},Vb.focus=Vb.activate=function(){return kb.apply(this,z(arguments))},Vb.blur=Vb.deactivate=function(){return lb.apply(this,z(arguments))},Vb.activeElement=function(){return mb.apply(this,z(arguments))};var Wb=0,Xb={},Yb=0,Zb={},$b={};A(Z,{autoActivate:!0});var _b=function(a){var b=this;b.id=""+Wb++,Xb[b.id]={instance:b,elements:[],handlers:{}},a&&b.clip(a),Vb.on("*",function(a){return b.emit(a)}),Vb.on("destroy",function(){b.destroy()}),Vb.create()},ac=function(a,d){var e,f,g,h={},i=Xb[this.id],j=i&&i.handlers;if(!i)throw new Error("Attempted to add new listener(s) to a destroyed ZeroClipboard client instance");if("string"==typeof a&&a)g=a.toLowerCase().split(/\s+/);else if("object"==typeof a&&a&&"undefined"==typeof d)for(e in a)w.call(a,e)&&"string"==typeof e&&e&&"function"==typeof a[e]&&this.on(e,a[e]);if(g&&g.length){for(e=0,f=g.length;f>e;e++)a=g[e].replace(/^on/,""),h[a]=!0,j[a]||(j[a]=[]),j[a].push(d);if(h.ready&&N.ready&&this.emit({type:"ready",client:this}),h.error){for(e=0,f=W.length;f>e;e++)if(N[W[e].replace(/^flash-/,"")]){this.emit({type:"error",name:W[e],client:this});break}c!==b&&Vb.version!==c&&this.emit({type:"error",name:"version-mismatch",jsVersion:Vb.version,swfVersion:c})}}return this},bc=function(a,b){var c,d,e,f,g,h=Xb[this.id],i=h&&h.handlers;if(!i)return this;if(0===arguments.length)f=u(i);else if("string"==typeof a&&a)f=a.split(/\s+/);else if("object"==typeof a&&a&&"undefined"==typeof b)for(c in a)w.call(a,c)&&"string"==typeof c&&c&&"function"==typeof a[c]&&this.off(c,a[c]);if(f&&f.length)for(c=0,d=f.length;d>c;c++)if(a=f[c].toLowerCase().replace(/^on/,""),g=i[a],g&&g.length)if(b)for(e=g.indexOf(b);-1!==e;)g.splice(e,1),e=g.indexOf(b,e);else g.length=0;return this},cc=function(a){var b=null,c=Xb[this.id]&&Xb[this.id].handlers;return c&&(b="string"==typeof a&&a?c[a]?c[a].slice(0):[]:B(c)),b},dc=function(a){if(ic.call(this,a)){"object"==typeof a&&a&&"string"==typeof a.type&&a.type&&(a=A({},a));var b=A({},ob(a),{client:this});jc.call(this,b)}return this},ec=function(a){if(!Xb[this.id])throw new Error("Attempted to clip element(s) to a destroyed ZeroClipboard client instance");a=kc(a);for(var b=0;b<a.length;b++)if(w.call(a,b)&&a[b]&&1===a[b].nodeType){a[b].zcClippingId?-1===Zb[a[b].zcClippingId].indexOf(this.id)&&Zb[a[b].zcClippingId].push(this.id):(a[b].zcClippingId="zcClippingId_"+Yb++,Zb[a[b].zcClippingId]=[this.id],Z.autoActivate===!0&&lc(a[b]));var c=Xb[this.id]&&Xb[this.id].elements;-1===c.indexOf(a[b])&&c.push(a[b])}return this},fc=function(a){var b=Xb[this.id];if(!b)return this;var c,d=b.elements;a="undefined"==typeof a?d.slice(0):kc(a);for(var e=a.length;e--;)if(w.call(a,e)&&a[e]&&1===a[e].nodeType){for(c=0;-1!==(c=d.indexOf(a[e],c));)d.splice(c,1);var f=Zb[a[e].zcClippingId];if(f){for(c=0;-1!==(c=f.indexOf(this.id,c));)f.splice(c,1);0===f.length&&(Z.autoActivate===!0&&mc(a[e]),delete a[e].zcClippingId)}}return this},gc=function(){var a=Xb[this.id];return a&&a.elements?a.elements.slice(0):[]},hc=function(){Xb[this.id]&&(this.unclip(),this.off(),delete Xb[this.id])},ic=function(a){if(!a||!a.type)return!1;if(a.client&&a.client!==this)return!1;var b=Xb[this.id],c=b&&b.elements,d=!!c&&c.length>0,e=!a.target||d&&-1!==c.indexOf(a.target),f=a.relatedTarget&&d&&-1!==c.indexOf(a.relatedTarget),g=a.client&&a.client===this;return b&&(e||f||g)?!0:!1},jc=function(a){var b=Xb[this.id];if("object"==typeof a&&a&&a.type&&b){var c=rb(a),d=b&&b.handlers["*"]||[],e=b&&b.handlers[a.type]||[],g=d.concat(e);if(g&&g.length){var h,i,j,k,l,m=this;for(h=0,i=g.length;i>h;h++)j=g[h],k=m,"string"==typeof j&&"function"==typeof f[j]&&(j=f[j]),"object"==typeof j&&j&&"function"==typeof j.handleEvent&&(k=j,j=j.handleEvent),"function"==typeof j&&(l=A({},a),sb(j,k,[l],c))}}},kc=function(a){return"string"==typeof a&&(a=[]),"number"!=typeof a.length?[a]:a},lc=function(a){if(a&&1===a.nodeType){var b=function(a){(a||(a=f.event))&&("js"!==a._source&&(a.stopImmediatePropagation(),a.preventDefault()),delete a._source)},c=function(c){(c||(c=f.event))&&(b(c),Vb.focus(a))};a.addEventListener("mouseover",c,!1),a.addEventListener("mouseout",b,!1),a.addEventListener("mouseenter",b,!1),a.addEventListener("mouseleave",b,!1),a.addEventListener("mousemove",b,!1),$b[a.zcClippingId]={mouseover:c,mouseout:b,mouseenter:b,mouseleave:b,mousemove:b}}},mc=function(a){if(a&&1===a.nodeType){var b=$b[a.zcClippingId];if("object"==typeof b&&b){for(var c,d,e=["move","leave","enter","out","over"],f=0,g=e.length;g>f;f++)c="mouse"+e[f],d=b[c],"function"==typeof d&&a.removeEventListener(c,d,!1);delete $b[a.zcClippingId]}}};Vb._createClient=function(){_b.apply(this,z(arguments))},Vb.prototype.on=function(){return ac.apply(this,z(arguments))},Vb.prototype.off=function(){return bc.apply(this,z(arguments))},Vb.prototype.handlers=function(){return cc.apply(this,z(arguments))},Vb.prototype.emit=function(){return dc.apply(this,z(arguments))},Vb.prototype.clip=function(){return ec.apply(this,z(arguments))},Vb.prototype.unclip=function(){return fc.apply(this,z(arguments))},Vb.prototype.elements=function(){return gc.apply(this,z(arguments))},Vb.prototype.destroy=function(){return hc.apply(this,z(arguments))},Vb.prototype.setText=function(a){if(!Xb[this.id])throw new Error("Attempted to set pending clipboard data from a destroyed ZeroClipboard client instance");return Vb.setData("text/plain",a),this},Vb.prototype.setHtml=function(a){if(!Xb[this.id])throw new Error("Attempted to set pending clipboard data from a destroyed ZeroClipboard client instance");return Vb.setData("text/html",a),this},Vb.prototype.setRichText=function(a){if(!Xb[this.id])throw new Error("Attempted to set pending clipboard data from a destroyed ZeroClipboard client instance");return Vb.setData("application/rtf",a),this},Vb.prototype.setData=function(){if(!Xb[this.id])throw new Error("Attempted to set pending clipboard data from a destroyed ZeroClipboard client instance");return Vb.setData.apply(this,z(arguments)),this},Vb.prototype.clearData=function(){if(!Xb[this.id])throw new Error("Attempted to clear pending clipboard data from a destroyed ZeroClipboard client instance");return Vb.clearData.apply(this,z(arguments)),this},Vb.prototype.getData=function(){if(!Xb[this.id])throw new Error("Attempted to get pending clipboard data from a destroyed ZeroClipboard client instance");return Vb.getData.apply(this,z(arguments))},"function"==typeof define&&define.amd?define(function(){return Vb}):"object"==typeof module&&module&&"object"==typeof module.exports&&module.exports?module.exports=Vb:a.ZeroClipboard=Vb}(function(){return this||window}());
diff --git a/extensions/BugModal/web/ZeroClipboard/ZeroClipboard.swf b/extensions/BugModal/web/ZeroClipboard/ZeroClipboard.swf
new file mode 100644
index 000000000..8bad6a3e3
--- /dev/null
+++ b/extensions/BugModal/web/ZeroClipboard/ZeroClipboard.swf
Binary files differ
diff --git a/extensions/BugModal/web/bug_modal.css b/extensions/BugModal/web/bug_modal.css
new file mode 100644
index 000000000..27a9e3f73
--- /dev/null
+++ b/extensions/BugModal/web/bug_modal.css
@@ -0,0 +1,666 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This Source Code Form is "Incompatible With Secondary Licenses", as
+ * defined by the Mozilla Public License, v. 2.0. */
+
+/* generic */
+
+.container {
+ display: table-cell;
+ width: 100%;
+}
+
+.layout-table {
+ border-spacing: 0;
+}
+
+.layout-table td {
+ padding: 0;
+}
+
+.inline {
+ display: table-cell;
+}
+
+.gravatar {
+ vertical-align: middle;
+ margin-right: 5px;
+}
+
+.flag .vcard {
+ display: inline;
+}
+
+.group-padlock {
+ vertical-align: middle;
+ margin-right: 5px;
+}
+
+button.minor {
+ background-color: #888;
+ background-image: linear-gradient(#aaa, #888);
+ font-size: 11px;
+ padding: 0.25em 0.5em;
+}
+
+button.minor:hover {
+ -webkit-box-shadow: 0 1px 0 0 rgba(0,0,0,0.2), inset 0 -1px 0 0 rgba(0,0,0,0.3), inset 0 12px 24px 2px #bbb;
+ -moz-box-shadow: 0 1px 0 0 rgba(0,0,0,0.2), inset 0 -1px 0 0 rgba(0,0,0,0.3), inset 0 12px 24px 2px #bbb;
+ box-shadow: 0 1px 0 0 rgba(0,0,0,0.2), inset 0 -1px 0 0 rgba(0,0,0,0.3), inset 0 12px 24px 2px #bbb;
+}
+
+button.in-page {
+ background-color: #fff;
+ background-image: linear-gradient(#fff, #fff);
+ color: #000;
+ border: 1px solid #ccc;
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+ font-size: 11px;
+ padding: 0.25em 0.5em;
+}
+
+button.in-page:hover {
+ -webkit-box-shadow: inset 0 12px 24px 2px #eee;
+ -moz-box-shadow: inset 0 12px 24px 2px #eee;
+ box-shadow: inset 0 12px 24px 2px #eee;
+ -moz-transition: all linear 100ms;
+ -webkit-transition: all linear 100ms;
+ transition: all linear 100ms;
+}
+
+select[multiple], .text_input, .yui-ac-input, input {
+ font-size: 12px !important;
+}
+
+.spin-toggle {
+ cursor: pointer;
+}
+
+.spin-toggle:hover {
+ text-decoration: underline;
+}
+
+.spin-latch {
+ color: #999;
+ padding-right: 5px;
+}
+
+/* modules */
+
+.module {
+ color: #000;
+ border-radius: 2px;
+ margin-top: 5px;
+ font-size: 13px;
+}
+
+.module.module-collapsed .module-content {
+ border: 1px solid red;
+}
+
+.module-header {
+ background: #eee;
+ padding: 2px 5px;
+ cursor: pointer;
+}
+
+.module-header:hover {
+ outline: 1px solid #ccc;
+}
+
+.module-latch {
+ display: table-cell;
+ padding-left: 5px;
+ padding-right: 5px;
+}
+
+.module-spinner {
+ color: #999;
+ display: table-cell;
+ width: 10px;
+}
+
+.module-title, .module-subtitle {
+ display: table-cell;
+ padding-left: 5px;
+}
+
+.module-subtitle {
+ padding-right: 5px;
+ color: #666;
+ font-size: 12px;
+}
+
+.module .fields-lhs {
+ min-width: 450px;
+ display: table-cell;
+ vertical-align: top;
+}
+
+.module .fields-rhs {
+ min-width: 450px;
+ display: table-cell;
+ vertical-align: top;
+ width: 100%;
+}
+
+.module-content {
+ padding: 2px 5px;
+ background: #fff;
+}
+
+.module .field {
+ margin-top: 4px;
+ vertical-align: top;
+}
+
+.module .field.right {
+}
+
+.module .field .name {
+ display: table-cell;
+ width: 100px;
+ min-width: 100px;
+ text-align: right;
+ vertical-align: top;
+ padding-right: 10px;
+ color: #666;
+}
+
+.module .field.inline .name {
+ min-width: 0px;
+ width: auto;
+ padding-left: 10px;
+}
+
+.module .indent {
+ padding-left: 10px;
+}
+
+.module .field .value {
+ display: table-cell;
+}
+
+.module .field .value.wide {
+ display: block;
+}
+
+.module .field .value.edit {
+ width: 100%;
+}
+
+.module .field .value input {
+ width: 100%;
+}
+
+.module .field .value input[type="checkbox"] {
+ width: auto;
+}
+
+.module .field .value.short input {
+ width: 170px;
+}
+
+/* field types */
+
+input[type="number"] {
+ text-align: right;
+ width: 5em !important;
+}
+
+.cf_date-img, .cf_datetime-img {
+ vertical-align: middle;
+}
+
+/* specific fields */
+
+#field-value-bug_id {
+ font-weight: bold;
+}
+
+#field-value-short_desc {
+ font-weight: bold;
+ font-size: 120%;
+}
+
+#status-needinfo, #status-needinfo .vcard {
+ display: inline;
+}
+
+#duplicate-container, #duplicate-actions {
+ display: table-cell;
+ vertical-align: top;
+ padding-left: 8px;
+}
+
+#dup_id {
+ margin-left: 4px;
+}
+
+#duplicate-container #dup_up {
+ padding-left: 5px;
+}
+
+#after-comment-commit-button {
+ margin-left: -8px;
+}
+
+#needinfo_from_autocomplete {
+ width: auto;
+}
+
+#needinfo_role_identity {
+ margin-left: 5px;
+}
+
+#user-story {
+ margin: 0;
+}
+
+#user-story-actions {
+ float: right;
+}
+
+#login-required {
+ padding: 20px 8px;
+ margin-bottom: 50px;
+}
+
+#product-info, #component-info {
+ color: #484;
+}
+
+#cc-latch {
+ color: #999;
+}
+
+#cc-latch, #cc-summary {
+ cursor: pointer;
+}
+
+#cc-summary:hover {
+ text-decoration: underline;
+}
+
+#cc-list {
+ max-height: 150px;
+ overflow-y: scroll;
+}
+
+/* actions */
+
+#copy-summary {
+ margin-left: 20px;
+}
+
+#top-actions {
+ margin-top: 5px;
+ padding-bottom: 20px;
+}
+
+#top-actions .save-btn {
+ float: right;
+}
+
+#bottom-actions {
+ margin-bottom: 50px;
+}
+
+#bottom-right-actions {
+ float: right;
+}
+
+.edit-textarea-set-btn {
+ float: right;
+}
+
+/* attachments */
+
+#attachments {
+ width: 100%;
+}
+
+#attachments tr:hover {
+ background-image: linear-gradient(to right, #666, rgba(0, 0, 0, 0) 1px);
+}
+
+#attachments td {
+ padding: 4px 8px;
+ vertical-align: top;
+ font-size: 13px;
+ border-bottom: 1px dotted silver;
+}
+
+#attachments .attach-desc-td {
+ width: 100%;
+}
+
+#attachments .attach-desc {
+ font-weight: bold;
+}
+
+#attachments .attach-desc a {
+ color: #000;
+}
+
+#attachments .attach-info {
+ font-size: 11px;
+}
+
+#attachments .attach-time {
+ font-size: 11px;
+}
+
+#attachments .attach-actions {
+ white-space: nowrap;
+}
+
+#attachments .attach-flag {
+ white-space: nowrap;
+}
+
+#attachments .flag-name-status {
+ font-weight: bold;
+}
+
+#attachments .attach-obsolete .attach-desc {
+ text-decoration: line-through;
+}
+
+#attachments .attach-patch {
+ background: #ffc;
+ background-image: linear-gradient(to right, #ffc, #fff);
+}
+
+#attachments .vcard {
+ display: inline;
+}
+
+#attachments-actions {
+ padding: 2px;
+}
+
+#attachments .attach-flag .vcard {
+ white-space: nowrap;
+}
+
+/* flags */
+
+.flags td {
+ font-size: 13px !important;
+}
+
+.flag-name {
+ text-align: right;
+}
+
+td.flag-name, td.flag-requestee {
+ padding-left: 5px;
+}
+
+td.flag-value select {
+ margin-left: 5px;
+}
+
+td.flag-requestee {
+ width: 100%;
+}
+
+.flags .vcard {
+ white-space: nowrap;
+}
+
+.tracking-flags td, .tracking-flags th {
+ padding: 0 5px;
+}
+
+.tracking-flags th {
+ font-weight: normal;
+ text-align: left;
+ color: #666;
+}
+
+.tracking-flag-name, .tracking-flag-tracking {
+ text-align: right;
+ white-space: nowrap;
+}
+
+/* comments and activity */
+
+.change-set {
+ clear: both;
+ -webkit-box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1);
+ -moz-box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1);
+ box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1);
+ border-bottom: 1px solid rgba(0, 0, 0, 0.2);
+ margin-top: 20px;
+ border: 1px solid #ddd;
+}
+
+.change-set:target {
+ outline: 2px solid #0095dd;
+}
+
+.change-head {
+ width: 100%;
+ background: #eee;
+}
+
+.change-gravatar {
+ padding-left: 8px !important;
+}
+
+.change-author {
+ width: 100%;
+ vertical-align: top;
+ padding-top: 4px !important;
+}
+
+.change-author .vcard {
+ display: inline;
+ font-weight: bold;
+}
+
+.change-author .user-role {
+ margin-left: 1em;
+ color: #448844;
+}
+
+
+.change-name, .change-time, .comment-private {
+ display: inline;
+}
+
+.comment-actions {
+ white-space: nowrap;
+ padding: 2px 2px 0 0 !important;
+}
+
+.comment-spinner {
+ font-family: monospace;
+ white-space: nowrap;
+ vertical-align: bottom;
+}
+
+.comment-text {
+ white-space: pre-wrap;
+ font: 13px/1.2 "Droid Sans Mono",Menlo,Monaco,"Courier New",Courier,monospace;
+ background: #fff;
+ margin: 1px 0 0 0;
+ overflow: auto;
+ padding: 8px;
+ border-top: 1px solid #ddd;
+}
+
+.comment-text span.quote, .comment-text span.quote_wrapped {
+ background: #eee !important;
+ color: #444 !important;
+ display: block !important;
+ padding: 5px !important;
+ display: inline-block !important;
+ width: 99% !important;
+}
+
+.comment-tags {
+ padding: 0 8px 2px 8px !important;
+}
+
+.comment-tag {
+ border: 1px solid #ccc;
+ padding: 2px 4px;
+ margin-right: 2px;
+ border-radius: 0.5em;
+ background-color: #fff;
+ color: #000;
+ font-size: 12px;
+}
+
+.comment-collapse-reason {
+ padding: 5px 7px !important;
+ width: 100%;
+}
+
+.default-collapsed {
+ background: inherit;
+ color: #888;
+}
+
+.default-collapsed .comment-actions {
+ padding: 2px;
+}
+
+.private-comment {
+ color: #8b0000;
+ background: #f3eeee;
+}
+
+.activity {
+ padding: 5px 8px;
+ background: #eee;
+ border-top: 1px solid #ddd;
+}
+
+.activity-deleted {
+ text-decoration: line-through;
+}
+
+.change-set .reporter {
+ background-image: linear-gradient(to right, #feb, rgba(0, 0, 0, 0) 150px);
+}
+
+.change-set .assignee {
+ background-image: linear-gradient(to right, #ffc, rgba(0, 0, 0, 0) 150px);
+}
+
+/* add comment */
+
+#add-comment {
+ margin-top: 20px;
+}
+
+#add-comment-label {
+ display: inline;
+ font-weight: bold;
+}
+
+#add-comment-private {
+ float: right;
+}
+
+#comment {
+ width: 100%;
+ box-sizing: border-box !important;
+}
+
+/* controls */
+
+#summary-container {
+ display: table-cell;
+ width: 100%;
+ vertical-align: top;
+}
+
+#xhr-error {
+ background: #fff;
+ color: #000;
+ border-radius: 2px;
+ border: 1px solid maroon;
+ margin: 5px 0px;
+ padding: 5px;
+}
+
+#mode-container {
+ display: table-cell;
+ white-space: nowrap;
+ text-align: right;
+ padding: 5px;
+ margin: 5px;
+}
+
+#mode-btn, #commit-btn {
+ margin: 0 0 5px 0;
+}
+
+#mode-btn-loading, #mode-btn-editing {
+ display: none;
+}
+
+#edit-throbber {
+ margin-right: 5px;
+}
+
+#commit {
+ margin: 5px;
+}
+
+#mode-container .button-row {
+ margin-top: 1px;
+}
+
+/* theme */
+
+#bugzilla-body {
+ margin: 5px !important;
+ max-width: 1024px !important;
+ min-width: 800px !important;
+ width: 100% !important;
+}
+
+#footer {
+ max-width: 1024px !important;
+ min-width: 800px !important;
+}
+
+.vcard {
+ white-space: normal;
+}
+
+.vcard a.disabled {
+ color: #888;
+}
+
+.xdsoft_datetimepicker button, .xdsoft_datetimepicker button:hover {
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+}
+
+div.ui-widget-content {
+ background: #fff;
+}
+
+div.ui-tooltip {
+ padding: 4px;
+ font-size: 13px;
+ font-family: inherit;
+ max-width: 500px;
+}
+
+.yui-ac {
+ width: 100%;
+}
+
diff --git a/extensions/BugModal/web/bug_modal.js b/extensions/BugModal/web/bug_modal.js
new file mode 100644
index 000000000..c3fd84e97
--- /dev/null
+++ b/extensions/BugModal/web/bug_modal.js
@@ -0,0 +1,730 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This Source Code Form is "Incompatible With Secondary Licenses", as
+ * defined by the Mozilla Public License, v. 2.0. */
+
+$(function() {
+ 'use strict';
+
+ // all keywords for autocompletion (lazy-loaded on edit)
+ var keywords = [];
+
+ // products with descriptions (also lazy-loaded)
+ var products = [];
+
+ // scroll to an element
+ function scroll_to(el, complete) {
+ var offset = el.offset();
+ $('html, body')
+ .animate({
+ scrollTop: offset.top - 20,
+ scrollLeft: offset.left = 20
+ },
+ 200,
+ complete
+ );
+ }
+
+ // expand all modules
+ $('#expand-all-btn')
+ .click(function(event) {
+ event.preventDefault();
+ var btn = $(event.target);
+ if (btn.data('expanded-modules')) {
+ btn.data('expanded-modules').slideToggle(200, 'swing', function() {
+ btn.data('expanded-spinners').html('&#9656;');
+ });
+ btn.data('expanded-modules', false);
+ btn.text('Expand All');
+ }
+ else {
+ var modules = $('.module-content:hidden');
+ var spinners = $([]);
+ modules.each(function() {
+ spinners.push($(this).parent('.module').find('.module-spinner')[0]);
+ });
+ btn.data('expanded-modules', modules);
+ btn.data('expanded-spinners', spinners);
+ modules.slideToggle(200, 'swing', function() {
+ spinners.html('&#9662;');
+ });
+ btn.text('Collapse');
+ }
+ });
+
+ // expand/colapse module
+ $('.module-header')
+ .click(function(event) {
+ event.preventDefault();
+ var target = $(event.target);
+ var latch = target.hasClass('module-header') ? target.children('.module-latch') : target.parent('.module-latch');
+ var spinner = $(latch.children('.module-spinner')[0]);
+ var module = $(latch.parents('.module')[0]);
+ var content = $(module.children('.module-content')[0]);
+ content.slideToggle(200, 'swing', function() {
+ spinner.html(content.is(':visible') ? '&#9662;' : '&#9656;');
+ });
+ });
+
+ // toggle obsolete attachments
+ $('#attachments-obsolete-btn')
+ .click(function(event) {
+ event.preventDefault();
+ $(event.target).text(($('#attachments tr:hidden').length ? 'Hide' : 'Show') + ' Obsolete Attachments');
+ $('#attachments tr.attach-obsolete').toggle();
+ });
+
+ // comment collapse/expand
+ $('.comment-spinner')
+ .click(function(event) {
+ event.preventDefault();
+ var spinner = $(event.target);
+ var id = spinner.attr('id').match(/\d+$/)[0];
+ // switch to full header for initially collapsed comments
+ if (spinner.attr('id').match(/^ccs-/)) {
+ $('#cc-' + id).hide();
+ $('#ch-' + id).show();
+ }
+ $('#ct-' + id).slideToggle('fast', function() {
+ $('#c' + id).find('.activity').toggle();
+ spinner.text($('#ct-' + id + ':visible').length ? '-' : '+');
+ });
+ });
+
+ // url --> unsafe warning
+ $('.unsafe-url')
+ .click(function(event) {
+ event.preventDefault();
+ if (confirm('This is considered an unsafe URL and could possibly be harmful. ' +
+ 'The full URL is:\n\n' + $(event.target).attr('title') + '\n\nContinue?'))
+ {
+ try {
+ window.open($(event.target).attr('title'));
+ } catch(ex) {
+ alert('Malformed URL');
+ }
+ }
+ });
+
+ // last comment btn
+ $('#last-comment-btn')
+ .click(function(event) {
+ event.preventDefault();
+ var id = $('.comment:last')[0].parentNode.id;
+ scroll_to($('#' + id));
+ window.location.hash = id;
+ });
+
+ // top btn
+ $('#top-btn')
+ .click(function(event) {
+ event.preventDefault();
+ scroll_to($('body'));
+ });
+
+ // use non-native tooltips for relative times and bug summaries
+ $('.rel-time, .bz_bug_link').tooltip({
+ position: { my: "left top+8", at: "left bottom", collision: "flipfit" },
+ show: { effect: 'none' },
+ hide: { effect: 'none' }
+ });
+
+ // tooltips create a new ui-helper-hidden-accessible div each time a
+ // tooltip is shown. this is never removed leading to memory leak and
+ // bloated dom. http://bugs.jqueryui.com/ticket/10689
+ $('.ui-helper-hidden-accessible').remove();
+
+ // product/component info
+ $('.spin-toggle')
+ .click(function(event) {
+ event.preventDefault();
+ var latch = $($(event.target).data('latch'));
+ var el_for = $($(event.target).data('for'));
+
+ if (latch.data('expanded')) {
+ latch.data('expanded', false).html('&#9656;');
+ el_for.hide();
+ }
+ else {
+ latch.data('expanded', true).html('&#9662;');
+ el_for.show();
+ }
+ });
+
+ // cc list
+ $('#cc-latch, #cc-summary')
+ .click(function(event) {
+ event.preventDefault();
+ var latch = $('#cc-latch');
+
+ if (latch.data('expanded')) {
+ latch.data('expanded', false).html('&#9656;');
+ $('#cc-list').hide();
+ }
+ else {
+ latch.data('expanded', true).html('&#9662;');
+ $('#cc-list').show();
+ if (!latch.data('fetched')) {
+ $('#cc-list').html(
+ '<img src="extensions/BugModal/web/throbber.gif" width="16" height="11"> Loading...'
+ );
+ bugzilla_ajax(
+ {
+ url: 'rest/bug_modal/cc/' + BUGZILLA.bug_id
+ },
+ function(data) {
+ $('#cc-list').html(data.html);
+ latch.data('fetched', true);
+ }
+ );
+ }
+ }
+ });
+
+ // copy summary to clipboard
+ if ($('#copy-summary').length) {
+ var zero = new ZeroClipboard($('#copy-summary'));
+ zero.on({
+ 'error': function(event) {
+ console.log(event.message);
+ zero.destroy();
+ $('#copy-summary').hide();
+
+ },
+ 'copy': function(event) {
+ var clipboard = event.clipboardData;
+ clipboard.setData('text/plain', 'Bug ' + BUGZILLA.bug_id + ' - ' + $('#field-value-short_desc').text());
+ }
+ });
+ }
+
+ //
+ // anything after this point is only executed for logged in users
+ //
+
+ if (BUGZILLA.user.id === 0) return;
+
+ // edit/save mode button
+ $('#mode-btn')
+ .click(function(event) {
+ event.preventDefault();
+
+ // hide buttons, old error messages
+ $('#mode-btn-readonly').hide();
+
+ // toggle visibility
+ $('.edit-hide').hide();
+ $('.edit-show').show();
+
+ // expand specific modules
+ $('#module-details .module-header').each(function() {
+ if ($(this.parentNode).find('.module-content:visible').length === 0) {
+ $(this).click();
+ }
+ });
+
+ // if there's no current user-story, it's a better experience if it's editable by default
+ if ($('#cf_user_story').val() === '') {
+ $('#user-story-edit-btn').click();
+ }
+
+ // "loading.." ui
+ $('#mode-btn-loading').show();
+ $('#cancel-btn').prop('disabled', true);
+ $('#mode-btn').prop('disabled', true);
+
+ // load the missing select data
+ bugzilla_ajax(
+ {
+ url: 'rest/bug_modal/edit/' + BUGZILLA.bug_id
+ },
+ function(data) {
+ $('#mode-btn').hide();
+
+ // populate select menus
+ $.each(data.options, function(key, value) {
+ var el = $('#' + key);
+ if (!el) return;
+ var selected = el.val();
+ el.empty();
+ $(value).each(function(i, v) {
+ el.append($('<option>', { value: v.name, text: v.name }));
+ });
+ el.val(selected);
+ if (el.attr('multiple') && value.length < 5) {
+ el.attr('size', value.length);
+ }
+ });
+
+ // build our product description hash
+ $.each(data.options.product, function() {
+ products[this.name] = this.description;
+ });
+
+ // keywords is a multi-value autocomplete
+ // (this should probably be a simple jquery plugin)
+ keywords = data.keywords;
+ $('#keywords')
+ .bind('keydown', function(event) {
+ if (event.keyCode == $.ui.keyCode.TAB && $(this).autocomplete('instance').menu.active)
+ {
+ event.preventDefault();
+ }
+ })
+ .blur(function() {
+ $(this).val($(this).val().replace(/,\s*$/, ''));
+ })
+ .autocomplete({
+ source: function(request, response) {
+ response($.ui.autocomplete.filter(keywords, request.term.split(/,\s*/).pop()));
+ },
+ focus: function() {
+ return false;
+ },
+ select: function(event, ui) {
+ var terms = this.value.split(/,\s*/);
+ terms.pop();
+ terms.push(ui.item.value);
+ terms.push('');
+ this.value = terms.join(', ');
+ return false;
+ }
+ });
+
+ $('#cancel-btn').prop('disabled', false);
+ $('#top-save-btn').show();
+ $('#cancel-btn').show();
+ $('#commit-btn').show();
+ },
+ function() {
+ $('#mode-btn-readonly').show();
+ $('#mode-btn-loading').hide();
+ $('#mode-btn').prop('disabled', false);
+ $('#mode-btn').show();
+ $('#cancel-btn').hide();
+ $('#commit-btn').hide();
+
+ $('.edit-show').hide();
+ $('.edit-hide').show();
+ }
+ );
+ });
+ $('#mode-btn').prop('disabled', false);
+
+ // cc add/remove
+ $('#cc-btn')
+ .click(function(event) {
+ event.preventDefault();
+ var is_cced = $(event.target).data('is-cced') == '1';
+
+ var cc_change;
+ if (is_cced) {
+ cc_change = { remove: [ BUGZILLA.user.login ] };
+ $('#cc-btn')
+ .text('Follow')
+ .data('is-cced', '0')
+ .prop('disabled', true);
+ }
+ else {
+ cc_change = { add: [ BUGZILLA.user.login ] };
+ $('#cc-btn')
+ .text('Stop following')
+ .data('is-cced', '1')
+ .prop('disabled', true);
+ }
+
+ bugzilla_ajax(
+ {
+ url: 'rest/bug/' + BUGZILLA.bug_id,
+ type: 'PUT',
+ data: JSON.stringify({ cc: cc_change })
+ },
+ function(data) {
+ $('#cc-btn').prop('disabled', false);
+ if (!(data.bugs[0].changes && data.bugs[0].changes.cc))
+ return;
+ if (data.bugs[0].changes.cc.added == BUGZILLA.user.login) {
+ $('#cc-btn')
+ .text('Stop following')
+ .data('is-cced', '1');
+ }
+ else if (data.bugs[0].changes.cc.removed == BUGZILLA.user.login) {
+ $('#cc-btn')
+ .text('Follow')
+ .data('is-cced', '0');
+ }
+ },
+ function(message) {
+ $('#cc-btn').prop('disabled', false);
+ }
+ );
+
+ });
+
+ // cancel button, reset the ui back to read-only state
+ // for now, do this with a redirect to self
+ // ideally this should revert all field back to their initially loaded
+ // values and switch the ui back to read-only mode without the redirect
+ $('#cancel-btn')
+ .click(function(event) {
+ event.preventDefault();
+ window.location.replace($('#this-bug').val());
+ });
+
+ // top comment button, scroll the textarea into view
+ $('.comment-btn')
+ .click(function(event) {
+ event.preventDefault();
+ // focus first to grow the textarea, so we scroll to the correct location
+ $('#comment').focus();
+ scroll_to($('#bottom-save-btn'));
+ });
+
+ // needinfo in people section -> scroll to near-comment ui
+ $('#needinfo-scroll')
+ .click(function(event) {
+ event.preventDefault();
+ scroll_to($('#needinfo_role'), function() { $('#needinfo_role').focus(); });
+ });
+
+ // knob
+ $('#bug_status')
+ .change(function(event) {
+ if (event.target.value == "RESOLVED" || event.target.value == "VERIFIED") {
+ $('#resolution').change().show();
+ }
+ else {
+ $('#resolution').hide();
+ $('#duplicate-container').hide();
+ $('#mark-as-dup-btn').show();
+ }
+ })
+ .change();
+ $('#resolution')
+ .change(function(event) {
+ if (event.target.value == "DUPLICATE") {
+ $('#duplicate-container').show();
+ $('#mark-as-dup-btn').hide();
+ $('#dup_id').focus();
+ }
+ else {
+ $('#duplicate-container').hide();
+ $('#mark-as-dup-btn').show();
+ }
+ })
+ .change();
+ $('#mark-as-dup-btn')
+ .click(function(event) {
+ event.preventDefault();
+ $('#bug_status').val('RESOLVED').change();
+ $('#resolution').val('DUPLICATE').change();
+ $('#dup_id').focus();
+ });
+
+ // add see-also button
+ $('.bug-urls-btn')
+ .click(function(event) {
+ event.preventDefault();
+ var name = event.target.id.replace(/-btn$/, '');
+ $(event.target).hide();
+ $('#' + name).show().focus();
+ });
+
+ // bug flag value <select>
+ $('.bug-flag')
+ .change(function(event) {
+ var target = $(event.target);
+ var id = target.prop('id').replace(/^flag(_type)?-(\d+)/, "#requestee$1-$2");
+ if (target.val() == '?') {
+ $(id + '-container').show();
+ $(id).focus().select();
+ }
+ else {
+ $(id + '-container').hide();
+ }
+ });
+
+ // tracking flags
+ $('.tracking-flags select')
+ .change(function(event) {
+ tracking_flag_change(event.target);
+ });
+
+ // add attachments
+ $('#attachments-add-btn')
+ .click(function(event) {
+ event.preventDefault();
+ window.location.replace('attachment.cgi?bugid=' + BUGZILLA.bug_id + '&action=enter');
+ });
+
+ // take button
+ $('#take-btn')
+ .click(function(event) {
+ event.preventDefault();
+ $('#field-assigned_to .edit-hide').hide();
+ $('#field-assigned_to .edit-show').show();
+ $('#assigned_to').val(BUGZILLA.user.login).focus().select();
+ $('#top-save-btn').show();
+ });
+
+ // reply button
+ $('.reply-btn')
+ .click(function(event) {
+ event.preventDefault();
+ var comment_id = $(event.target).data('reply-id');
+ var comment_author = $(event.target).data('reply-name');
+
+ var prefix = "(In reply to " + comment_author + " from comment #" + comment_id + ")\n";
+ var reply_text = "";
+ if (BUGZILLA.user.settings.quote_replies == 'quoted_reply') {
+ var text = $('#ct-' + comment_id).text();
+ reply_text = prefix + wrapReplyText(text);
+ }
+ else if (BUGZILLA.user.settings.quote_replies == 'simply_reply') {
+ reply_text = prefix;
+ }
+
+ // quoting a private comment, check the 'private' cb
+ $('#add-comment-private-cb').prop('checked',
+ $('#add-comment-private-cb:checked').length || $('#is-private-' + comment_id + ':checked').length);
+
+ // remove embedded links to attachment details
+ reply_text = reply_text.replace(/(attachment\s+\d+)(\s+\[[^\[\n]+\])+/gi, '$1');
+
+ if ($('#comment').val() != reply_text) {
+ $('#comment').val($('#comment').val() + reply_text);
+ }
+ scroll_to($('#comment'), function() { $('#comment').focus(); });
+ });
+
+ // add comment --> enlarge on focus
+ if (BUGZILLA.user.settings.zoom_textareas) {
+ $('#comment')
+ .focus(function(event) {
+ $(event.target).attr('rows', 25);
+ });
+ }
+
+ // add comment --> private
+ $('#add-comment-private-cb')
+ .click(function(event) {
+ if ($(event.target).prop('checked')) {
+ $('#comment').addClass('private-comment');
+ }
+ else {
+ $('#comment').removeClass('private-comment');
+ }
+ });
+
+ // show "save changes" button if there are any immediately editable elements
+ if ($('.module select:visible').length || $('.module input:visible').length) {
+ $('#top-save-btn').show();
+ }
+
+ // status/resolve as buttons
+ $('.resolution-btn')
+ .click(function(event) {
+ event.preventDefault();
+ $('#field-status-view').hide();
+ $('#field-status-edit .edit-hide').hide();
+ $('#field-status-edit .edit-show').show();
+ $('#field-status-edit').show();
+ $('#bug_status').val('RESOLVED').change();
+ $('#resolution').val($(event.target).text()).change();
+ $('#top-save-btn').show();
+ if ($(event.target).text() == "DUPLICATE") {
+ scroll_to($('body'));
+ }
+ else {
+ scroll_to($('body'), function() { $('#resolution').focus(); });
+ }
+ });
+ $('.status-btn')
+ .click(function(event) {
+ event.preventDefault();
+ $('#field-status-view').hide();
+ $('#field-status-edit .edit-hide').hide();
+ $('#field-status-edit .edit-show').show();
+ $('#field-status-edit').show();
+ $('#bug_status').val($(event.target).data('status')).change();
+ $('#top-save-btn').show();
+ scroll_to($('body'), function() { $('#bug_status').focus(); });
+ });
+
+ // vote button
+ // ideally this should function like CC and xhr it, but that would require
+ // a rewrite of the voting extension
+ $('#vote-btn')
+ .click(function(event) {
+ event.preventDefault();
+ window.location.replace('page.cgi?id=voting/user.html&bug_id=' + BUGZILLA.bug_id + '#vote_' + BUGZILLA.bug_id);
+ });
+
+ // user-story
+ $('#user-story-edit-btn')
+ .click(function(event) {
+ event.preventDefault();
+ $('#user-story').hide();
+ $('#user-story-edit-btn').hide();
+ $('#cf_user_story').show().focus().select();
+ $('#top-save-btn').show();
+ });
+ $('#user-story-reply-btn')
+ .click(function(event) {
+ event.preventDefault();
+ var text = "(Commenting on User Story)\n" + wrapReplyText($('#cf_user_story').val());
+ var current = $('#comment').val();
+ if (current != text) {
+ $('#comment').val(current + text);
+ $('#comment').focus();
+ scroll_to($('#bottom-save-btn'));
+ }
+ });
+
+ // custom textarea fields
+ $('.edit-textarea-btn')
+ .click(function(event) {
+ event.preventDefault();
+ var id = $(event.target).attr('id').replace(/-edit$/, '');
+ $(event.target).hide();
+ $('#' + id + '-view').hide();
+ $('#' + id).show().focus().select();
+ });
+
+ // date/datetime pickers
+ $('.cf_datetime').datetimepicker({
+ format: 'Y-m-d G:i:s',
+ datepicker: true,
+ timepicker: true,
+ scrollInput: false,
+ lazyInit: false, // there's a bug which prevents img->show from working with lazy:true
+ closeOnDateSelect: true
+ });
+ $('.cf_date').datetimepicker({
+ format: 'Y-m-d',
+ datepicker: true,
+ timepicker: false,
+ scrollInput: false,
+ lazyInit: false,
+ closeOnDateSelect: true
+ });
+ $('.cf_datetime-img, .cf_date-img')
+ .click(function(event) {
+ var id = $(event.target).attr('id').replace(/-img$/, '');
+ $('#' + id).datetimepicker('show');
+ });
+
+ // new bug button
+ $.contextMenu({
+ selector: '#new-bug-btn',
+ trigger: 'left',
+ items: [
+ {
+ name: 'Create a new Bug',
+ callback: function() {
+ window.open('enter_bug.cgi', '_blank');
+ }
+ },
+ {
+ name: '&hellip; in this product',
+ callback: function() {
+ window.open('enter_bug.cgi?product=' + encodeURIComponent($('#product').val()), '_blank');
+ }
+ },
+ {
+ name: '&hellip; in this component',
+ callback: function() {
+ window.open('enter_bug.cgi?' +
+ 'product=' + encodeURIComponent($('#product').val()) +
+ '&component=' + encodeURIComponent($('#component').val()), '_blank');
+ }
+ },
+ {
+ name: '&hellip; that blocks this bug',
+ callback: function() {
+ window.open('enter_bug.cgi?format=__default__' +
+ '&product=' + encodeURIComponent($('#product').val()) +
+ '&blocked=' + BUGZILLA.bug_id, '_blank');
+ }
+ },
+ {
+ name: '&hellip; that depends on this bug',
+ callback: function() {
+ window.open('enter_bug.cgi?format=__default__' +
+ '&product=' + encodeURIComponent($('#product').val()) +
+ '&dependson=' + BUGZILLA.bug_id, '_blank');
+ }
+ },
+ {
+ name: '&hellip; as a clone of this bug',
+ callback: function() {
+ window.open('enter_bug.cgi?format=__default__' +
+ '&product=' + encodeURIComponent($('#product').val()) +
+ '&cloned_bug_id=' + BUGZILLA.bug_id, '_blank');
+ }
+ },
+ {
+ name: '&hellip; as a clone, in a different product',
+ callback: function() {
+ window.open('enter_bug.cgi?format=__default__' +
+ '&cloned_bug_id=' + BUGZILLA.bug_id, '_blank');
+ }
+ },
+ ]
+ });
+
+});
+
+function confirmUnsafeURL(url) {
+ return confirm(
+ 'This is considered an unsafe URL and could possibly be harmful.\n' +
+ 'The full URL is:\n\n' + url + '\n\nContinue?');
+}
+
+// fix url after bug creation/update
+if (history && history.replaceState) {
+ var href = document.location.href;
+ if (!href.match(/show_bug\.cgi/)) {
+ history.replaceState(null, BUGZILLA.bug_title, 'show_bug.cgi?id=' + BUGZILLA.bug_id);
+ document.title = BUGZILLA.bug_title;
+ }
+ if (href.match(/show_bug\.cgi\?.*list_id=/)) {
+ href = href.replace(/[\?&]+list_id=(\d+|cookie)/, '');
+ history.replaceState(null, BUGZILLA.bug_title, href);
+ }
+}
+
+// ajax wrapper, to simplify error handling and auth
+function bugzilla_ajax(request, done_fn, error_fn) {
+ $('#xhr-error').hide('');
+ $('#xhr-error').html('');
+ request.url += (request.url.match('\\?') ? '&' : '?') +
+ 'Bugzilla_api_token=' + encodeURIComponent(BUGZILLA.api_token);
+ if (request.type != 'GET') {
+ request.contentType = 'application/json';
+ }
+ $.ajax(request)
+ .done(function(data) {
+ if (data.error) {
+ $('#xhr-error').html(data.message);
+ $('#xhr-error').show('fast');
+ if (error_fn)
+ error_fn(data.message);
+ }
+ else if (done_fn) {
+ done_fn(data);
+ }
+ })
+ .error(function(data) {
+ $('#xhr-error').html(data.responseJSON.message);
+ $('#xhr-error').show('fast');
+ if (error_fn)
+ error_fn(data.responseJSON.message);
+ });
+}
+
+// no-ops
+function initHidingOptionsForIE() {}
+function showFieldWhen() {}
diff --git a/extensions/BugModal/web/calendar.png b/extensions/BugModal/web/calendar.png
new file mode 100644
index 000000000..5eef6597a
--- /dev/null
+++ b/extensions/BugModal/web/calendar.png
Binary files differ
diff --git a/extensions/BugModal/web/throbber.gif b/extensions/BugModal/web/throbber.gif
new file mode 100644
index 000000000..bc4fa6561
--- /dev/null
+++ b/extensions/BugModal/web/throbber.gif
Binary files differ
diff --git a/extensions/TrackingFlags/web/js/tracking_flags.js b/extensions/TrackingFlags/web/js/tracking_flags.js
index b0bdb2ebd..0b899676c 100644
--- a/extensions/TrackingFlags/web/js/tracking_flags.js
+++ b/extensions/TrackingFlags/web/js/tracking_flags.js
@@ -55,16 +55,18 @@ function tracking_flag_change(e) {
// create "comment required"
var span = document.createElement('span');
span.id = 'cr_' + e.id;
- span.appendChild(document.createTextNode('('));
+ span.appendChild(document.createTextNode(' ('));
var a = document.createElement('a');
a.appendChild(document.createTextNode('comment required'));
a.href = '#';
- a.onclick = function() {
+ a.onclick = function(event) {
+ event.preventDefault();
var c = document.getElementById('comment');
c.focus();
c.select();
- document.getElementById('add_comment').scrollIntoView();
- return false;
+ var btn = document.getElementById('add_comment') || document.getElementById('add-comment');
+ if (btn)
+ btn.scrollIntoView();
};
span.appendChild(a);
span.appendChild(document.createTextNode(')'));
diff --git a/js/jquery/plugins/datetimepicker/datetimepicker-min.js b/js/jquery/plugins/datetimepicker/datetimepicker-min.js
new file mode 100644
index 000000000..879077e59
--- /dev/null
+++ b/js/jquery/plugins/datetimepicker/datetimepicker-min.js
@@ -0,0 +1,33 @@
+/**
+ * @preserve jQuery DateTimePicker plugin v2.4.1
+ * @homepage http://xdsoft.net/jqplugins/datetimepicker/
+ * (c) 2014, Chupurnov Valeriy.
+ */
+/* Copyright (c) 2013 Brandon Aaron (http://brandon.aaron.sh)
+ * Licensed under the MIT License (LICENSE.txt).
+ * Version: 3.1.12
+ * Requires: jQuery 1.2.2+
+ */
+// Parse and Format Library
+//http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
+/*
+ * Copyright (C) 2004 Baron Schwartz <baron at sequent dot org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, version 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ */
+(function(a){var b={i18n:{ar:{months:["كانون الثاني","شباط","آذار","نيسان","مايو","حزيران","تموز","آب","أيلول","تشرين الأول","تشرين الثاني","كانون الأول"],dayOfWeek:["ن","ث","ع","خ","ج","س","ح"]},ro:{months:["ianuarie","februarie","martie","aprilie","mai","iunie","iulie","august","septembrie","octombrie","noiembrie","decembrie"],dayOfWeek:["l","ma","mi","j","v","s","d"]},id:{months:["Januari","Februari","Maret","April","Mei","Juni","Juli","Agustus","September","Oktober","November","Desember"],dayOfWeek:["Sen","Sel","Rab","Kam","Jum","Sab","Min"]},bg:{months:["Януари","Февруари","Март","Април","Май","Юни","Юли","Август","Септември","Октомври","Ноември","Декември"],dayOfWeek:["Нд","Пн","Вт","Ср","Чт","Пт","Сб"]},fa:{months:["فروردین","اردیبهشت","خرداد","تیر","مرداد","شهریور","مهر","آبان","آذر","دی","بهمن","اسفند"],dayOfWeek:["یکشنبه","دوشنبه","سه شنبه","چهارشنبه","پنجشنبه","جمعه","شنبه"]},ru:{months:["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"],dayOfWeek:["Вск","Пн","Вт","Ср","Чт","Пт","Сб"]},uk:{months:["Січень","Лютий","Березень","Квітень","Травень","Червень","Липень","Серпень","Вересень","Жовтень","Листопад","Грудень"],dayOfWeek:["Ндл","Пнд","Втр","Срд","Чтв","Птн","Сбт"]},en:{months:["January","February","March","April","May","June","July","August","September","October","November","December"],dayOfWeek:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]},el:{months:["Ιανουάριος","Φεβρουάριος","Μάρτιος","Απρίλιος","Μάιος","Ιούνιος","Ιούλιος","Αύγουστος","Σεπτέμβριος","Οκτώβριος","Νοέμβριος","Δεκέμβριος"],dayOfWeek:["Κυρ","Δευ","Τρι","Τετ","Πεμ","Παρ","Σαβ"]},de:{months:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],dayOfWeek:["So","Mo","Di","Mi","Do","Fr","Sa"]},nl:{months:["januari","februari","maart","april","mei","juni","juli","augustus","september","oktober","november","december"],dayOfWeek:["zo","ma","di","wo","do","vr","za"]},tr:{months:["Ocak","Şubat","Mart","Nisan","Mayıs","Haziran","Temmuz","Ağustos","Eylül","Ekim","Kasım","Aralık"],dayOfWeek:["Paz","Pts","Sal","Çar","Per","Cum","Cts"]},fr:{months:["Janvier","Février","Mars","Avril","Mai","Juin","Juillet","Août","Septembre","Octobre","Novembre","Décembre"],dayOfWeek:["Dim","Lun","Mar","Mer","Jeu","Ven","Sam"]},es:{months:["Enero","Febrero","Marzo","Abril","Mayo","Junio","Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"],dayOfWeek:["Dom","Lun","Mar","Mié","Jue","Vie","Sáb"]},th:{months:["มกราคม","กุมภาพันธ์","มีนาคม","เมษายน","พฤษภาคม","มิถุนายน","กรกฎาคม","สิงหาคม","กันยายน","ตุลาคม","พฤศจิกายน","ธันวาคม"],dayOfWeek:["อา.","จ.","อ.","พ.","พฤ.","ศ.","ส."]},pl:{months:["styczeń","luty","marzec","kwiecień","maj","czerwiec","lipiec","sierpień","wrzesień","październik","listopad","grudzień"],dayOfWeek:["nd","pn","wt","śr","cz","pt","sb"]},pt:{months:["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"],dayOfWeek:["Dom","Seg","Ter","Qua","Qui","Sex","Sab"]},ch:{months:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],dayOfWeek:["日","一","二","三","四","五","六"]},se:{months:["Januari","Februari","Mars","April","Maj","Juni","Juli","Augusti","September","Oktober","November","December"],dayOfWeek:["Sön","Mån","Tis","Ons","Tor","Fre","Lör"]},kr:{months:["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"],dayOfWeek:["일","월","화","수","목","금","토"]},it:{months:["Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno","Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre"],dayOfWeek:["Dom","Lun","Mar","Mer","Gio","Ven","Sab"]},da:{months:["January","Februar","Marts","April","Maj","Juni","July","August","September","Oktober","November","December"],dayOfWeek:["Søn","Man","Tir","Ons","Tor","Fre","Lør"]},no:{months:["Januar","Februar","Mars","April","Mai","Juni","Juli","August","September","Oktober","November","Desember"],dayOfWeek:["Søn","Man","Tir","Ons","Tor","Fre","Lør"]},ja:{months:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],dayOfWeek:["日","月","火","水","木","金","土"]},vi:{months:["Tháng 1","Tháng 2","Tháng 3","Tháng 4","Tháng 5","Tháng 6","Tháng 7","Tháng 8","Tháng 9","Tháng 10","Tháng 11","Tháng 12"],dayOfWeek:["CN","T2","T3","T4","T5","T6","T7"]},sl:{months:["Januar","Februar","Marec","April","Maj","Junij","Julij","Avgust","September","Oktober","November","December"],dayOfWeek:["Ned","Pon","Tor","Sre","Čet","Pet","Sob"]},cs:{months:["Leden","Únor","Březen","Duben","Květen","Červen","Červenec","Srpen","Září","Říjen","Listopad","Prosinec"],dayOfWeek:["Ne","Po","Út","St","Čt","Pá","So"]},hu:{months:["Január","Február","Március","Április","Május","Június","Július","Augusztus","Szeptember","Október","November","December"],dayOfWeek:["Va","Hé","Ke","Sze","Cs","Pé","Szo"]},az:{months:["Yanvar","Fevral","Mart","Aprel","May","Iyun","Iyul","Avqust","Sentyabr","Oktyabr","Noyabr","Dekabr"],dayOfWeek:["B","Be","Ça","Ç","Ca","C","Ş"]},bs:{months:["Januar","Februar","Mart","April","Maj","Jun","Jul","Avgust","Septembar","Oktobar","Novembar","Decembar"],dayOfWeek:["Ned","Pon","Uto","Sri","Čet","Pet","Sub"]},ca:{months:["Gener","Febrer","Març","Abril","Maig","Juny","Juliol","Agost","Setembre","Octubre","Novembre","Desembre"],dayOfWeek:["Dg","Dl","Dt","Dc","Dj","Dv","Ds"]},"en-GB":{months:["January","February","March","April","May","June","July","August","September","October","November","December"],dayOfWeek:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]},et:{months:["Jaanuar","Veebruar","Märts","Aprill","Mai","Juuni","Juuli","August","September","Oktoober","November","Detsember"],dayOfWeek:["P","E","T","K","N","R","L"]},eu:{months:["Urtarrila","Otsaila","Martxoa","Apirila","Maiatza","Ekaina","Uztaila","Abuztua","Iraila","Urria","Azaroa","Abendua"],dayOfWeek:["Ig.","Al.","Ar.","Az.","Og.","Or.","La."]},fi:{months:["Tammikuu","Helmikuu","Maaliskuu","Huhtikuu","Toukokuu","Kesäkuu","Heinäkuu","Elokuu","Syyskuu","Lokakuu","Marraskuu","Joulukuu"],dayOfWeek:["Su","Ma","Ti","Ke","To","Pe","La"]},gl:{months:["Xan","Feb","Maz","Abr","Mai","Xun","Xul","Ago","Set","Out","Nov","Dec"],dayOfWeek:["Dom","Lun","Mar","Mer","Xov","Ven","Sab"]},hr:{months:["Siječanj","Veljača","Ožujak","Travanj","Svibanj","Lipanj","Srpanj","Kolovoz","Rujan","Listopad","Studeni","Prosinac"],dayOfWeek:["Ned","Pon","Uto","Sri","Čet","Pet","Sub"]},ko:{months:["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"],dayOfWeek:["일","월","화","수","목","금","토"]},lt:{months:["Sausio","Vasario","Kovo","Balandžio","Gegužės","Birželio","Liepos","Rugpjūčio","Rugsėjo","Spalio","Lapkričio","Gruodžio"],dayOfWeek:["Sek","Pir","Ant","Tre","Ket","Pen","Šeš"]},lv:{months:["Janvāris","Februāris","Marts","Aprīlis ","Maijs","Jūnijs","Jūlijs","Augusts","Septembris","Oktobris","Novembris","Decembris"],dayOfWeek:["Sv","Pr","Ot","Tr","Ct","Pk","St"]},mk:{months:["јануари","февруари","март","април","мај","јуни","јули","август","септември","октомври","ноември","декември"],dayOfWeek:["нед","пон","вто","сре","чет","пет","саб"]},mn:{months:["1-р сар","2-р сар","3-р сар","4-р сар","5-р сар","6-р сар","7-р сар","8-р сар","9-р сар","10-р сар","11-р сар","12-р сар"],dayOfWeek:["Дав","Мяг","Лха","Пүр","Бсн","Бям","Ням"]},"pt-BR":{months:["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"],dayOfWeek:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"]},sk:{months:["Január","Február","Marec","Apríl","Máj","Jún","Júl","August","September","Október","November","December"],dayOfWeek:["Ne","Po","Ut","St","Št","Pi","So"]},sq:{months:["January","February","March","April","May","June","July","August","September","October","November","December"],dayOfWeek:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]},"sr-YU":{months:["Januar","Februar","Mart","April","Maj","Jun","Jul","Avgust","Septembar","Oktobar","Novembar","Decembar"],dayOfWeek:["Ned","Pon","Uto","Sre","čet","Pet","Sub"]},sr:{months:["јануар","фебруар","март","април","мај","јун","јул","август","септембар","октобар","новембар","децембар"],dayOfWeek:["нед","пон","уто","сре","чет","пет","суб"]},sv:{months:["Januari","Februari","Mars","April","Maj","Juni","Juli","Augusti","September","Oktober","November","December"],dayOfWeek:["Sön","Mån","Tis","Ons","Tor","Fre","Lör"]},"zh-TW":{months:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],dayOfWeek:["日","一","二","三","四","五","六"]},zh:{months:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],dayOfWeek:["日","一","二","三","四","五","六"]},he:{months:["ינואר","פברואר","מרץ","אפריל","מאי","יוני","יולי","אוגוסט","ספטמבר","אוקטובר","נובמבר","דצמבר"],dayOfWeek:["א'","ב'","ג'","ד'","ה'","ו'","שבת"]},hy:{months:["Հունվար","Փետրվար","Մարտ","Ապրիլ","Մայիս","Հունիս","Հուլիս","Օգոստոս","Սեպտեմբեր","Հոկտեմբեր","Նոյեմբեր","Դեկտեմբեր"],dayOfWeek:["Կի","Երկ","Երք","Չոր","Հնգ","Ուրբ","Շբթ"]}},value:"",lang:"en",format:"Y/m/d H:i",formatTime:"H:i",formatDate:"Y/m/d",startDate:false,step:60,monthChangeSpinner:true,closeOnDateSelect:false,closeOnWithoutClick:true,closeOnInputClick:true,timepicker:true,datepicker:true,weeks:false,defaultTime:false,defaultDate:false,minDate:false,maxDate:false,minTime:false,maxTime:false,allowTimes:[],opened:false,initTime:true,inline:false,theme:"",onSelectDate:function(){},onSelectTime:function(){},onChangeMonth:function(){},onChangeYear:function(){},onChangeDateTime:function(){},onShow:function(){},onClose:function(){},onGenerate:function(){},withoutCopyright:true,inverseButton:false,hours12:false,next:"xdsoft_next",prev:"xdsoft_prev",dayOfWeekStart:0,parentID:"body",timeHeightInTimePicker:25,timepickerScrollbar:true,todayButton:true,prevButton:true,nextButton:true,defaultSelect:true,scrollMonth:true,scrollTime:true,scrollInput:true,lazyInit:false,mask:false,validateOnBlur:true,allowBlank:true,yearStart:1950,yearEnd:2050,monthStart:0,monthEnd:11,style:"",id:"",fixed:false,roundTime:"round",className:"",weekends:[],disabledDates:[],yearOffset:0,beforeShowDay:null,enterLikeTab:true};if(!Array.prototype.indexOf){Array.prototype.indexOf=function(e,f){var d,c;for(d=(f||0),c=this.length;d<c;d+=1){if(this[d]===e){return d}}return -1}}Date.prototype.countDaysInMonth=function(){return new Date(this.getFullYear(),this.getMonth()+1,0).getDate()};a.fn.xdsoftScroller=function(c){return this.each(function(){var m=a(this),n=function(u){var t={x:0,y:0},v;if(u.type==="touchstart"||u.type==="touchmove"||u.type==="touchend"||u.type==="touchcancel"){v=u.originalEvent.touches[0]||u.originalEvent.changedTouches[0];t.x=v.clientX;t.y=v.clientY}else{if(u.type==="mousedown"||u.type==="mouseup"||u.type==="mousemove"||u.type==="mouseover"||u.type==="mouseout"||u.type==="mouseenter"||u.type==="mouseleave"){t.x=u.clientX;t.y=u.clientY}}return t},g=0,q,k,r,s,j,p=100,e=false,i=0,l=0,o=0,f=false,d=0,h=function(){};if(c==="hide"){m.find(".xdsoft_scrollbar").hide();return}if(!a(this).hasClass("xdsoft_scroller_box")){q=m.children().eq(0);k=m[0].clientHeight;r=q[0].offsetHeight;s=a('<div class="xdsoft_scrollbar"></div>');j=a('<div class="xdsoft_scroller"></div>');s.append(j);m.addClass("xdsoft_scroller_box").append(s);h=function h(t){var u=n(t).y-i+d;if(u<0){u=0}if(u+j[0].offsetHeight>o){u=o-j[0].offsetHeight}m.trigger("scroll_element.xdsoft_scroller",[p?u/p:0])};j.on("touchstart.xdsoft_scroller mousedown.xdsoft_scroller",function(u){if(!k){m.trigger("resize_scroll.xdsoft_scroller",[c])}i=n(u).y;d=parseInt(j.css("margin-top"),10);o=s[0].offsetHeight;if(u.type==="mousedown"){if(document){a(document.body).addClass("xdsoft_noselect")}a([document.body,window]).on("mouseup.xdsoft_scroller",function t(){a([document.body,window]).off("mouseup.xdsoft_scroller",t).off("mousemove.xdsoft_scroller",h).removeClass("xdsoft_noselect")});a(document.body).on("mousemove.xdsoft_scroller",h)}else{f=true;u.stopPropagation();u.preventDefault()}}).on("touchmove",function(t){if(f){t.preventDefault();h(t)}}).on("touchend touchcancel",function(t){f=false;d=0});m.on("scroll_element.xdsoft_scroller",function(u,t){if(!k){m.trigger("resize_scroll.xdsoft_scroller",[t,true])}t=t>1?1:(t<0||isNaN(t))?0:t;j.css("margin-top",p*t);setTimeout(function(){q.css("marginTop",-parseInt((q[0].offsetHeight-k)*t,10))},10)}).on("resize_scroll.xdsoft_scroller",function(x,u,t){var w,v;k=m[0].clientHeight;r=q[0].offsetHeight;w=k/r;v=w*s[0].offsetHeight;if(w>1){j.hide()}else{j.show();j.css("height",parseInt(v>10?v:10,10));p=s[0].offsetHeight-j[0].offsetHeight;if(t!==true){m.trigger("scroll_element.xdsoft_scroller",[u||Math.abs(parseInt(q.css("marginTop"),10))/(r-k)])}}});m.on("mousewheel",function(t){var u=Math.abs(parseInt(q.css("marginTop"),10));u=u-(t.deltaY*20);if(u<0){u=0}m.trigger("scroll_element.xdsoft_scroller",[u/(r-k)]);t.stopPropagation();return false});m.on("touchstart",function(t){e=n(t);l=Math.abs(parseInt(q.css("marginTop"),10))});m.on("touchmove",function(t){if(e){t.preventDefault();var u=n(t);m.trigger("scroll_element.xdsoft_scroller",[(l-(u.y-e.y))/(r-k)])}});m.on("touchend touchcancel",function(t){e=false;l=0})}m.trigger("resize_scroll.xdsoft_scroller",[c])})};a.fn.datetimepicker=function(e){var t=48,o=57,i=96,c=105,l=17,q=46,m=13,C=27,y=8,u=37,r=38,A=39,B=40,h=9,v=116,w=65,d=67,z=86,p=90,k=89,x=false,g=(a.isPlainObject(e)||!e)?a.extend(true,{},b,e):a.extend(true,{},b),n=0,f,s,j=function(E){E.on("open.xdsoft focusin.xdsoft mousedown.xdsoft",function D(F){if(E.is(":disabled")||E.data("xdsoft_datetimepicker")){return}clearTimeout(n);n=setTimeout(function(){if(!E.data("xdsoft_datetimepicker")){f(E)}E.off("open.xdsoft focusin.xdsoft mousedown.xdsoft",D).trigger("open.xdsoft")},100)})};f=function(M){var O=a("<div "+(g.id?'id="'+g.id+'"':"")+" "+(g.style?'style="'+g.style+'"':"")+' class="xdsoft_datetimepicker xdsoft_'+g.theme+" xdsoft_noselect "+(g.weeks?" xdsoft_showweeks":"")+g.className+'"></div>'),R=a('<div class="xdsoft_copyright"><a target="_blank" href="http://xdsoft.net/jqplugins/datetimepicker/">xdsoft.net</a></div>'),K=a('<div class="xdsoft_datepicker active"></div>'),S=a('<div class="xdsoft_mounthpicker"><button type="button" class="xdsoft_prev"></button><button type="button" class="xdsoft_today_button"></button><div class="xdsoft_label xdsoft_month"><span></span><i></i></div><div class="xdsoft_label xdsoft_year"><span></span><i></i></div><button type="button" class="xdsoft_next"></button></div>'),P=a('<div class="xdsoft_calendar"></div>'),U=a('<div class="xdsoft_timepicker active"><button type="button" class="xdsoft_prev"></button><div class="xdsoft_time_box"></div><button type="button" class="xdsoft_next"></button></div>'),G=U.find(".xdsoft_time_box").eq(0),F=a('<div class="xdsoft_time_variant"></div>'),E=a('<div class="xdsoft_select xdsoft_monthselect"><div></div></div>'),T=a('<div class="xdsoft_select xdsoft_yearselect"><div></div></div>'),Q=false,V,H,D,X,I,L=0,W=0,N;S.find(".xdsoft_month span").after(E);S.find(".xdsoft_year span").after(T);S.find(".xdsoft_month,.xdsoft_year").on("mousedown.xdsoft",function(ab){var Y=a(this).find(".xdsoft_select").eq(0),ae=0,ac=0,ad=Y.is(":visible"),Z,aa;S.find(".xdsoft_select").hide();if(N.currentTime){ae=N.currentTime[a(this).hasClass("xdsoft_month")?"getMonth":"getFullYear"]()}Y[ad?"hide":"show"]();for(Z=Y.find("div.xdsoft_option"),aa=0;aa<Z.length;aa+=1){if(Z.eq(aa).data("value")===ae){break}else{ac+=Z[0].offsetHeight}}Y.xdsoftScroller(ac/(Y.children()[0].offsetHeight-(Y[0].clientHeight)));ab.stopPropagation();return false});S.find(".xdsoft_select").xdsoftScroller().on("mousedown.xdsoft",function(Y){Y.stopPropagation();Y.preventDefault()}).on("mousedown.xdsoft",".xdsoft_option",function(Z){if(N.currentTime===undefined||N.currentTime===null){N.currentTime=N.now()}var Y=N.currentTime.getFullYear();if(N&&N.currentTime){N.currentTime[a(this).parent().parent().hasClass("xdsoft_monthselect")?"setMonth":"setFullYear"](a(this).data("value"))}a(this).parent().parent().hide();O.trigger("xchange.xdsoft");if(g.onChangeMonth&&a.isFunction(g.onChangeMonth)){g.onChangeMonth.call(O,N.currentTime,O.data("input"))}if(Y!==N.currentTime.getFullYear()&&a.isFunction(g.onChangeYear)){g.onChangeYear.call(O,N.currentTime,O.data("input"))}});O.setOptions=function(Y){g=a.extend(true,{},g,Y);if(Y.allowTimes&&a.isArray(Y.allowTimes)&&Y.allowTimes.length){g.allowTimes=a.extend(true,[],Y.allowTimes)}if(Y.weekends&&a.isArray(Y.weekends)&&Y.weekends.length){g.weekends=a.extend(true,[],Y.weekends)}if(Y.disabledDates&&a.isArray(Y.disabledDates)&&Y.disabledDates.length){g.disabledDates=a.extend(true,[],Y.disabledDates)}if((g.open||g.opened)&&(!g.inline)){M.trigger("open.xdsoft")}if(g.inline){Q=true;O.addClass("xdsoft_inline");M.after(O).hide()}if(g.inverseButton){g.next="xdsoft_prev";g.prev="xdsoft_next"}if(g.datepicker){K.addClass("active")}else{K.removeClass("active")}if(g.timepicker){U.addClass("active")}else{U.removeClass("active")}if(g.value){if(M&&M.val){M.val(g.value)}N.setCurrentTime(g.value)}if(isNaN(g.dayOfWeekStart)){g.dayOfWeekStart=0}else{g.dayOfWeekStart=parseInt(g.dayOfWeekStart,10)%7}if(!g.timepickerScrollbar){G.xdsoftScroller("hide")}if(g.minDate&&/^-(.*)$/.test(g.minDate)){g.minDate=N.strToDateTime(g.minDate).dateFormat(g.formatDate)}if(g.maxDate&&/^\+(.*)$/.test(g.maxDate)){g.maxDate=N.strToDateTime(g.maxDate).dateFormat(g.formatDate)}S.find(".xdsoft_today_button").css("visibility",!g.todayButton?"hidden":"visible");S.find("."+g.prev).css("visibility",!g.prevButton?"hidden":"visible");S.find("."+g.next).css("visibility",!g.nextButton?"hidden":"visible");if(g.mask){var aa,ab=function(ae){try{if(document.selection&&document.selection.createRange){var ad=document.selection.createRange();return ad.getBookmark().charCodeAt(2)-2}if(ae.setSelectionRange){return ae.selectionStart}}catch(af){return 0}},Z=function(ae,af){ae=(typeof ae==="string"||ae instanceof String)?document.getElementById(ae):ae;if(!ae){return false}if(ae.createTextRange){var ad=ae.createTextRange();ad.collapse(true);ad.moveEnd("character",af);ad.moveStart("character",af);ad.select();return true}if(ae.setSelectionRange){ae.setSelectionRange(af,af);return true}return false},ac=function(ad,af){var ae=ad.replace(/([\[\]\/\{\}\(\)\-\.\+]{1})/g,"\\$1").replace(/_/g,"{digit+}").replace(/([0-9]{1})/g,"{digit$1}").replace(/\{digit([0-9]{1})\}/g,"[0-$1_]{1}").replace(/\{digit[\+]\}/g,"[0-9_]{1}");return(new RegExp(ae)).test(af)};M.off("keydown.xdsoft");if(g.mask===true){g.mask=g.format.replace(/Y/g,"9999").replace(/F/g,"9999").replace(/m/g,"19").replace(/d/g,"39").replace(/H/g,"29").replace(/i/g,"59").replace(/s/g,"59")}if(a.type(g.mask)==="string"){if(!ac(g.mask,M.val())){M.val(g.mask.replace(/[0-9]/g,"_"))}M.on("keydown.xdsoft",function(ae){var af=this.value,ad=ae.which,ah,ag;if(((ad>=t&&ad<=o)||(ad>=i&&ad<=c))||(ad===y||ad===q)){ah=ab(this);ag=(ad!==y&&ad!==q)?String.fromCharCode((i<=ad&&ad<=c)?ad-t:ad):"_";if((ad===y||ad===q)&&ah){ah-=1;ag="_"}while(/[^0-9_]/.test(g.mask.substr(ah,1))&&ah<g.mask.length&&ah>0){ah+=(ad===y||ad===q)?-1:1}af=af.substr(0,ah)+ag+af.substr(ah+1);if(a.trim(af)===""){af=g.mask.replace(/[0-9]/g,"_")}else{if(ah===g.mask.length){ae.preventDefault();return false}}ah+=(ad===y||ad===q)?0:1;while(/[^0-9_]/.test(g.mask.substr(ah,1))&&ah<g.mask.length&&ah>0){ah+=(ad===y||ad===q)?-1:1}if(ac(g.mask,af)){this.value=af;Z(this,ah)}else{if(a.trim(af)===""){this.value=g.mask.replace(/[0-9]/g,"_")}else{M.trigger("error_input.xdsoft")}}}else{if(([w,d,z,p,k].indexOf(ad)!==-1&&x)||[C,r,B,u,A,v,l,h,m].indexOf(ad)!==-1){return true}}ae.preventDefault();return false})}}if(g.validateOnBlur){M.off("blur.xdsoft").on("blur.xdsoft",function(){if(g.allowBlank&&!a.trim(a(this).val()).length){a(this).val(null);O.data("xdsoft_datetime").empty()}else{if(!Date.parseDate(a(this).val(),g.format)){var ae=+([a(this).val()[0],a(this).val()[1]].join("")),ad=+([a(this).val()[2],a(this).val()[3]].join(""));if(!g.datepicker&&g.timepicker&&ae>=0&&ae<24&&ad>=0&&ad<60){a(this).val([ae,ad].map(function(af){return af>9?af:"0"+af}).join(":"))}else{a(this).val((N.now()).dateFormat(g.format))}O.data("xdsoft_datetime").setCurrentTime(a(this).val())}else{O.data("xdsoft_datetime").setCurrentTime(a(this).val())}}O.trigger("changedatetime.xdsoft")})}g.dayOfWeekStartPrev=(g.dayOfWeekStart===0)?6:g.dayOfWeekStart-1;O.trigger("xchange.xdsoft").trigger("afterOpen.xdsoft")};O.data("options",g).on("mousedown.xdsoft",function(Y){Y.stopPropagation();Y.preventDefault();T.hide();E.hide();return false});G.append(F);G.xdsoftScroller();O.on("afterOpen.xdsoft",function(){G.xdsoftScroller()});O.append(K).append(U);if(g.withoutCopyright!==true){O.append(R)}K.append(S).append(P);a(g.parentID).append(O);V=function(){var Y=this;Y.now=function(ab){var ac=new Date(),Z,aa;if(!ab&&g.defaultDate){Z=Y.strToDate(g.defaultDate);ac.setFullYear(Z.getFullYear());ac.setMonth(Z.getMonth());ac.setDate(Z.getDate())}if(g.yearOffset){ac.setFullYear(ac.getFullYear()+g.yearOffset)}if(!ab&&g.defaultTime){aa=Y.strtotime(g.defaultTime);ac.setHours(aa.getHours());ac.setMinutes(aa.getMinutes())}return ac};Y.isValidDate=function(Z){if(Object.prototype.toString.call(Z)!=="[object Date]"){return false}return !isNaN(Z.getTime())};Y.setCurrentTime=function(Z){Y.currentTime=(typeof Z==="string")?Y.strToDateTime(Z):Y.isValidDate(Z)?Z:Y.now();O.trigger("xchange.xdsoft")};Y.empty=function(){Y.currentTime=null};Y.getCurrentTime=function(Z){return Y.currentTime};Y.nextMonth=function(){if(Y.currentTime===undefined||Y.currentTime===null){Y.currentTime=Y.now()}var aa=Y.currentTime.getMonth()+1,Z;if(aa===12){Y.currentTime.setFullYear(Y.currentTime.getFullYear()+1);aa=0}Z=Y.currentTime.getFullYear();Y.currentTime.setDate(Math.min(new Date(Y.currentTime.getFullYear(),aa+1,0).getDate(),Y.currentTime.getDate()));Y.currentTime.setMonth(aa);if(g.onChangeMonth&&a.isFunction(g.onChangeMonth)){g.onChangeMonth.call(O,N.currentTime,O.data("input"))}if(Z!==Y.currentTime.getFullYear()&&a.isFunction(g.onChangeYear)){g.onChangeYear.call(O,N.currentTime,O.data("input"))}O.trigger("xchange.xdsoft");return aa};Y.prevMonth=function(){if(Y.currentTime===undefined||Y.currentTime===null){Y.currentTime=Y.now()}var Z=Y.currentTime.getMonth()-1;if(Z===-1){Y.currentTime.setFullYear(Y.currentTime.getFullYear()-1);Z=11}Y.currentTime.setDate(Math.min(new Date(Y.currentTime.getFullYear(),Z+1,0).getDate(),Y.currentTime.getDate()));Y.currentTime.setMonth(Z);if(g.onChangeMonth&&a.isFunction(g.onChangeMonth)){g.onChangeMonth.call(O,N.currentTime,O.data("input"))}O.trigger("xchange.xdsoft");return Z};Y.getWeekOfYear=function(aa){var Z=new Date(aa.getFullYear(),0,1);return Math.ceil((((aa-Z)/86400000)+Z.getDay()+1)/7)};Y.strToDateTime=function(ac){var aa=[],Z,ab;if(ac&&ac instanceof Date&&Y.isValidDate(ac)){return ac}aa=/^(\+|\-)(.*)$/.exec(ac);if(aa){aa[2]=Date.parseDate(aa[2],g.formatDate)}if(aa&&aa[2]){Z=aa[2].getTime()-(aa[2].getTimezoneOffset())*60000;ab=new Date((N.now()).getTime()+parseInt(aa[1]+"1",10)*Z)}else{ab=ac?Date.parseDate(ac,g.format):Y.now()}if(!Y.isValidDate(ab)){ab=Y.now()}return ab};Y.strToDate=function(aa){if(aa&&aa instanceof Date&&Y.isValidDate(aa)){return aa}var Z=aa?Date.parseDate(aa,g.formatDate):Y.now(true);if(!Y.isValidDate(Z)){Z=Y.now(true)}return Z};Y.strtotime=function(Z){if(Z&&Z instanceof Date&&Y.isValidDate(Z)){return Z}var aa=Z?Date.parseDate(Z,g.formatTime):Y.now(true);if(!Y.isValidDate(aa)){aa=Y.now(true)}return aa};Y.str=function(){return Y.currentTime.dateFormat(g.format)};Y.currentTime=this.now()};N=new V();S.find(".xdsoft_today_button").on("mousedown.xdsoft",function(){O.data("changed",true);N.setCurrentTime(0);O.trigger("afterOpen.xdsoft")}).on("dblclick.xdsoft",function(){M.val(N.str());O.trigger("close.xdsoft")});S.find(".xdsoft_prev,.xdsoft_next").on("mousedown.xdsoft",function(){var Z=a(this),ac=0,Y=false;(function ab(ad){if(Z.hasClass(g.next)){N.nextMonth()}else{if(Z.hasClass(g.prev)){N.prevMonth()}}if(g.monthChangeSpinner){if(!Y){ac=setTimeout(ab,ad||100)}}}(500));a([document.body,window]).on("mouseup.xdsoft",function aa(){clearTimeout(ac);Y=true;a([document.body,window]).off("mouseup.xdsoft",aa)})});U.find(".xdsoft_prev,.xdsoft_next").on("mousedown.xdsoft",function(){var ab=a(this),ad=0,Y=false,ac=110;(function aa(af){var ah=G[0].clientHeight,ae=F[0].offsetHeight,ag=Math.abs(parseInt(F.css("marginTop"),10));if(ab.hasClass(g.next)&&(ae-ah)-g.timeHeightInTimePicker>=ag){F.css("marginTop","-"+(ag+g.timeHeightInTimePicker)+"px")}else{if(ab.hasClass(g.prev)&&ag-g.timeHeightInTimePicker>=0){F.css("marginTop","-"+(ag-g.timeHeightInTimePicker)+"px")}}G.trigger("scroll_element.xdsoft_scroller",[Math.abs(parseInt(F.css("marginTop"),10)/(ae-ah))]);ac=(ac>10)?10:ac-10;if(!Y){ad=setTimeout(aa,af||ac)}}(500));a([document.body,window]).on("mouseup.xdsoft",function Z(){clearTimeout(ad);Y=true;a([document.body,window]).off("mouseup.xdsoft",Z)})});H=0;O.on("xchange.xdsoft",function(Y){clearTimeout(H);H=setTimeout(function(){if(N.currentTime===undefined||N.currentTime===null){N.currentTime=N.now()}var ao="",aa=new Date(N.currentTime.getFullYear(),N.currentTime.getMonth(),1,12,0,0),ag=0,af,ak=N.now(),Z=false,ae=false,aj,al,ac,am,ad=[],ap,an=true,ab="",ah="",ai;while(aa.getDay()!==g.dayOfWeekStart){aa.setDate(aa.getDate()-1)}ao+="<table><thead><tr>";if(g.weeks){ao+="<th></th>"}for(af=0;af<7;af+=1){ao+="<th>"+g.i18n[g.lang].dayOfWeek[(af+g.dayOfWeekStart)%7]+"</th>"}ao+="</tr></thead>";ao+="<tbody>";if(g.maxDate!==false){Z=N.strToDate(g.maxDate);Z=new Date(Z.getFullYear(),Z.getMonth(),Z.getDate(),23,59,59,999)}if(g.minDate!==false){ae=N.strToDate(g.minDate);ae=new Date(ae.getFullYear(),ae.getMonth(),ae.getDate())}while(ag<N.currentTime.countDaysInMonth()||aa.getDay()!==g.dayOfWeekStart||N.currentTime.getMonth()===aa.getMonth()){ad=[];ag+=1;aj=aa.getDate();al=aa.getFullYear();ac=aa.getMonth();am=N.getWeekOfYear(aa);ad.push("xdsoft_date");if(g.beforeShowDay&&a.isFunction(g.beforeShowDay.call)){ap=g.beforeShowDay.call(O,aa)}else{ap=null}if((Z!==false&&aa>Z)||(ae!==false&&aa<ae)||(ap&&ap[0]===false)){ad.push("xdsoft_disabled")}else{if(g.disabledDates.indexOf(aa.dateFormat(g.formatDate))!==-1){ad.push("xdsoft_disabled")}}if(ap&&ap[1]!==""){ad.push(ap[1])}if(N.currentTime.getMonth()!==ac){ad.push("xdsoft_other_month")}if((g.defaultSelect||O.data("changed"))&&N.currentTime.dateFormat(g.formatDate)===aa.dateFormat(g.formatDate)){ad.push("xdsoft_current")}if(ak.dateFormat(g.formatDate)===aa.dateFormat(g.formatDate)){ad.push("xdsoft_today")}if(aa.getDay()===0||aa.getDay()===6||~g.weekends.indexOf(aa.dateFormat(g.formatDate))){ad.push("xdsoft_weekend")}if(g.beforeShowDay&&a.isFunction(g.beforeShowDay)){ad.push(g.beforeShowDay(aa))}if(an){ao+="<tr>";an=false;if(g.weeks){ao+="<th>"+am+"</th>"}}ao+='<td data-date="'+aj+'" data-month="'+ac+'" data-year="'+al+'" class="xdsoft_date xdsoft_day_of_week'+aa.getDay()+" "+ad.join(" ")+'"><div>'+aj+"</div></td>";if(aa.getDay()===g.dayOfWeekStartPrev){ao+="</tr>";an=true}aa.setDate(aj+1)}ao+="</tbody></table>";P.html(ao);S.find(".xdsoft_label span").eq(0).text(g.i18n[g.lang].months[N.currentTime.getMonth()]);S.find(".xdsoft_label span").eq(1).text(N.currentTime.getFullYear());ab="";ah="";ac="";ai=function ai(au,aq){var at=N.now();at.setHours(au);au=parseInt(at.getHours(),10);at.setMinutes(aq);aq=parseInt(at.getMinutes(),10);var ar=new Date(N.currentTime);ar.setHours(au);ar.setMinutes(aq);ad=[];if((g.minDateTime!==false&&g.minDateTime>ar)||(g.maxTime!==false&&N.strtotime(g.maxTime).getTime()<at.getTime())||(g.minTime!==false&&N.strtotime(g.minTime).getTime()>at.getTime())){ad.push("xdsoft_disabled")}var av=new Date(N.currentTime);av.setHours(parseInt(N.currentTime.getHours(),10));av.setMinutes(Math[g.roundTime](N.currentTime.getMinutes()/g.step)*g.step);if((g.initTime||g.defaultSelect||O.data("changed"))&&av.getHours()===parseInt(au,10)&&(g.step>59||av.getMinutes()===parseInt(aq,10))){if(g.defaultSelect||O.data("changed")){ad.push("xdsoft_current")}else{if(g.initTime){ad.push("xdsoft_init_time")}}}if(parseInt(ak.getHours(),10)===parseInt(au,10)&&parseInt(ak.getMinutes(),10)===parseInt(aq,10)){ad.push("xdsoft_today")}ab+='<div class="xdsoft_time '+ad.join(" ")+'" data-hour="'+au+'" data-minute="'+aq+'">'+at.dateFormat(g.formatTime)+"</div>"};if(!g.allowTimes||!a.isArray(g.allowTimes)||!g.allowTimes.length){for(ag=0,af=0;ag<(g.hours12?12:24);ag+=1){for(af=0;af<60;af+=g.step){ah=(ag<10?"0":"")+ag;ac=(af<10?"0":"")+af;ai(ah,ac)}}}else{for(ag=0;ag<g.allowTimes.length;ag+=1){ah=N.strtotime(g.allowTimes[ag]).getHours();ac=N.strtotime(g.allowTimes[ag]).getMinutes();ai(ah,ac)}}F.html(ab);e="";ag=0;for(ag=parseInt(g.yearStart,10)+g.yearOffset;ag<=parseInt(g.yearEnd,10)+g.yearOffset;ag+=1){e+='<div class="xdsoft_option '+(N.currentTime.getFullYear()===ag?"xdsoft_current":"")+'" data-value="'+ag+'">'+ag+"</div>"}T.children().eq(0).html(e);for(ag=parseInt(g.monthStart),e="";ag<=parseInt(g.monthEnd);ag+=1){e+='<div class="xdsoft_option '+(N.currentTime.getMonth()===ag?"xdsoft_current":"")+'" data-value="'+ag+'">'+g.i18n[g.lang].months[ag]+"</div>"}E.children().eq(0).html(e);a(O).trigger("generate.xdsoft")},10);Y.stopPropagation()}).on("afterOpen.xdsoft",function(){if(g.timepicker){var Z,ab,Y,aa;if(F.find(".xdsoft_current").length){Z=".xdsoft_current"}else{if(F.find(".xdsoft_init_time").length){Z=".xdsoft_init_time"}}if(Z){ab=G[0].clientHeight;Y=F[0].offsetHeight;aa=F.find(Z).index()*g.timeHeightInTimePicker+1;if((Y-ab)<aa){aa=Y-ab}G.trigger("scroll_element.xdsoft_scroller",[parseInt(aa,10)/(Y-ab)])}else{G.trigger("scroll_element.xdsoft_scroller",[0])}}});D=0;P.on("click.xdsoft","td",function(Y){Y.stopPropagation();D+=1;var aa=a(this),Z=N.currentTime;if(Z===undefined||Z===null){N.currentTime=N.now();Z=N.currentTime}if(aa.hasClass("xdsoft_disabled")){return false}Z.setDate(1);Z.setFullYear(aa.data("year"));Z.setMonth(aa.data("month"));Z.setDate(aa.data("date"));O.trigger("select.xdsoft",[Z]);M.val(N.str());if((D>1||(g.closeOnDateSelect===true||(g.closeOnDateSelect===0&&!g.timepicker)))&&!g.inline){O.trigger("close.xdsoft")}if(g.onSelectDate&&a.isFunction(g.onSelectDate)){g.onSelectDate.call(O,N.currentTime,O.data("input"),Y)}O.data("changed",true);O.trigger("xchange.xdsoft");O.trigger("changedatetime.xdsoft");setTimeout(function(){D=0},200)});F.on("click.xdsoft","div",function(Y){Y.stopPropagation();var aa=a(this),Z=N.currentTime;if(Z===undefined||Z===null){N.currentTime=N.now();Z=N.currentTime}if(aa.hasClass("xdsoft_disabled")){return false}Z.setHours(aa.data("hour"));Z.setMinutes(aa.data("minute"));O.trigger("select.xdsoft",[Z]);O.data("input").val(N.str());if(!g.inline){O.trigger("close.xdsoft")}if(g.onSelectTime&&a.isFunction(g.onSelectTime)){g.onSelectTime.call(O,N.currentTime,O.data("input"),Y)}O.data("changed",true);O.trigger("xchange.xdsoft");O.trigger("changedatetime.xdsoft")});K.on("mousewheel.xdsoft",function(Y){if(!g.scrollMonth){return true}if(Y.deltaY<0){N.nextMonth()}else{N.prevMonth()}return false});M.on("mousewheel.xdsoft",function(Y){if(!g.scrollInput){return true}if(!g.datepicker&&g.timepicker){X=F.find(".xdsoft_current").length?F.find(".xdsoft_current").eq(0).index():0;if(X+Y.deltaY>=0&&X+Y.deltaY<F.children().length){X+=Y.deltaY}if(F.children().eq(X).length){F.children().eq(X).trigger("mousedown")}return false}if(g.datepicker&&!g.timepicker){K.trigger(Y,[Y.deltaY,Y.deltaX,Y.deltaY]);if(M.val){M.val(N.str())}O.trigger("changedatetime.xdsoft");return false}});O.on("changedatetime.xdsoft",function(Y){if(g.onChangeDateTime&&a.isFunction(g.onChangeDateTime)){var Z=O.data("input");g.onChangeDateTime.call(O,N.currentTime,Z,Y);delete g.value;Z.trigger("change")}}).on("generate.xdsoft",function(){if(g.onGenerate&&a.isFunction(g.onGenerate)){g.onGenerate.call(O,N.currentTime,O.data("input"))}if(Q){O.trigger("afterOpen.xdsoft");Q=false}}).on("click.xdsoft",function(Y){Y.stopPropagation()});X=0;I=function(){var ab=O.data("input").offset(),aa=ab.top+O.data("input")[0].offsetHeight-1,Z=ab.left,Y="absolute";if(g.fixed){aa-=a(window).scrollTop();Z-=a(window).scrollLeft();Y="fixed"}else{if(aa+O[0].offsetHeight>a(window).height()+a(window).scrollTop()){aa=ab.top-O[0].offsetHeight+1}if(aa<0){aa=0}if(Z+O[0].offsetWidth>a(window).width()){Z=a(window).width()-O[0].offsetWidth}}O.css({left:Z,top:aa,position:Y})};O.on("open.xdsoft",function(Z){var aa=true;if(g.onShow&&a.isFunction(g.onShow)){aa=g.onShow.call(O,N.currentTime,O.data("input"),Z)}if(aa!==false){O.show();I();a(window).off("resize.xdsoft",I).on("resize.xdsoft",I);if(g.closeOnWithoutClick){a([document.body,window]).on("mousedown.xdsoft",function Y(){O.trigger("close.xdsoft");a([document.body,window]).off("mousedown.xdsoft",Y)})}}}).on("close.xdsoft",function(Z){var Y=true;S.find(".xdsoft_month,.xdsoft_year").find(".xdsoft_select").hide();if(g.onClose&&a.isFunction(g.onClose)){Y=g.onClose.call(O,N.currentTime,O.data("input"),Z)}if(Y!==false&&!g.opened&&!g.inline){O.hide()}Z.stopPropagation()}).on("toggle.xdsoft",function(Y){if(O.is(":visible")){O.trigger("close.xdsoft")}else{O.trigger("open.xdsoft")}}).data("input",M);L=0;W=0;O.data("xdsoft_datetime",N);O.setOptions(g);function J(){var Y=false,Z;if(g.startDate){Y=N.strToDate(g.startDate)}else{Y=g.value||((M&&M.val&&M.val())?M.val():"");if(Y){Y=N.strToDateTime(Y)}else{if(g.defaultDate){Y=N.strToDate(g.defaultDate);if(g.defaultTime){Z=N.strtotime(g.defaultTime);Y.setHours(Z.getHours());Y.setMinutes(Z.getMinutes())}}}}if(Y&&N.isValidDate(Y)){O.data("changed",true)}else{Y=""}return Y||0}N.setCurrentTime(J());M.data("xdsoft_datetimepicker",O).on("open.xdsoft focusin.xdsoft mousedown.xdsoft",function(Y){if(M.is(":disabled")||(M.data("xdsoft_datetimepicker").is(":visible")&&g.closeOnInputClick)){return}clearTimeout(L);L=setTimeout(function(){if(M.is(":disabled")){return}Q=true;N.setCurrentTime(J());O.trigger("open.xdsoft")},100)}).on("keydown.xdsoft",function(aa){var ab=this.value,Z,Y=aa.which;if([m].indexOf(Y)!==-1&&g.enterLikeTab){Z=a("input:visible,textarea:visible");O.trigger("close.xdsoft");Z.eq(Z.index(this)+1).focus();return false}if([h].indexOf(Y)!==-1){O.trigger("close.xdsoft");return true}})};s=function(D){var E=D.data("xdsoft_datetimepicker");if(E){E.data("xdsoft_datetime",null);E.remove();D.data("xdsoft_datetimepicker",null).off(".xdsoft");a(window).off("resize.xdsoft");a([window,document.body]).off("mousedown.xdsoft");if(D.unmousewheel){D.unmousewheel()}}};a(document).off("keydown.xdsoftctrl keyup.xdsoftctrl").on("keydown.xdsoftctrl",function(D){if(D.keyCode===l){x=true}}).on("keyup.xdsoftctrl",function(D){if(D.keyCode===l){x=false}});return this.each(function(){var D=a(this).data("xdsoft_datetimepicker");if(D){if(a.type(e)==="string"){switch(e){case"show":a(this).select().focus();D.trigger("open.xdsoft");break;case"hide":D.trigger("close.xdsoft");break;case"toggle":D.trigger("toggle.xdsoft");break;case"destroy":s(a(this));break;case"reset":this.value=this.defaultValue;if(!this.value||!D.data("xdsoft_datetime").isValidDate(Date.parseDate(this.value,g.format))){D.data("changed",false)}D.data("xdsoft_datetime").setCurrentTime(this.value);break;case"validate":var E=D.data("input");E.trigger("blur.xdsoft");break}}else{D.setOptions(e)}return 0}if(a.type(e)!=="string"){if(!g.lazyInit||g.open||g.inline){f(a(this))}else{j(a(this))}}})};a.fn.datetimepicker.defaults=b}(jQuery));(function(){
+/*! Copyright (c) 2013 Brandon Aaron (http://brandon.aaron.sh)
+ * Licensed under the MIT License (LICENSE.txt).
+ *
+ * Version: 3.1.12
+ *
+ * Requires: jQuery 1.2.2+
+ */
+;!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a:a(jQuery)}(function(a){function b(b){var g=b||window.event,h=i.call(arguments,1),j=0,l=0,m=0,n=0,o=0,p=0;if(b=a.event.fix(g),b.type="mousewheel","detail" in g&&(m=-1*g.detail),"wheelDelta" in g&&(m=g.wheelDelta),"wheelDeltaY" in g&&(m=g.wheelDeltaY),"wheelDeltaX" in g&&(l=-1*g.wheelDeltaX),"axis" in g&&g.axis===g.HORIZONTAL_AXIS&&(l=-1*m,m=0),j=0===m?l:m,"deltaY" in g&&(m=-1*g.deltaY,j=m),"deltaX" in g&&(l=g.deltaX,0===m&&(j=-1*l)),0!==m||0!==l){if(1===g.deltaMode){var q=a.data(this,"mousewheel-line-height");j*=q,m*=q,l*=q}else{if(2===g.deltaMode){var r=a.data(this,"mousewheel-page-height");j*=r,m*=r,l*=r}}if(n=Math.max(Math.abs(m),Math.abs(l)),(!f||f>n)&&(f=n,d(g,n)&&(f/=40)),d(g,n)&&(j/=40,l/=40,m/=40),j=Math[j>=1?"floor":"ceil"](j/f),l=Math[l>=1?"floor":"ceil"](l/f),m=Math[m>=1?"floor":"ceil"](m/f),k.settings.normalizeOffset&&this.getBoundingClientRect){var s=this.getBoundingClientRect();o=b.clientX-s.left,p=b.clientY-s.top}return b.deltaX=l,b.deltaY=m,b.deltaFactor=f,b.offsetX=o,b.offsetY=p,b.deltaMode=0,h.unshift(b,j,l,m),e&&clearTimeout(e),e=setTimeout(c,200),(a.event.dispatch||a.event.handle).apply(this,h)}}function c(){f=null}function d(a,b){return k.settings.adjustOldDeltas&&"mousewheel"===a.type&&b%120===0}var e,f,g=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],h="onwheel" in document||document.documentMode>=9?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],i=Array.prototype.slice;if(a.event.fixHooks){for(var j=g.length;j;){a.event.fixHooks[g[--j]]=a.event.mouseHooks}}var k=a.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener){for(var c=h.length;c;){this.addEventListener(h[--c],b,!1)}}else{this.onmousewheel=b}a.data(this,"mousewheel-line-height",k.getLineHeight(this)),a.data(this,"mousewheel-page-height",k.getPageHeight(this))},teardown:function(){if(this.removeEventListener){for(var c=h.length;c;){this.removeEventListener(h[--c],b,!1)}}else{this.onmousewheel=null}a.removeData(this,"mousewheel-line-height"),a.removeData(this,"mousewheel-page-height")},getLineHeight:function(b){var c=a(b),d=c["offsetParent" in a.fn?"offsetParent":"parent"]();return d.length||(d=a("body")),parseInt(d.css("fontSize"),10)||parseInt(c.css("fontSize"),10)||16},getPageHeight:function(b){return a(b).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};a.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})});Date.parseFunctions={count:0};Date.parseRegexes=[];Date.formatFunctions={count:0};Date.prototype.dateFormat=function(b){if(b=="unixtime"){return parseInt(this.getTime()/1000)}if(Date.formatFunctions[b]==null){Date.createNewFormat(b)}var a=Date.formatFunctions[b];return this[a]()};Date.createNewFormat=function(format){var funcName="format"+Date.formatFunctions.count++;Date.formatFunctions[format]=funcName;var codePrefix="Date.prototype."+funcName+" = function() {return ";var code="";var special=false;var ch="";for(var i=0;i<format.length;++i){ch=format.charAt(i);if(!special&&ch=="\\"){special=true}else{if(special){special=false;code+="'"+String.escape(ch)+"' + "}else{code+=Date.getFormatCode(ch)}}}if(code.length==0){code='""'}else{code=code.substring(0,code.length-3)}eval(codePrefix+code+";}")};Date.getFormatCode=function(a){switch(a){case"d":return"String.leftPad(this.getDate(), 2, '0') + ";case"D":return"Date.dayNames[this.getDay()].substring(0, 3) + ";case"j":return"this.getDate() + ";case"l":return"Date.dayNames[this.getDay()] + ";case"S":return"this.getSuffix() + ";case"w":return"this.getDay() + ";case"z":return"this.getDayOfYear() + ";case"W":return"this.getWeekOfYear() + ";case"F":return"Date.monthNames[this.getMonth()] + ";case"m":return"String.leftPad(this.getMonth() + 1, 2, '0') + ";case"M":return"Date.monthNames[this.getMonth()].substring(0, 3) + ";case"n":return"(this.getMonth() + 1) + ";case"t":return"this.getDaysInMonth() + ";case"L":return"(this.isLeapYear() ? 1 : 0) + ";case"Y":return"this.getFullYear() + ";case"y":return"('' + this.getFullYear()).substring(2, 4) + ";case"a":return"(this.getHours() < 12 ? 'am' : 'pm') + ";case"A":return"(this.getHours() < 12 ? 'AM' : 'PM') + ";case"g":return"((this.getHours() %12) ? this.getHours() % 12 : 12) + ";case"G":return"this.getHours() + ";case"h":return"String.leftPad((this.getHours() %12) ? this.getHours() % 12 : 12, 2, '0') + ";case"H":return"String.leftPad(this.getHours(), 2, '0') + ";case"i":return"String.leftPad(this.getMinutes(), 2, '0') + ";case"s":return"String.leftPad(this.getSeconds(), 2, '0') + ";case"O":return"this.getGMTOffset() + ";case"T":return"this.getTimezone() + ";case"Z":return"(this.getTimezoneOffset() * -60) + ";default:return"'"+String.escape(a)+"' + "}};Date.parseDate=function(a,c){if(c=="unixtime"){return new Date(!isNaN(parseInt(a))?parseInt(a)*1000:0)}if(Date.parseFunctions[c]==null){Date.createParser(c)}var b=Date.parseFunctions[c];return Date[b](a)};Date.createParser=function(format){var funcName="parse"+Date.parseFunctions.count++;var regexNum=Date.parseRegexes.length;var currentGroup=1;Date.parseFunctions[format]=funcName;var code="Date."+funcName+" = function(input) {\nvar y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, z = -1;\nvar d = new Date();\ny = d.getFullYear();\nm = d.getMonth();\nd = d.getDate();\nvar results = input.match(Date.parseRegexes["+regexNum+"]);\nif (results && results.length > 0) {";var regex="";var special=false;var ch="";for(var i=0;i<format.length;++i){ch=format.charAt(i);if(!special&&ch=="\\"){special=true}else{if(special){special=false;regex+=String.escape(ch)}else{obj=Date.formatCodeToRegex(ch,currentGroup);currentGroup+=obj.g;regex+=obj.s;if(obj.g&&obj.c){code+=obj.c}}}}code+="if (y > 0 && z > 0){\nvar doyDate = new Date(y,0);\ndoyDate.setDate(z);\nm = doyDate.getMonth();\nd = doyDate.getDate();\n}";code+="if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n{return new Date(y, m, d, h, i, s);}\nelse if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n{return new Date(y, m, d, h, i);}\nelse if (y > 0 && m >= 0 && d > 0 && h >= 0)\n{return new Date(y, m, d, h);}\nelse if (y > 0 && m >= 0 && d > 0)\n{return new Date(y, m, d);}\nelse if (y > 0 && m >= 0)\n{return new Date(y, m);}\nelse if (y > 0)\n{return new Date(y);}\n}return null;}";Date.parseRegexes[regexNum]=new RegExp("^"+regex+"$");eval(code)};Date.formatCodeToRegex=function(b,a){switch(b){case"D":return{g:0,c:null,s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};case"j":case"d":return{g:1,c:"d = parseInt(results["+a+"], 10);\n",s:"(\\d{1,2})"};case"l":return{g:0,c:null,s:"(?:"+Date.dayNames.join("|")+")"};case"S":return{g:0,c:null,s:"(?:st|nd|rd|th)"};case"w":return{g:0,c:null,s:"\\d"};case"z":return{g:1,c:"z = parseInt(results["+a+"], 10);\n",s:"(\\d{1,3})"};case"W":return{g:0,c:null,s:"(?:\\d{2})"};case"F":return{g:1,c:"m = parseInt(Date.monthNumbers[results["+a+"].substring(0, 3)], 10);\n",s:"("+Date.monthNames.join("|")+")"};case"M":return{g:1,c:"m = parseInt(Date.monthNumbers[results["+a+"]], 10);\n",s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};case"n":case"m":return{g:1,c:"m = parseInt(results["+a+"], 10) - 1;\n",s:"(\\d{1,2})"};case"t":return{g:0,c:null,s:"\\d{1,2}"};case"L":return{g:0,c:null,s:"(?:1|0)"};case"Y":return{g:1,c:"y = parseInt(results["+a+"], 10);\n",s:"(\\d{4})"};case"y":return{g:1,c:"var ty = parseInt(results["+a+"], 10);\ny = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",s:"(\\d{1,2})"};case"a":return{g:1,c:"if (results["+a+"] == 'am') {\nif (h == 12) { h = 0; }\n} else { if (h < 12) { h += 12; }}",s:"(am|pm)"};case"A":return{g:1,c:"if (results["+a+"] == 'AM') {\nif (h == 12) { h = 0; }\n} else { if (h < 12) { h += 12; }}",s:"(AM|PM)"};case"g":case"G":case"h":case"H":return{g:1,c:"h = parseInt(results["+a+"], 10);\n",s:"(\\d{1,2})"};case"i":return{g:1,c:"i = parseInt(results["+a+"], 10);\n",s:"(\\d{2})"};case"s":return{g:1,c:"s = parseInt(results["+a+"], 10);\n",s:"(\\d{2})"};case"O":return{g:0,c:null,s:"[+-]\\d{4}"};case"T":return{g:0,c:null,s:"[A-Z]{3}"};case"Z":return{g:0,c:null,s:"[+-]\\d{1,5}"};default:return{g:0,c:null,s:String.escape(b)}}};Date.prototype.getTimezone=function(){return this.toString().replace(/^.*? ([A-Z]{3}) [0-9]{4}.*$/,"$1").replace(/^.*?\(([A-Z])[a-z]+ ([A-Z])[a-z]+ ([A-Z])[a-z]+\)$/,"$1$2$3")};Date.prototype.getGMTOffset=function(){return(this.getTimezoneOffset()>0?"-":"+")+String.leftPad(Math.floor(Math.abs(this.getTimezoneOffset())/60),2,"0")+String.leftPad(Math.abs(this.getTimezoneOffset())%60,2,"0")};Date.prototype.getDayOfYear=function(){var a=0;Date.daysInMonth[1]=this.isLeapYear()?29:28;for(var b=0;b<this.getMonth();++b){a+=Date.daysInMonth[b]}return a+this.getDate()};Date.prototype.getWeekOfYear=function(){var b=this.getDayOfYear()+(4-this.getDay());var a=new Date(this.getFullYear(),0,1);var c=(7-a.getDay()+4);return String.leftPad(Math.ceil((b-c)/7)+1,2,"0")};Date.prototype.isLeapYear=function(){var a=this.getFullYear();return((a&3)==0&&(a%100||(a%400==0&&a)))};Date.prototype.getFirstDayOfMonth=function(){var a=(this.getDay()-(this.getDate()-1))%7;return(a<0)?(a+7):a};Date.prototype.getLastDayOfMonth=function(){var a=(this.getDay()+(Date.daysInMonth[this.getMonth()]-this.getDate()))%7;return(a<0)?(a+7):a};Date.prototype.getDaysInMonth=function(){Date.daysInMonth[1]=this.isLeapYear()?29:28;return Date.daysInMonth[this.getMonth()]};Date.prototype.getSuffix=function(){switch(this.getDate()){case 1:case 21:case 31:return"st";case 2:case 22:return"nd";case 3:case 23:return"rd";default:return"th"}};String.escape=function(a){return a.replace(/('|\\)/g,"\\$1")};String.leftPad=function(d,b,c){var a=new String(d);if(c==null){c=" "}while(a.length<b){a=c+a}return a};Date.daysInMonth=[31,28,31,30,31,30,31,31,30,31,30,31];Date.monthNames=["January","February","March","April","May","June","July","August","September","October","November","December"];Date.dayNames=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];Date.y2kYear=50;Date.monthNumbers={Jan:0,Feb:1,Mar:2,Apr:3,May:4,Jun:5,Jul:6,Aug:7,Sep:8,Oct:9,Nov:10,Dec:11};Date.patterns={ISO8601LongPattern:"Y-m-d H:i:s",ISO8601ShortPattern:"Y-m-d",ShortDatePattern:"n/j/Y",LongDatePattern:"l, F d, Y",FullDateTimePattern:"l, F d, Y g:i:s A",MonthDayPattern:"F d",ShortTimePattern:"g:i A",LongTimePattern:"g:i:s A",SortableDateTimePattern:"Y-m-d\\TH:i:s",UniversalSortableDateTimePattern:"Y-m-d H:i:sO",YearMonthPattern:"F, Y"}}()); \ No newline at end of file
diff --git a/js/jquery/plugins/datetimepicker/datetimepicker.css b/js/jquery/plugins/datetimepicker/datetimepicker.css
new file mode 100644
index 000000000..d281264a9
--- /dev/null
+++ b/js/jquery/plugins/datetimepicker/datetimepicker.css
@@ -0,0 +1,473 @@
+.xdsoft_datetimepicker {
+ box-shadow: 0 5px 15px -5px rgba(0, 0, 0, 0.506);
+ background: #fff;
+ border-bottom: 1px solid #bbb;
+ border-left: 1px solid #ccc;
+ border-right: 1px solid #ccc;
+ border-top: 1px solid #ccc;
+ color: #333;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ padding: 8px;
+ padding-left: 0;
+ padding-top: 2px;
+ position: absolute;
+ z-index: 9999;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ display: none;
+}
+
+.xdsoft_datetimepicker iframe {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 75px;
+ height: 210px;
+ background: transparent;
+ border: none;
+}
+
+/*For IE8 or lower*/
+.xdsoft_datetimepicker button {
+ border: none !important;
+}
+
+.xdsoft_noselect {
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -o-user-select: none;
+ user-select: none;
+}
+
+.xdsoft_noselect::selection { background: transparent }
+.xdsoft_noselect::-moz-selection { background: transparent }
+
+.xdsoft_datetimepicker.xdsoft_inline {
+ display: inline-block;
+ position: static;
+ box-shadow: none;
+}
+
+.xdsoft_datetimepicker * {
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ padding: 0;
+ margin: 0;
+}
+
+.xdsoft_datetimepicker .xdsoft_datepicker, .xdsoft_datetimepicker .xdsoft_timepicker {
+ display: none;
+}
+
+.xdsoft_datetimepicker .xdsoft_datepicker.active, .xdsoft_datetimepicker .xdsoft_timepicker.active {
+ display: block;
+}
+
+.xdsoft_datetimepicker .xdsoft_datepicker {
+ width: 224px;
+ float: left;
+ margin-left: 8px;
+}
+
+.xdsoft_datetimepicker.xdsoft_showweeks .xdsoft_datepicker {
+ width: 256px;
+}
+
+.xdsoft_datetimepicker .xdsoft_timepicker {
+ width: 58px;
+ float: left;
+ text-align: center;
+ margin-left: 8px;
+ margin-top: 0;
+}
+
+.xdsoft_datetimepicker .xdsoft_datepicker.active+.xdsoft_timepicker {
+ margin-top: 8px;
+ margin-bottom: 3px
+}
+
+.xdsoft_datetimepicker .xdsoft_mounthpicker {
+ position: relative;
+ text-align: center;
+}
+
+.xdsoft_datetimepicker .xdsoft_label i,
+.xdsoft_datetimepicker .xdsoft_prev,
+.xdsoft_datetimepicker .xdsoft_next,
+.xdsoft_datetimepicker .xdsoft_today_button {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAeCAYAAADaW7vzAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6Q0NBRjI1NjM0M0UwMTFFNDk4NkFGMzJFQkQzQjEwRUIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6Q0NBRjI1NjQ0M0UwMTFFNDk4NkFGMzJFQkQzQjEwRUIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpDQ0FGMjU2MTQzRTAxMUU0OTg2QUYzMkVCRDNCMTBFQiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpDQ0FGMjU2MjQzRTAxMUU0OTg2QUYzMkVCRDNCMTBFQiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PoNEP54AAAIOSURBVHja7Jq9TsMwEMcxrZD4WpBYeKUCe+kTMCACHZh4BFfHO/AAIHZGFhYkBBsSEqxsLCAgXKhbXYOTxh9pfJVP+qutnZ5s/5Lz2Y5I03QhWji2GIcgAokWgfCxNvcOCCGKqiSqhUp0laHOne05vdEyGMfkdxJDVjgwDlEQgYQBgx+ULJaWSXXS6r/ER5FBVR8VfGftTKcITNs+a1XpcFoExREIDF14AVIFxgQUS+h520cdud6wNkC0UBw6BCO/HoCYwBhD8QCkQ/x1mwDyD4plh4D6DDV0TAGyo4HcawLIBBSLDkHeH0Mg2yVP3l4TQMZQDDsEOl/MgHQqhMNuE0D+oBh0CIr8MAKyazBH9WyBuKxDWgbXfjNf32TZ1KWm/Ap1oSk/R53UtQ5xTh3LUlMmT8gt6g51Q9p+SobxgJQ/qmsfZhWywGFSl0yBjCLJCMgXail3b7+rumdVJ2YRss4cN+r6qAHDkPWjPjdJCF4n9RmAD/V9A/Wp4NQassDjwlB6XBiCxcJQWmZZb8THFilfy/lfrTvLghq2TqTHrRMTKNJ0sIhdo15RT+RpyWwFdY96UZ/LdQKBGjcXpcc1AlSFEfLmouD+1knuxBDUVrvOBmoOC/rEcN7OQxKVeJTCiAdUzUJhA2Oez9QTkp72OTVcxDcXY8iKNkxGAJXmJCOQwOa6dhyXsOa6XwEGAKdeb5ET3rQdAAAAAElFTkSuQmCC);
+}
+
+.xdsoft_datetimepicker .xdsoft_label i {
+ opacity: 0.5;
+ background-position: -92px -19px;
+ display: inline-block;
+ width: 9px;
+ height: 20px;
+ vertical-align: middle;
+}
+
+.xdsoft_datetimepicker .xdsoft_prev {
+ float: left;
+ background-position: -20px 0;
+}
+.xdsoft_datetimepicker .xdsoft_today_button {
+ float: left;
+ background-position: -70px 0;
+ margin-left: 5px;
+}
+
+.xdsoft_datetimepicker .xdsoft_next {
+ float: right;
+ background-position: 0 0;
+}
+
+.xdsoft_datetimepicker .xdsoft_next,
+.xdsoft_datetimepicker .xdsoft_prev ,
+.xdsoft_datetimepicker .xdsoft_today_button {
+ background-color: transparent;
+ background-repeat: no-repeat;
+ border: 0 none;
+ cursor: pointer;
+ display: block;
+ height: 30px;
+ opacity: 0.5;
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
+ outline: medium none;
+ overflow: hidden;
+ padding: 0;
+ position: relative;
+ text-indent: 100%;
+ white-space: nowrap;
+ width: 20px;
+}
+
+.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_prev,
+.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_next {
+ float: none;
+ background-position: -40px -15px;
+ height: 15px;
+ width: 30px;
+ display: block;
+ margin-left: 14px;
+ margin-top: 7px;
+}
+
+.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_prev {
+ background-position: -40px 0;
+ margin-bottom: 7px;
+ margin-top: 0;
+}
+
+.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box {
+ height: 151px;
+ overflow: hidden;
+ border-bottom: 1px solid #ddd;
+}
+
+.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box >div >div {
+ background: #f5f5f5;
+ border-top: 1px solid #ddd;
+ color: #666;
+ font-size: 12px;
+ text-align: center;
+ border-collapse: collapse;
+ cursor: pointer;
+ border-bottom-width: 0;
+ height: 25px;
+ line-height: 25px;
+}
+
+.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box >div > div:first-child {
+ border-top-width: 0;
+}
+
+.xdsoft_datetimepicker .xdsoft_today_button:hover,
+.xdsoft_datetimepicker .xdsoft_next:hover,
+.xdsoft_datetimepicker .xdsoft_prev:hover {
+ opacity: 1;
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
+}
+
+.xdsoft_datetimepicker .xdsoft_label {
+ display: inline;
+ position: relative;
+ z-index: 9999;
+ margin: 0;
+ padding: 5px 3px;
+ font-size: 14px;
+ line-height: 20px;
+ font-weight: bold;
+ background-color: #fff;
+ float: left;
+ width: 182px;
+ text-align: center;
+ cursor: pointer;
+}
+
+.xdsoft_datetimepicker .xdsoft_label:hover>span {
+ text-decoration: underline;
+}
+
+.xdsoft_datetimepicker .xdsoft_label:hover i {
+ opacity: 1.0;
+}
+
+.xdsoft_datetimepicker .xdsoft_label > .xdsoft_select {
+ border: 1px solid #ccc;
+ position: absolute;
+ right: 0;
+ top: 30px;
+ z-index: 101;
+ display: none;
+ background: #fff;
+ max-height: 160px;
+ overflow-y: hidden;
+}
+
+.xdsoft_datetimepicker .xdsoft_label > .xdsoft_select.xdsoft_monthselect{ right: -7px }
+.xdsoft_datetimepicker .xdsoft_label > .xdsoft_select.xdsoft_yearselect{ right: 2px }
+.xdsoft_datetimepicker .xdsoft_label > .xdsoft_select > div > .xdsoft_option:hover {
+ color: #fff;
+ background: #ff8000;
+}
+
+.xdsoft_datetimepicker .xdsoft_label > .xdsoft_select > div > .xdsoft_option {
+ padding: 2px 10px 2px 5px;
+ text-decoration: none !important;
+}
+
+.xdsoft_datetimepicker .xdsoft_label > .xdsoft_select > div > .xdsoft_option.xdsoft_current {
+ background: #33aaff;
+ box-shadow: #178fe5 0 1px 3px 0 inset;
+ color: #fff;
+ font-weight: 700;
+}
+
+.xdsoft_datetimepicker .xdsoft_month {
+ width: 100px;
+ text-align: right;
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar {
+ clear: both;
+}
+
+.xdsoft_datetimepicker .xdsoft_year{
+ width: 48px;
+ margin-left: 5px;
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar table {
+ border-collapse: collapse;
+ width: 100%;
+
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar td > div {
+ padding-right: 5px;
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar th {
+ height: 25px;
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar td,.xdsoft_datetimepicker .xdsoft_calendar th {
+ width: 14.2857142%;
+ background: #f5f5f5;
+ border: 1px solid #ddd;
+ color: #666;
+ font-size: 12px;
+ text-align: right;
+ vertical-align: middle;
+ padding: 0;
+ border-collapse: collapse;
+ cursor: pointer;
+ height: 25px;
+}
+.xdsoft_datetimepicker.xdsoft_showweeks .xdsoft_calendar td,.xdsoft_datetimepicker.xdsoft_showweeks .xdsoft_calendar th {
+ width: 12.5%;
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar th {
+ background: #f1f1f1;
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_today {
+ color: #33aaff;
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_default,
+.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_current,
+.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box >div >div.xdsoft_current {
+ background: #33aaff;
+ box-shadow: #178fe5 0 1px 3px 0 inset;
+ color: #fff;
+ font-weight: 700;
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_other_month,
+.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_disabled,
+.xdsoft_datetimepicker .xdsoft_time_box >div >div.xdsoft_disabled {
+ opacity: 0.5;
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
+ cursor: default;
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_other_month.xdsoft_disabled {
+ opacity: 0.2;
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=20)";
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar td:hover,
+.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box >div >div:hover {
+ color: #fff !important;
+ background: #ff8000 !important;
+ box-shadow: none !important;
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_current.xdsoft_disabled:hover,
+.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box>div>div.xdsoft_current.xdsoft_disabled:hover {
+ background: #33aaff !important;
+ box-shadow: #178fe5 0 1px 3px 0 inset !important;
+ color: #fff !important;
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_disabled:hover,
+.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box >div >div.xdsoft_disabled:hover {
+ color: inherit !important;
+ background: inherit !important;
+ box-shadow: inherit !important;
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar th {
+ font-weight: 700;
+ text-align: center;
+ color: #999;
+ cursor: default;
+}
+
+.xdsoft_datetimepicker .xdsoft_copyright {
+ color: #ccc !important;
+ font-size: 10px;
+ clear: both;
+ float: none;
+ margin-left: 8px;
+}
+
+.xdsoft_datetimepicker .xdsoft_copyright a { color: #eee !important }
+.xdsoft_datetimepicker .xdsoft_copyright a:hover { color: #aaa !important }
+
+.xdsoft_time_box {
+ position: relative;
+ border: 1px solid #ccc;
+}
+.xdsoft_scrollbar >.xdsoft_scroller {
+ background: #ccc !important;
+ height: 20px;
+ border-radius: 3px;
+}
+.xdsoft_scrollbar {
+ position: absolute;
+ width: 7px;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ cursor: pointer;
+}
+.xdsoft_scroller_box {
+ position: relative;
+}
+
+.xdsoft_datetimepicker.xdsoft_dark {
+ box-shadow: 0 5px 15px -5px rgba(255, 255, 255, 0.506);
+ background: #000;
+ border-bottom: 1px solid #444;
+ border-left: 1px solid #333;
+ border-right: 1px solid #333;
+ border-top: 1px solid #333;
+ color: #ccc;
+}
+
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box {
+ border-bottom: 1px solid #222;
+}
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box >div >div {
+ background: #0a0a0a;
+ border-top: 1px solid #222;
+ color: #999;
+}
+
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label {
+ background-color: #000;
+}
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label > .xdsoft_select {
+ border: 1px solid #333;
+ background: #000;
+}
+
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label > .xdsoft_select > div > .xdsoft_option:hover {
+ color: #000;
+ background: #007fff;
+}
+
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label > .xdsoft_select > div > .xdsoft_option.xdsoft_current {
+ background: #cc5500;
+ box-shadow: #b03e00 0 1px 3px 0 inset;
+ color: #000;
+}
+
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label i,
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_prev,
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_next,
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_today_button {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAeCAYAAADaW7vzAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QUExQUUzOTA0M0UyMTFFNDlBM0FFQTJENTExRDVBODYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QUExQUUzOTE0M0UyMTFFNDlBM0FFQTJENTExRDVBODYiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpBQTFBRTM4RTQzRTIxMUU0OUEzQUVBMkQ1MTFENUE4NiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBQTFBRTM4RjQzRTIxMUU0OUEzQUVBMkQ1MTFENUE4NiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pp0VxGEAAAIASURBVHja7JrNSgMxEMebtgh+3MSLr1T1Xn2CHoSKB08+QmR8Bx9A8e7RixdB9CKCoNdexIugxFlJa7rNZneTbLIpM/CnNLsdMvNjM8l0mRCiQ9Ye61IKCAgZAUnH+mU3MMZaHYChBnJUDzWOFZdVfc5+ZFLbrWDeXPwbxIqrLLfaeS0hEBVGIRQCEiZoHQwtlGSByCCdYBl8g8egTTAWoKQMRBRBcZxYlhzhKegqMOageErsCHVkk3hXIFooDgHB1KkHIHVgzKB4ADJQ/A1jAFmAYhkQqA5TOBtocrKrgXwQA8gcFIuAIO8sQSA7hidvPwaQGZSaAYHOUWJABhWWw2EMIH9QagQERU4SArJXo0ZZL18uvaxejXt/Em8xjVBXmvFr1KVm/AJ10tRe2XnraNqaJvKE3KHuUbfK1E+VHB0q40/y3sdQSxY4FHWeKJCunP8UyDdqJZenT3ntVV5jIYCAh20vT7ioP8tpf6E2lfEMwERe+whV1MHjwZB7PBiCxcGQWwKZKD62lfGNnP/1poFAA60T7rF1UgcKd2id3KDeUS+oLWV8DfWAepOfq00CgQabi9zjcgJVYVD7PVzQUAUGAQkbNJTBICDhgwYTjDYD6XeW08ZKh+A4pYkzenOxXUbvZcWz7E8ykRMnIHGX1XPl+1m2vPYpL+2qdb8CDAARlKFEz/ZVkAAAAABJRU5ErkJggg==);
+}
+
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td,
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar th {
+ background: #0a0a0a;
+ border: 1px solid #222;
+ color: #999;
+}
+
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar th {
+ background: #0e0e0e;
+}
+
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_today {
+ color: #cc5500;
+}
+
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_default,
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_current,
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box >div >div.xdsoft_current {
+ background: #cc5500;
+ box-shadow: #b03e00 0 1px 3px 0 inset;
+ color: #000;
+}
+
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td:hover,
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box >div >div:hover {
+ color: #000 !important;
+ background: #007fff !important;
+}
+
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar th {
+ color: #666;
+}
+
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_copyright { color: #333 !important }
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_copyright a { color: #111 !important }
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_copyright a:hover { color: #555 !important }
+
+.xdsoft_dark .xdsoft_time_box {
+ border: 1px solid #333;
+}
+
+.xdsoft_dark .xdsoft_scrollbar >.xdsoft_scroller {
+ background: #333 !important;
+} \ No newline at end of file
diff --git a/process_bug.cgi b/process_bug.cgi
index 75c59b084..1a2b61717 100755
--- a/process_bug.cgi
+++ b/process_bug.cgi
@@ -446,7 +446,17 @@ elsif ($action eq 'next_bug' or $action eq 'same_bug') {
if ($action eq 'next_bug') {
$vars->{'nextbug'} = $bug->id;
}
- $template->process("bug/show.html.tmpl", $vars)
+
+ # BMO: add show_bug_format hook for experimental UI work
+ my $format_params = {
+ format => scalar $cgi->param('format'),
+ ctype => scalar $cgi->param('ctype'),
+ };
+ Bugzilla::Hook::process('show_bug_format', $format_params);
+ my $format = $template->get_format("bug/show",
+ $format_params->{format},
+ $format_params->{ctype});
+ $template->process($format->{template}, $vars)
|| ThrowTemplateError($template->error());
exit;
}
diff --git a/show_bug.cgi b/show_bug.cgi
index 332d5fcbd..06d17e352 100755
--- a/show_bug.cgi
+++ b/show_bug.cgi
@@ -30,6 +30,7 @@ use Bugzilla::Error;
use Bugzilla::User;
use Bugzilla::Keyword;
use Bugzilla::Bug;
+use Bugzilla::Hook;
my $cgi = Bugzilla->cgi;
my $template = Bugzilla->template;
@@ -37,11 +38,19 @@ my $vars = {};
my $user = Bugzilla->login();
-my $format = $template->get_format("bug/show", scalar $cgi->param('format'),
- scalar $cgi->param('ctype'));
+# BMO: add show_bug_format for experimental UI work
+my $format_params = {
+ format => scalar $cgi->param('format'),
+ ctype => scalar $cgi->param('ctype'),
+};
+Bugzilla::Hook::process('show_bug_format', $format_params);
+my $format = $template->get_format("bug/show",
+ $format_params->{format},
+ $format_params->{ctype});
# Editable, 'single' HTML bugs are treated slightly specially in a few places
-my $single = !$format->{format} && $format->{extension} eq 'html';
+my $single = (!$format->{format} || $format->{format} ne 'multiple')
+ && $format->{extension} eq 'html';
# If we don't have an ID, _AND_ we're only doing a single bug, then prompt
if (!$cgi->param('id') && $single) {
diff --git a/skins/contrib/Mozilla-OpenSans/global.css b/skins/contrib/Mozilla-OpenSans/global.css
index d498ef82f..8fd7d64a7 100644
--- a/skins/contrib/Mozilla-OpenSans/global.css
+++ b/skins/contrib/Mozilla-OpenSans/global.css
@@ -81,7 +81,7 @@ a:hover, #header a:hover, #footer a:hover {
}
select[multiple], textarea, input[type=text], input[type=password],
-input[type=email], input[type=url], input:not([type]), .text_input, .yui-ac-input {
+input[type=email], input[type=url], input[type=number], input:not([type]), .text_input, .yui-ac-input {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
@@ -481,19 +481,19 @@ table.edit_form hr {
}
.bz_comment_user a {
- -moz-transition: all 0.25s linear 0s;
- -webkit-transition: all 0.25s linear 0s;
- transition: all 0.25s linear 0s;
- transition: all 0.25s linear 0s;
+ -moz-transition: all 100ms linear 0s;
+ -webkit-transition: all 100ms linear 0s;
+ transition: all 100ms linear 0s;
+ transition: all 100ms linear 0s;
color: #0095dd;
padding: 0px;
margin: 0px;
}
.bz_comment_user a:hover {
- -moz-transition: all 0.25s linear 0s;
- -webkit-transition: all 0.25s linear 0s;
- transition: all 0.25s linear 0s;
+ -moz-transition: all 100ms linear 0s;
+ -webkit-transition: all 100ms linear 0s;
+ transition: all 100ms linear 0s;
background: #fff;
border: none;
text-decoration: none;
@@ -654,6 +654,16 @@ table.edit_form hr {
color: #bbb;
width: auto;
margin-bottom: 1em;
+ margin-left: 6px;
+}
+
+#links-actions {
+ margin-left: -10px;
+}
+
+#footer form input {
+ padding: 1px 2px;
+ margin-top: 3px;
}
#privacy-policy {
@@ -665,9 +675,9 @@ button, input[type=submit], input[type=button], #commit, #commit_top, #header .b
background-image: -moz-linear-gradient(#43a6e2,#277ac1);
background-image: -webkit-linear-gradient(#43a6e2,#277ac1);
background-image: linear-gradient(#43a6e2,#277ac1);
- -moz-transition: all linear 0.25s;
- -webkit-transition: all linear 0.25s;
- transition: all linear 0.25s;
+ -moz-transition: all linear 100ms;
+ -webkit-transition: all linear 100ms;
+ transition: all linear 100ms;
border-radius: .25em;
border: 0px none;
box-shadow: 0 1px 0 0 rgba(0,0,0,0.2),inset 0 -1px 0 0 rgba(0,0,0,0.3);
@@ -686,18 +696,18 @@ button:hover, input[type=submit]:hover, input[type=button]:hover, #commit:hover,
-webkit-box-shadow: 0 1px 0 0 rgba(0,0,0,0.2),inset 0 -1px 0 0 rgba(0,0,0,0.3),inset 0 12px 24px 2px #38a9ed;
-moz-box-shadow: 0 1px 0 0 rgba(0,0,0,0.2),inset 0 -1px 0 0 rgba(0,0,0,0.3),inset 0 12px 24px 2px #38a9ed;
box-shadow: 0 1px 0 0 rgba(0,0,0,0.2),inset 0 -1px 0 0 rgba(0,0,0,0.3),inset 0 12px 24px 2px #38a9ed;
- -moz-transition: all linear 0.25s;
- -webkit-transition: all linear 0.25s;
- transition: all linear 0.25s;
+ -moz-transition: all linear 100ms;
+ -webkit-transition: all linear 100ms;
+ transition: all linear 100ms;
}
button:active, input[type=submit]:active, input[type=button]:active, #commit:active, #commit_top:active, #header .btn:active, #header input[type=submit]:active {
-webkit-box-shadow: inset 0 2px 0 0 rgba(0,0,0,0.2),inset 0 12px 24px 6px rgba(0,0,0,0.2),inset 0 0 2px 2px rgba(0,0,0,0.2);
-moz-box-shadow: inset 0 2px 0 0 rgba(0,0,0,0.2),inset 0 12px 24px 6px rgba(0,0,0,0.2),inset 0 0 2px 2px rgba(0,0,0,0.2);
box-shadow: inset 0 2px 0 0 rgba(0,0,0,0.2),inset 0 12px 24px 6px rgba(0,0,0,0.2),inset 0 0 2px 2px rgba(0,0,0,0.2);
- -moz-transition: all linear 0.25s;
- -webkit-transition: all linear 0.25s;
- transition: all linear 0.25s;
+ -moz-transition: all linear 100ms;
+ -webkit-transition: all linear 100ms;
+ transition: all linear 100ms;
}
button[disabled], input[type=submit][disabled], input[type=button][disabled], button[disabled]:hover, input[type=submit][disabled]:hover, input[type=button][disabled]:hover, button[disabled]:active, input[type=submit][disabled]:active, input[type=button][disabled]:active {
@@ -787,93 +797,3 @@ table.tabs {
#summary_field.search_field_row input {
padding-bottom: 6px;
}
-
-/* Smaller than standard 990 (devices and browsers) */
-@media only screen and (max-width: 989px) {
- #header .links {
- float: none;
- }
-}
-
-/* Tablet Portrait size to standard 990 */
-@media only screen and (min-width: 768px) and (max-width: 989px) {
- body {
- min-width: 768px;
- }
-
- #header .wrapper, #bugzilla-body {
- -moz-box-sizing: border-box;
- -webkit-box-sizing: border-box;
- box-sizing: border-box;
- min-width: 768px;
- }
-
- #footer > * {
- -moz-box-sizing: border-box;
- -webkit-box-sizing: border-box;
- box-sizing: border-box;
- min-width: 768px;
- }
-}
-
-/* All Mobile Sizes */
-@media only screen and (max-width: 767px) {
- table.edit_form, table.edit_form > tbody > tr > td {
- -moz-box-sizing: border-box;
- -webkit-box-sizing: border-box;
- box-sizing: border-box;
- display: block;
- width: 100% !important;
- }
-
- .bz_column_spacer {
- width: auto;
- height: 20px;
- }
-}
-
-
-
-/* Mobile Landscape Size to Tablet Portrait */
-@media only screen and (min-width: 480px) and (max-width: 767px) {
- body {
- min-width: 480px;
- }
-
- #header .wrapper, #bugzilla-body, #attachment_table,
- .bz_comment, #add_comment > table, #comment {
- -moz-box-sizing: border-box;
- -webkit-box-sizing: border-box;
- box-sizing: border-box;
- width: 480px !important;
- }
-
- #footer > * {
- -moz-box-sizing: border-box;
- -webkit-box-sizing: border-box;
- box-sizing: border-box;
- width: 480px;
- }
-}
-
-/* Mobile Portrait Size to Mobile Landscape Size */
-@media only screen and (max-width: 479px) {
- body {
- min-width: 100%;
- }
-
- #header .wrapper, #bugzilla-body, #attachment_table,
- .bz_comment, #add_comment > table, #comment {
- -moz-box-sizing: border-box;
- -webkit-box-sizing: border-box;
- box-sizing: border-box;
- width: 100% !important;
- }
-
- #footer > * {
- -moz-box-sizing: border-box;
- -webkit-box-sizing: border-box;
- box-sizing: border-box;
- width: 100%;
- }
-}
diff --git a/skins/contrib/Mozilla/global.css b/skins/contrib/Mozilla/global.css
index cd88d4dd8..be5755e95 100644
--- a/skins/contrib/Mozilla/global.css
+++ b/skins/contrib/Mozilla/global.css
@@ -124,7 +124,7 @@ a:hover, #header a:hover, #footer a:hover {
}
select[multiple], textarea, input[type=text], input[type=password],
-input[type=email], input[type=url], input:not([type]), .text_input, .yui-ac-input {
+input[type=email], input[type=url], input[type=number], input:not([type]), .text_input, .yui-ac-input {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
@@ -524,19 +524,19 @@ table.edit_form hr {
}
.bz_comment_user a {
- -moz-transition: all 0.25s linear 0s;
- -webkit-transition: all 0.25s linear 0s;
- transition: all 0.25s linear 0s;
- transition: all 0.25s linear 0s;
+ -moz-transition: all 100ms linear 0s;
+ -webkit-transition: all 100ms linear 0s;
+ transition: all 100ms linear 0s;
+ transition: all 100ms linear 0s;
color: #0095dd;
padding: 0px;
margin: 0px;
}
.bz_comment_user a:hover {
- -moz-transition: all 0.25s linear 0s;
- -webkit-transition: all 0.25s linear 0s;
- transition: all 0.25s linear 0s;
+ -moz-transition: all 100ms linear 0s;
+ -webkit-transition: all 100ms linear 0s;
+ transition: all 100ms linear 0s;
background: #fff;
border: none;
text-decoration: none;
@@ -697,6 +697,16 @@ table.edit_form hr {
color: #bbb;
width: auto;
margin-bottom: 1em;
+ margin-left: 6px;
+}
+
+#links-actions, #links-saved {
+ margin-left: -10px;
+}
+
+#footer form input {
+ padding: 1px 2px;
+ margin-top: 3px;
}
#privacy-policy {
@@ -708,9 +718,9 @@ button, input[type=submit], input[type=button], #commit, #commit_top, #header .b
background-image: -moz-linear-gradient(#43a6e2,#277ac1);
background-image: -webkit-linear-gradient(#43a6e2,#277ac1);
background-image: linear-gradient(#43a6e2,#277ac1);
- -moz-transition: all linear 0.25s;
- -webkit-transition: all linear 0.25s;
- transition: all linear 0.25s;
+ -moz-transition: all linear 100ms;
+ -webkit-transition: all linear 100ms;
+ transition: all linear 100ms;
border-radius: .25em;
border: 0px none;
box-shadow: 0 1px 0 0 rgba(0,0,0,0.2),inset 0 -1px 0 0 rgba(0,0,0,0.3);
@@ -729,18 +739,18 @@ button:hover, input[type=submit]:hover, input[type=button]:hover, #commit:hover,
-webkit-box-shadow: 0 1px 0 0 rgba(0,0,0,0.2),inset 0 -1px 0 0 rgba(0,0,0,0.3),inset 0 12px 24px 2px #38a9ed;
-moz-box-shadow: 0 1px 0 0 rgba(0,0,0,0.2),inset 0 -1px 0 0 rgba(0,0,0,0.3),inset 0 12px 24px 2px #38a9ed;
box-shadow: 0 1px 0 0 rgba(0,0,0,0.2),inset 0 -1px 0 0 rgba(0,0,0,0.3),inset 0 12px 24px 2px #38a9ed;
- -moz-transition: all linear 0.25s;
- -webkit-transition: all linear 0.25s;
- transition: all linear 0.25s;
+ -moz-transition: all linear 100ms;
+ -webkit-transition: all linear 100ms;
+ transition: all linear 100ms;
}
button:active, input[type=submit]:active, input[type=button]:active, #commit:active, #commit_top:active, #header .btn:active, #header input[type=submit]:active {
-webkit-box-shadow: inset 0 2px 0 0 rgba(0,0,0,0.2),inset 0 12px 24px 6px rgba(0,0,0,0.2),inset 0 0 2px 2px rgba(0,0,0,0.2);
-moz-box-shadow: inset 0 2px 0 0 rgba(0,0,0,0.2),inset 0 12px 24px 6px rgba(0,0,0,0.2),inset 0 0 2px 2px rgba(0,0,0,0.2);
box-shadow: inset 0 2px 0 0 rgba(0,0,0,0.2),inset 0 12px 24px 6px rgba(0,0,0,0.2),inset 0 0 2px 2px rgba(0,0,0,0.2);
- -moz-transition: all linear 0.25s;
- -webkit-transition: all linear 0.25s;
- transition: all linear 0.25s;
+ -moz-transition: all linear 100ms;
+ -webkit-transition: all linear 100ms;
+ transition: all linear 100ms;
}
button[disabled], input[type=submit][disabled], input[type=button][disabled], button[disabled]:hover, input[type=submit][disabled]:hover, input[type=button][disabled]:hover, button[disabled]:active, input[type=submit][disabled]:active, input[type=button][disabled]:active {
@@ -830,93 +840,3 @@ table.tabs {
#summary_field.search_field_row input {
padding-bottom: 6px;
}
-
-/* Smaller than standard 990 (devices and browsers) */
-@media only screen and (max-width: 989px) {
- #header .links {
- float: none;
- }
-}
-
-/* Tablet Portrait size to standard 990 */
-@media only screen and (min-width: 768px) and (max-width: 989px) {
- body {
- min-width: 768px;
- }
-
- #header .wrapper, #bugzilla-body {
- -moz-box-sizing: border-box;
- -webkit-box-sizing: border-box;
- box-sizing: border-box;
- min-width: 768px;
- }
-
- #footer > * {
- -moz-box-sizing: border-box;
- -webkit-box-sizing: border-box;
- box-sizing: border-box;
- min-width: 768px;
- }
-}
-
-/* All Mobile Sizes */
-@media only screen and (max-width: 767px) {
- table.edit_form, table.edit_form > tbody > tr > td {
- -moz-box-sizing: border-box;
- -webkit-box-sizing: border-box;
- box-sizing: border-box;
- display: block;
- width: 100% !important;
- }
-
- .bz_column_spacer {
- width: auto;
- height: 20px;
- }
-}
-
-
-
-/* Mobile Landscape Size to Tablet Portrait */
-@media only screen and (min-width: 480px) and (max-width: 767px) {
- body {
- min-width: 480px;
- }
-
- #header .wrapper, #bugzilla-body, #attachment_table,
- .bz_comment, #add_comment > table, #comment {
- -moz-box-sizing: border-box;
- -webkit-box-sizing: border-box;
- box-sizing: border-box;
- width: 480px !important;
- }
-
- #footer > * {
- -moz-box-sizing: border-box;
- -webkit-box-sizing: border-box;
- box-sizing: border-box;
- width: 480px;
- }
-}
-
-/* Mobile Portrait Size to Mobile Landscape Size */
-@media only screen and (max-width: 479px) {
- body {
- min-width: 100%;
- }
-
- #header .wrapper, #bugzilla-body, #attachment_table,
- .bz_comment, #add_comment > table, #comment {
- -moz-box-sizing: border-box;
- -webkit-box-sizing: border-box;
- box-sizing: border-box;
- width: 100% !important;
- }
-
- #footer > * {
- -moz-box-sizing: border-box;
- -webkit-box-sizing: border-box;
- box-sizing: border-box;
- width: 100%;
- }
-}
diff --git a/t/008filter.t b/t/008filter.t
index 74e653434..1111c16f6 100644
--- a/t/008filter.t
+++ b/t/008filter.t
@@ -225,7 +225,7 @@ sub directive_ok {
return 1 if $directive =~ /FILTER\ (html|csv|js|base64|css_class_quote|ics|
quoteUrls|time|uri|xml|lower|html_light|
obsolete|inactive|closed|unitconvert|
- txt|html_linebreak|none|json|null)\b/x;
+ txt|html_linebreak|none|json|null|id)\b/x;
return 0;
}
diff --git a/template/en/default/attachment/created.html.tmpl b/template/en/default/attachment/created.html.tmpl
index da2fec823..92bd745a2 100644
--- a/template/en/default/attachment/created.html.tmpl
+++ b/template/en/default/attachment/created.html.tmpl
@@ -26,7 +26,14 @@
[% PROCESS global/variables.none.tmpl %]
[% bug = bugs.0 %]
-[% PROCESS "bug/show-header.html.tmpl" %]
+
+[%# BMO: allow experimental UIs to replace show-header %]
+[% IF alt_ui_header %]
+ [% PROCESS $alt_ui_header %]
+[% ELSE %]
+ [% PROCESS "bug/show-header.html.tmpl" %]
+[% END %]
+
[% PROCESS global/header.html.tmpl
title = "Attachment $attachment.id added to $terms.Bug $attachment.bug_id"
%]
@@ -64,4 +71,9 @@
Another Attachment to [% terms.Bug %] [%+ attachment.bug_id %]</a>
</p>
-[% PROCESS bug/show.html.tmpl %]
+[%# BMO: allow experimental UIs to replace show-bug %]
+[% IF alt_ui_show %]
+ [% PROCESS $alt_ui_show %]
+[% ELSE %]
+ [% PROCESS "bug/show.html.tmpl" %]
+[% END %]
diff --git a/template/en/default/attachment/updated.html.tmpl b/template/en/default/attachment/updated.html.tmpl
index 9a74f5c98..5b37231bd 100644
--- a/template/en/default/attachment/updated.html.tmpl
+++ b/template/en/default/attachment/updated.html.tmpl
@@ -26,7 +26,13 @@
[% PROCESS global/variables.none.tmpl %]
[% bug = bugs.0 %]
-[% PROCESS "bug/show-header.html.tmpl" %]
+[%# BMO: allow experimental UIs to replace show-header %]
+[% IF alt_ui_header %]
+ [% PROCESS $alt_ui_header %]
+[% ELSE %]
+ [% PROCESS "bug/show-header.html.tmpl" %]
+[% END %]
+
[% PROCESS global/header.html.tmpl
title = "Changes Submitted to Attachment $attachment.id of $terms.Bug $attachment.bug_id"
%]
@@ -43,4 +49,9 @@
</dd>
</dl>
-[% PROCESS bug/show.html.tmpl %]
+[%# BMO: allow experimental UIs to replace show-bug %]
+[% IF alt_ui_show %]
+ [% PROCESS $alt_ui_show %]
+[% ELSE %]
+ [% PROCESS "bug/show.html.tmpl" %]
+[% END %]
diff --git a/template/en/default/bug/create/created.html.tmpl b/template/en/default/bug/create/created.html.tmpl
index d9eaccbbf..4adad2d59 100644
--- a/template/en/default/bug/create/created.html.tmpl
+++ b/template/en/default/bug/create/created.html.tmpl
@@ -30,7 +30,13 @@
[% PROCESS global/variables.none.tmpl %]
-[% PROCESS "bug/show-header.html.tmpl" %]
+[%# BMO: allow experimental UIs to replace show-header %]
+[% IF alt_ui_header %]
+ [% PROCESS $alt_ui_header %]
+[% ELSE %]
+ [% PROCESS "bug/show-header.html.tmpl" %]
+[% END %]
+
[% PROCESS global/header.html.tmpl
title = "$terms.Bug $id Submitted &ndash; $filtered_desc"
header = "$terms.Bug&nbsp;$id Submitted"
@@ -39,7 +45,7 @@
[% header_done = 1 %]
[% FOREACH item = sentmail %]
- [% PROCESS bug/process/results.html.tmpl
+ [% INCLUDE bug/process/results.html.tmpl
type = item.type
id = item.id
sent_bugmail = item
@@ -50,12 +56,14 @@
<hr>
-[% PROCESS bug/edit.html.tmpl %]
-
-<hr>
-
-[% PROCESS bug/navigate.html.tmpl bottom_navigator => 1 %]
-
-<br>
+[%# BMO: allow experimental UIs to replace show-header %]
+[% IF alt_ui_edit %]
+ [% PROCESS $alt_ui_edit %]
+[% ELSE %]
+ [% PROCESS "bug/edit.html.tmpl" %]
+ <hr>
+ [% PROCESS bug/navigate.html.tmpl bottom_navigator => 1 %]
+ <br>
+[% END %]
[% PROCESS global/footer.html.tmpl %]
diff --git a/template/en/default/bug/process/header.html.tmpl b/template/en/default/bug/process/header.html.tmpl
index 6b608b9ed..8c0825ef8 100644
--- a/template/en/default/bug/process/header.html.tmpl
+++ b/template/en/default/bug/process/header.html.tmpl
@@ -26,7 +26,12 @@
[% USE Bugzilla %]
-[% PROCESS "bug/show-header.html.tmpl" %]
+[%# BMO: allow experimental UIs to replace show-header %]
+[% IF alt_ui_header %]
+ [% PROCESS $alt_ui_header %]
+[% ELSE %]
+ [% PROCESS "bug/show-header.html.tmpl" %]
+[% END %]
[% IF title_tag == "bug_processed" %]
[% title = BLOCK %]