diff options
44 files changed, 4980 insertions, 275 deletions
@@ -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> + • + <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> + • + <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 %]&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; + ' → '; + 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, '…') 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, '…') 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 %]&action=edit">Details</a> + [% IF attachment.ispatch %] + | <a href="attachment.cgi?id=[% attachment.id FILTER none %]&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 %] + [%~ '&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 ↓</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">▸</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">▸</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 %]& + [%~ %]product=[% bug.product FILTER uri %]& + [%~ %]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">▸</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 %]&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, '…')); + 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 ↑</button> + <button type="button" id="new-bug-btn" class="minor">New [% terms.Bug %] ▾</button> + </div> + </div> + </form> +[% ELSE %] + <div id="login-required"> + You need to <a href="show_bug.cgi?id=[% bug.bug_id FILTER none %]&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 ? "▸" : "▾" %]</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"> </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 Binary files differnew file mode 100644 index 000000000..8bad6a3e3 --- /dev/null +++ b/extensions/BugModal/web/ZeroClipboard/ZeroClipboard.swf 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('▸'); + }); + 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('▾'); + }); + 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') ? '▾' : '▸'); + }); + }); + + // 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('▸'); + el_for.hide(); + } + else { + latch.data('expanded', true).html('▾'); + 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('▸'); + $('#cc-list').hide(); + } + else { + latch.data('expanded', true).html('▾'); + $('#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: '… in this product', + callback: function() { + window.open('enter_bug.cgi?product=' + encodeURIComponent($('#product').val()), '_blank'); + } + }, + { + name: '… in this component', + callback: function() { + window.open('enter_bug.cgi?' + + 'product=' + encodeURIComponent($('#product').val()) + + '&component=' + encodeURIComponent($('#component').val()), '_blank'); + } + }, + { + name: '… that blocks this bug', + callback: function() { + window.open('enter_bug.cgi?format=__default__' + + '&product=' + encodeURIComponent($('#product').val()) + + '&blocked=' + BUGZILLA.bug_id, '_blank'); + } + }, + { + name: '… that depends on this bug', + callback: function() { + window.open('enter_bug.cgi?format=__default__' + + '&product=' + encodeURIComponent($('#product').val()) + + '&dependson=' + BUGZILLA.bug_id, '_blank'); + } + }, + { + name: '… 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: '… 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 Binary files differnew file mode 100644 index 000000000..5eef6597a --- /dev/null +++ b/extensions/BugModal/web/calendar.png diff --git a/extensions/BugModal/web/throbber.gif b/extensions/BugModal/web/throbber.gif Binary files differnew file mode 100644 index 000000000..bc4fa6561 --- /dev/null +++ b/extensions/BugModal/web/throbber.gif 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 – $filtered_desc" header = "$terms.Bug $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 %] |