Parcourir la source

ListView refactor (#2715)

* [list_settings] save last_view, kanban_board, gantt_mode

* listview.js cleanup

* image_view refactor

* image_view done

* wip

* [photoswipe] wip

* show view for calendar, kanban even if no results

* [gantt_view] refactor into separate file

* [imageview] 3 column border fix

* [imageview] gallery working

* delete old libs

* indentation to tabs

* [gantt] update lib, custom popup html

* custom fontawesome checkbox

* reset gantt state when not permitted

* checkbox styling fix

* working commit

* image, calendar, gantt view working

* more refactoring, kanban view

* minor

* removed old files

* user settings improved

* filters and sort selector de-coupling

* wip

* [imageview] white pswp background

* kanban filters saving fixed

* fixed reportview

* minor

* removed listing.js

* minor fixes and cleanup

* patch for UserSettings table

* patch fix
version-14
Faris Ansari il y a 8 ans
committed by Rushabh Mehta
Parent
révision
1a76d64781
73 fichiers modifiés avec 9453 ajouts et 4877 suppressions
  1. +0
    -1
      frappe/core/doctype/communication/communication_list.js
  2. +2
    -2
      frappe/core/doctype/file/file_list.js
  3. +2
    -2
      frappe/desk/form/load.py
  4. +1
    -1
      frappe/desk/form/meta.py
  5. +3
    -3
      frappe/desk/reportview.py
  6. +8
    -8
      frappe/email/page/email_inbox/email_inbox.js
  7. +1
    -1
      frappe/email/page/email_inbox/inbox_list.html
  8. +1
    -1
      frappe/installer.py
  9. +18
    -16
      frappe/model/db_query.py
  10. +0
    -39
      frappe/model/utils/list_settings.py
  11. +48
    -0
      frappe/model/utils/user_settings.py
  12. +1
    -1
      frappe/patches.txt
  13. +0
    -0
      frappe/patches/v8_0/__init__.py
  14. +26
    -0
      frappe/patches/v8_0/rename_listsettings_to_usersettings.py
  15. +19
    -7
      frappe/public/build.json
  16. +26
    -0
      frappe/public/css/desk.css
  17. +1
    -0
      frappe/public/css/form_grid.css
  18. +116
    -53
      frappe/public/css/list.css
  19. +3
    -0
      frappe/public/css/sidebar.css
  20. +1
    -0
      frappe/public/images/default-skin.svg
  21. +1
    -1
      frappe/public/js/frappe/form/sidebar.js
  22. +2
    -2
      frappe/public/js/frappe/form/templates/form_sidebar.html
  23. +0
    -9
      frappe/public/js/frappe/list/blueimp-gallery.html
  24. +0
    -1007
      frappe/public/js/frappe/list/doclistview.js
  25. +5
    -4
      frappe/public/js/frappe/list/header_select_all_like_filter.html
  26. +0
    -44
      frappe/public/js/frappe/list/image_view_item_row.html
  27. +0
    -146
      frappe/public/js/frappe/list/imageview.js
  28. +3
    -4
      frappe/public/js/frappe/list/list_item_main.html
  29. +3
    -2
      frappe/public/js/frappe/list/list_item_main_head.html
  30. +4
    -4
      frappe/public/js/frappe/list/list_item_row.html
  31. +1
    -1
      frappe/public/js/frappe/list/list_item_row_head.html
  32. +1
    -3
      frappe/public/js/frappe/list/list_item_subject.html
  33. +528
    -0
      frappe/public/js/frappe/list/list_renderer.js
  34. +10
    -12
      frappe/public/js/frappe/list/list_sidebar.js
  35. +853
    -0
      frappe/public/js/frappe/list/list_view.js
  36. +0
    -403
      frappe/public/js/frappe/list/listview.js
  37. +62
    -8
      frappe/public/js/frappe/misc/utils.js
  38. +4
    -5
      frappe/public/js/frappe/model/model.js
  39. +41
    -0
      frappe/public/js/frappe/model/user_settings.js
  40. +2
    -1
      frappe/public/js/frappe/router.js
  41. +434
    -0
      frappe/public/js/frappe/ui/base_list.js
  42. +29
    -17
      frappe/public/js/frappe/ui/filters/filters.js
  43. +0
    -13
      frappe/public/js/frappe/ui/listing.html
  44. +23
    -35
      frappe/public/js/frappe/ui/listing.js
  45. +0
    -1
      frappe/public/js/frappe/ui/page.js
  46. +23
    -7
      frappe/public/js/frappe/ui/sort_selector.js
  47. +1
    -1
      frappe/public/js/frappe/ui/toolbar/awesome_bar.js
  48. +1
    -1
      frappe/public/js/frappe/ui/toolbar/notifications.js
  49. +31
    -16
      frappe/public/js/frappe/views/calendar/calendar.js
  50. +4
    -4
      frappe/public/js/frappe/views/container.js
  51. +1
    -1
      frappe/public/js/frappe/views/factory.js
  52. +201
    -0
      frappe/public/js/frappe/views/gantt/gantt_view.js
  53. +180
    -0
      frappe/public/js/frappe/views/image/image_view.js
  54. +2
    -0
      frappe/public/js/frappe/views/image/image_view_item_main_head.html
  55. +44
    -0
      frappe/public/js/frappe/views/image/image_view_item_row.html
  56. +69
    -0
      frappe/public/js/frappe/views/image/photoswipe_dom.html
  57. +1048
    -0
      frappe/public/js/frappe/views/kanban/kanban_board.js
  58. +68
    -1032
      frappe/public/js/frappe/views/kanban/kanban_view.js
  59. +14
    -14
      frappe/public/js/frappe/views/reports/reportview.js
  60. +1
    -1
      frappe/public/js/legacy/form.js
  61. +59
    -21
      frappe/public/js/lib/frappe-gantt/frappe-gantt.js
  62. +0
    -72
      frappe/public/js/lib/gallery/css/blueimp-gallery-indicator.css
  63. +0
    -226
      frappe/public/js/lib/gallery/css/blueimp-gallery.css
  64. +0
    -155
      frappe/public/js/lib/gallery/js/blueimp-gallery-indicator.js
  65. +0
    -1399
      frappe/public/js/lib/gallery/js/blueimp-gallery.js
  66. +483
    -0
      frappe/public/js/lib/photoswipe/default-skin.css
  67. +861
    -0
      frappe/public/js/lib/photoswipe/photoswipe-ui-default.js
  68. +178
    -0
      frappe/public/js/lib/photoswipe/photoswipe.css
  69. +3718
    -0
      frappe/public/js/lib/photoswipe/photoswipe.js
  70. +28
    -0
      frappe/public/less/desk.less
  71. +1
    -0
      frappe/public/less/form_grid.less
  72. +150
    -70
      frappe/public/less/list.less
  73. +4
    -0
      frappe/public/less/sidebar.less

+ 0
- 1
frappe/core/doctype/communication/communication_list.js Voir le fichier

@@ -1,5 +1,4 @@
frappe.listview_settings['Communication'] = {
add_fields: ["sent_or_received", "recipients", "subject", "communication_medium", "communication_type"],
//default_filters: [["Communication", "communication_type", "=", "Communication"]],
filters: [["status", "=", "Open"]]
};

+ 2
- 2
frappe/core/doctype/file/file_list.js Voir le fichier

@@ -42,7 +42,7 @@ frappe.listview_settings['File'] = {
doclist.listview.settings.setup_menu(doclist);
doclist.listview.settings.setup_dragdrop(doclist);

doclist.$page.on("click", ".list-delete", function(event) {
doclist.$page.on("click", ".list-row-checkbox", function(event) {
doclist.listview.settings.add_menu_item_copy(doclist);
})
},
@@ -127,7 +127,7 @@ frappe.listview_settings['File'] = {
add_menu_item_copy: function(doclist){
if (!doclist.copy) {
var copy_menu = doclist.page.add_menu_item(__("Copy"), function() {
if(doclist.$page.find(".list-delete:checked").length){
if(doclist.$page.find(".list-row-checkbox:checked").length){
doclist.selected_files = doclist.get_checked_items();
doclist.old_parent = doclist.current_folder;
doclist.listview.settings.add_menu_item_paste(doclist);


+ 2
- 2
frappe/desk/form/load.py Voir le fichier

@@ -7,7 +7,7 @@ import frappe.utils
import frappe.share
import frappe.defaults
import frappe.desk.form.meta
from frappe.model.utils.list_settings import get_list_settings
from frappe.model.utils.user_settings import get_user_settings
from frappe.permissions import get_doc_permissions
from frappe import _

@@ -70,7 +70,7 @@ def getdoctype(doctype, with_parent=False, cached_timestamp=None):
docs = get_meta_bundle(doctype)

frappe.response['user_permissions'] = get_user_permissions(docs)
frappe.response['list_settings'] = get_list_settings(parent_dt or doctype)
frappe.response['user_settings'] = get_user_settings(parent_dt or doctype)

if cached_timestamp and docs[0].modified==cached_timestamp:
return "use_cache"


+ 1
- 1
frappe/desk/form/meta.py Voir le fichier

@@ -176,7 +176,7 @@ class FormMeta(Meta):

def load_kanban_boards(self):
kanban_boards = frappe.get_all(
'Kanban Board', filters={'reference_doctype': self.name})
'Kanban Board', fields=['name', 'filters', 'reference_doctype'], filters={'reference_doctype': self.name})
self.set("__kanban_boards", kanban_boards, as_value=True)

def load_kanban_column_fields(self):


+ 3
- 3
frappe/desk/reportview.py Voir le fichier

@@ -32,10 +32,10 @@ def get_form_params():
data["fields"] = json.loads(data["fields"])
if isinstance(data.get("docstatus"), basestring):
data["docstatus"] = json.loads(data["docstatus"])
if isinstance(data.get("save_list_settings"), basestring):
data["save_list_settings"] = json.loads(data["save_list_settings"])
if isinstance(data.get("save_user_settings"), basestring):
data["save_user_settings"] = json.loads(data["save_user_settings"])
else:
data["save_list_settings"] = True
data["save_user_settings"] = True


# queries must always be server side


+ 8
- 8
frappe/email/page/email_inbox/email_inbox.js Voir le fichier

@@ -126,7 +126,7 @@ frappe.Inbox = frappe.ui.Listing.extend({
$(me.page.sidebar).find(".list-row").removeClass("list-row-head").css("font-weight","normal");
$(btn.currentTarget).closest(".list-row").addClass("list-row-head").css("font-weight","bold");
me.cur_page = 1;
$(me.page.main).find(".list-select-all,.list-delete").prop("checked",false);
$(me.page.main).find(".list-select-all,.list-row-checkbox").prop("checked",false);
me.toggle_actions();

if(me.account=="Sent"){
@@ -518,11 +518,11 @@ frappe.Inbox = frappe.ui.Listing.extend({
var me = this;

$(".list-select-all").on("click", function () {
$(me.wrapper).find('.list-delete').prop("checked", $(this).prop("checked"));
$(me.wrapper).find('.list-row-checkbox').prop("checked", $(this).prop("checked"));
me.toggle_actions();
});

$(me.wrapper).on("click", ".list-delete", function (event) {
$(me.wrapper).on("click", ".list-row-checkbox", function (event) {
me.toggle_actions();

// multi-select using shift key
@@ -530,9 +530,9 @@ frappe.Inbox = frappe.ui.Listing.extend({
if (event.shiftKey && $this.prop("checked")) {
var $end_row = $this.parents(".list-row");
var $start_row = $end_row.prevAll(".list-row")
.find(".list-delete:checked").last().parents(".list-row");
.find(".list-row-checkbox:checked").last().parents(".list-row");
if ($start_row) {
$start_row.nextUntil($end_row).find(".list-delete").prop("checked", true);
$start_row.nextUntil($end_row).find(".list-row-checkbox").prop("checked", true);
}
}
});
@@ -563,7 +563,7 @@ frappe.Inbox = frappe.ui.Listing.extend({
},
toggle_actions: function () {
var me = this;
if (me.page.main.find(".list-delete:checked").length) {
if (me.page.main.find(".list-row-checkbox:checked").length) {
//show buttons
$(me.page.actions_btn_group).show();
$(me.page.btn_primary).hide()
@@ -625,10 +625,10 @@ frappe.Inbox = frappe.ui.Listing.extend({
val:val
}
})
$('.list-delete:checked').prop( "checked", false );
$('.list-row-checkbox:checked').prop( "checked", false );
},
action_checked_items: function(action) {
return $.map(this.page.main.find('.list-delete:checked'), function(e) {
return $.map(this.page.main.find('.list-row-checkbox:checked'), function(e) {
return eval('$(e).closest(".row-named")'+action);
});
},


+ 1
- 1
frappe/email/page/email_inbox/inbox_list.html Voir le fichier

@@ -4,7 +4,7 @@
<div class="row">
<div class="col-sm-2 list-col ellipsis col-xs-12">
<span class="list-value">
<input class="list-delete noclick" type="checkbox" style="margin: 0 7px 0 0; vertical-align: middle;">
<input class="list-row-checkbox noclick" type="checkbox" style="margin: 0 7px 0 0; vertical-align: middle;">
{% if (data.sender_full_name){var sender = data.sender_full_name} else {var sender = data.sender} %}
<a class="grey" title="{%= sender %}">{%= sender %}</a>
</span>


+ 1
- 1
frappe/installer.py Voir le fichier

@@ -71,7 +71,7 @@ def create_database_and_user(force, verbose):
frappe.db.close()

def create_list_settings_table():
frappe.db.sql_ddl("""create table if not exists __ListSettings (
frappe.db.sql_ddl("""create table if not exists __UserSettings (
`user` VARCHAR(180) NOT NULL,
`doctype` VARCHAR(180) NOT NULL,
`data` TEXT,


+ 18
- 16
frappe/model/db_query.py Voir le fichier

@@ -11,7 +11,7 @@ import frappe.permissions
from frappe.utils import flt, cint, getdate, get_datetime, get_time, make_filter_tuple, get_filter, add_to_date
from frappe import _
from frappe.model import optional_fields
from frappe.model.utils.list_settings import get_list_settings, update_list_settings
from frappe.model.utils.user_settings import get_user_settings, update_user_settings
from datetime import datetime

class DatabaseQuery(object):
@@ -30,8 +30,8 @@ class DatabaseQuery(object):
limit_page_length=None, as_list=False, with_childnames=False, debug=False,
ignore_permissions=False, user=None, with_comment_count=False,
join='left join', distinct=False, start=None, page_length=None, limit=None,
ignore_ifnull=False, save_list_settings=False, save_list_settings_fields=False,
update=None, add_total_row=None):
ignore_ifnull=False, save_user_settings=False, save_user_settings_fields=False,
update=None, add_total_row=None, user_settings=None):
if not ignore_permissions and not frappe.has_permission(self.doctype, "read", user=user):
raise frappe.PermissionError, self.doctype

@@ -72,8 +72,10 @@ class DatabaseQuery(object):
self.flags.ignore_permissions = ignore_permissions
self.user = user or frappe.session.user
self.update = update
self.list_settings_fields = copy.deepcopy(self.fields)
self.user_settings_fields = copy.deepcopy(self.fields)
#self.debug = True
if user_settings:
self.user_settings = json.loads(user_settings)

if query:
result = self.run_custom_query(query)
@@ -83,9 +85,9 @@ class DatabaseQuery(object):
if with_comment_count and not as_list and self.doctype:
self.add_comment_count(result)

if save_list_settings:
self.save_list_settings_fields = save_list_settings_fields
self.update_list_settings()
if save_user_settings:
self.save_user_settings_fields = save_user_settings_fields
self.update_user_settings()

return result

@@ -513,17 +515,17 @@ class DatabaseQuery(object):
if "_comments" in r:
r._comment_count = len(json.loads(r._comments or "[]"))

def update_list_settings(self):
# update list settings if new search
list_settings = json.loads(get_list_settings(self.doctype) or '{}')
list_settings['filters'] = self.filters
list_settings['limit'] = self.limit_page_length
list_settings['order_by'] = self.order_by
def update_user_settings(self):
# update user settings if new search
user_settings = json.loads(get_user_settings(self.doctype))

if self.save_list_settings_fields:
list_settings['fields'] = self.list_settings_fields
if hasattr(self, 'user_settings'):
user_settings.update(self.user_settings)

update_list_settings(self.doctype, list_settings)
if self.save_user_settings_fields:
user_settings['fields'] = self.user_settings_fields

update_user_settings(self.doctype, user_settings)

def get_order_by(doctype, meta):
order_by = ""


+ 0
- 39
frappe/model/utils/list_settings.py Voir le fichier

@@ -1,39 +0,0 @@
import frappe, json

def get_list_settings(doctype, for_update=False):
list_settings = frappe.cache().hget('_list_settings',
'{0}::{1}'.format(doctype, frappe.session.user))

if list_settings is None:
list_settings = frappe.db.sql('''select data from __ListSettings
where user=%s and doctype=%s''', (frappe.session.user, doctype))
list_settings = list_settings and list_settings[0][0] or '{}'

if not for_update:
update_list_settings(doctype, list_settings, True)

return list_settings

def update_list_settings(doctype, list_settings, for_update=False):
'''update list settings in cache'''

if for_update:
current = json.loads(list_settings)
else:
current = json.loads(get_list_settings(doctype, for_update = True))

if isinstance(current, basestring):
# corrupt due to old code, remove this in a future release
current = {}

current.update(list_settings)

frappe.cache().hset('_list_settings', '{0}::{1}'.format(doctype, frappe.session.user),
json.dumps(current))

def sync_list_settings():
'''Sync from cache to database (called asynchronously via the browser)'''
for key, data in frappe.cache().hgetall('_list_settings').iteritems():
doctype, user = key.split('::')
frappe.db.sql('''insert into __ListSettings (user, doctype, data) values (%s, %s, %s)
on duplicate key update data=%s''', (user, doctype, data, data))

+ 48
- 0
frappe/model/utils/user_settings.py Voir le fichier

@@ -0,0 +1,48 @@
# Settings saved per user basis
# such as page_limit, filters, last_view

import frappe, json

def get_user_settings(doctype, for_update=False):
user_settings = frappe.cache().hget('_user_settings',
'{0}::{1}'.format(doctype, frappe.session.user))

if user_settings is None:
user_settings = frappe.db.sql('''select data from __UserSettings
where user=%s and doctype=%s''', (frappe.session.user, doctype))
user_settings = user_settings and user_settings[0][0] or '{}'

if not for_update:
update_user_settings(doctype, user_settings, True)
return user_settings or '{}'

def update_user_settings(doctype, user_settings, for_update=False):
'''update user settings in cache'''

if for_update:
current = json.loads(user_settings)
else:
current = json.loads(get_user_settings(doctype, for_update = True))

if isinstance(current, basestring):
# corrupt due to old code, remove this in a future release
current = {}

current.update(user_settings)

frappe.cache().hset('_user_settings', '{0}::{1}'.format(doctype, frappe.session.user),
json.dumps(current))

def sync_user_settings():
'''Sync from cache to database (called asynchronously via the browser)'''
for key, data in frappe.cache().hgetall('_user_settings').iteritems():
doctype, user = key.split('::')
frappe.db.sql('''insert into __UserSettings (user, doctype, data) values (%s, %s, %s)
on duplicate key update data=%s''', (user, doctype, data, data))

@frappe.whitelist()
def save(doctype, user_settings):
user_settings = json.loads(user_settings or '{}')
update_user_settings(doctype, user_settings)
return user_settings

+ 1
- 1
frappe/patches.txt Voir le fichier

@@ -163,5 +163,5 @@ execute:frappe.rename_doc('Country', 'Macedonia, Republic of', 'Macedonia', igno
execute:frappe.rename_doc('Country', 'Iran, Islamic Republic of', 'Iran', ignore_if_exists=True)
execute:frappe.rename_doc('Country', 'Tanzania, United Republic of', 'Tanzania', ignore_if_exists=True)
execute:frappe.rename_doc('Country', 'Syrian Arab Republic', 'Syria', ignore_if_exists=True)
frappe.patches.v8_0.rename_listsettings_to_usersettings
frappe.patches.v7_2.update_communications


+ 0
- 0
frappe/patches/v8_0/__init__.py Voir le fichier


+ 26
- 0
frappe/patches/v8_0/rename_listsettings_to_usersettings.py Voir le fichier

@@ -0,0 +1,26 @@
import frappe, json

def execute():
for us in frappe.db.sql('''select user, doctype, data from __ListSettings''', as_dict=True):
try:
data = json.loads(us.data)
except:
continue
if 'List' in data:
continue

if 'limit' in data:
data['page_length'] = data['limit']
del data['limit']

new_data = dict(List=data)
new_data = json.dumps(new_data)

frappe.db.sql('''update __ListSettings
set data=%(new_data)s
where user=%(user)s
and doctype=%(doctype)s''',
{'new_data': new_data, 'user': us.user, 'doctype': us.doctype})

frappe.db.sql("RENAME TABLE __ListSettings to __UserSettings")

+ 19
- 7
frappe/public/build.json Voir le fichier

@@ -112,6 +112,7 @@
"public/js/frappe/model/create_new.js",
"public/js/frappe/model/perm.js",
"public/js/frappe/model/workflow.js",
"public/js/frappe/model/user_settings.js",

"public/js/lib/md5.min.js",
"public/js/frappe/misc/user.js",
@@ -211,7 +212,9 @@
],
"js/list.min.js": [
"public/js/frappe/ui/listing.html",
"public/js/frappe/ui/listing.js",
"public/js/frappe/ui/base_list.js",

"public/js/frappe/model/indicator.js",
"public/js/frappe/ui/filters/filters.js",
"public/js/frappe/ui/filters/edit_filter.html",
@@ -222,7 +225,9 @@
"public/js/frappe/ui/like.js",
"public/js/frappe/ui/liked_by.html",
"public/html/print_template.html",
"public/js/frappe/list/doclistview.js",

"public/js/frappe/list/list_view.js",

"public/js/frappe/list/list_sidebar.js",
"public/js/frappe/list/list_sidebar.html",
"public/js/frappe/list/list_sidebar_stat.html",
@@ -232,14 +237,21 @@
"public/js/frappe/list/list_item_row_head.html",
"public/js/frappe/list/list_item_subject.html",
"public/js/frappe/list/list_permission_footer.html",
"public/js/frappe/list/listview.js",
"public/js/frappe/views/calendar.js",
"public/js/frappe/list/blueimp-gallery.html",
"public/js/frappe/list/image_view_item_row.html",
"public/js/frappe/list/image_view_item_main_head.html",
"public/js/frappe/list/list_renderer.js",
"public/js/frappe/views/gantt/gantt_view.js",
"public/js/frappe/views/calendar/calendar.js",
"public/js/frappe/views/image/image_view.js",
"public/js/frappe/views/kanban/kanban_view.js",

"public/js/frappe/list/header_select_all_like_filter.html",
"public/js/frappe/list/item_assigned_to_comment_count.html",
"public/js/frappe/views/treeview.js",

"public/js/frappe/views/image/image_view_item_row.html",
"public/js/frappe/views/image/image_view_item_main_head.html",
"public/js/frappe/views/image/photoswipe_dom.html",
"public/js/frappe/views/kanban/kanban_board.html",
"public/js/frappe/views/kanban/kanban_column.html",
"public/js/frappe/views/kanban/kanban_card.html"


+ 26
- 0
frappe/public/css/desk.css Voir le fichier

@@ -838,3 +838,29 @@ fieldset[disabled] .form-control {
.c3-tooltip td.value {
text-align: right;
}
input[type="checkbox"] {
visibility: hidden;
position: relative;
}
input[type="checkbox"]:before {
position: absolute;
font-family: 'FontAwesome';
content: '\f096';
visibility: visible;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 14px;
display: inline-block;
font-size: 14px;
color: #d1d8dd;
-webkit-transition: 150ms color;
-o-transition: 150ms color;
transition: 150ms color;
}
input[type="checkbox"]:checked:before {
content: '\f14a';
font-size: 13px;
color: #3b99fc;
}

+ 1
- 0
frappe/public/css/form_grid.css Voir le fichier

@@ -76,6 +76,7 @@
.grid-body .editable-row .checkbox {
margin: 0px;
text-align: center;
margin-top: 9px;
}
.grid-body .editable-row textarea {
height: 38px !important;


+ 116
- 53
frappe/public/css/list.css Voir le fichier

@@ -110,6 +110,25 @@
border-top: 1px solid #d1d8dd;
}
.list-value {
display: table;
vertical-align: middle;
}
.list-value .list-row-checkbox,
.list-value .liked-by,
.list-value .list-id,
.list-value .list-select-all {
display: table-cell;
vertical-align: middle;
}
.list-value .list-row-checkbox,
.list-value .list-select-all {
margin: 0;
margin-right: 7px;
}
.list-value .liked-by {
padding-top: 2px;
}
.list-value .list-col-title {
vertical-align: middle;
}
.progress {
@@ -118,7 +137,7 @@
.doclist-row {
font-size: 12px;
}
.doclist-row .likes-count {
.likes-count {
display: inline-block;
width: 15px;
margin-left: -5px;
@@ -164,9 +183,6 @@
.listview-main-section .octicon-heart {
cursor: pointer;
}
.list-row-head .octicon-heart {
margin-right: 13px;
}
.like-action.octicon-heart {
color: #ff5858;
}
@@ -198,55 +214,6 @@
width: 37px;
text-align: left;
}
.image-view {
float: left;
}
.image-view-subject {
padding: inherit;
}
.image-view-col {
padding-bottom: 5px;
padding-top: 5px;
}
.image-view-col a {
text-decoration: none !important;
}
table.field-info {
opacity: 0;
bottom: -20px;
font-size: 8pt;
color: white;
background-color: #36414C;
position: absolute;
padding-bottom: 0px !important;
border: 0px;
}
table.field-info tr td {
border: none !important;
}
.zoom-view {
top: 10px !important;
right: 10px !important;
width: 36px;
height: 36px;
opacity: 0;
font-size: 16px;
color: #36414C;
position: absolute;
padding: 0px !important;
border-radius: 5px;
border: 0px;
}
.image-field {
background-size: contain;
position: relative;
}
.image-field:hover .field-info {
opacity: 0.7;
}
.image-field:hover .zoom-view {
opacity: 0.6;
}
.tag-row {
margin-top: 5px;
padding-left: 55px;
@@ -291,3 +258,99 @@ table.field-info tr td {
font-size: 11px;
max-width: 100px;
}
.image-view-container {
display: flex;
flex-wrap: wrap;
}
.image-view-container .image-view-item {
flex: 0 0 25%;
padding: 15px;
border-bottom: 1px solid #EBEFF2;
border-right: 1px solid #EBEFF2;
}
.image-view-container .image-view-item:nth-child(4n) {
border-right: none;
}
.image-view-container .image-view-item:nth-last-child(-n + 4):nth-child(4n + 1),
.image-view-container .image-view-item:nth-last-child(-n + 4):nth-child(4n + 1) ~ .image-view-item {
border-bottom: none;
}
.image-view-container .image-view-header {
margin-bottom: 10px;
}
.image-view-container .image-view-body:hover .zoom-view {
opacity: 0.7;
}
.image-view-container .image-view-body a {
text-decoration: none;
}
.image-view-container .image-field {
display: flex;
align-content: center;
align-items: center;
justify-content: center;
position: relative;
height: 200px;
padding: 15px;
}
.image-view-container .image-field img {
max-height: 100%;
}
.image-view-container .placeholder-text {
font-size: 72px;
color: #d1d8dd;
}
.image-view-container .zoom-view {
bottom: 10px !important;
right: 10px !important;
width: 36px;
height: 36px;
opacity: 0;
font-size: 16px;
color: #36414C;
position: absolute;
}
@media (max-width: 767px) {
.image-view-container .zoom-view {
opacity: 0.5;
}
}
@media (max-width: 991px) {
.image-view-container .image-view-item {
flex: 0 0 33.33333333%;
}
.image-view-container .image-view-item:nth-child(3n) {
border-right: none;
}
.image-view-container .image-view-item:nth-last-child(-n + 3):nth-child(3n + 1),
.image-view-container .image-view-item:nth-last-child(-n + 3):nth-child(3n + 1) ~ .image-view-item {
border-bottom: none;
}
.image-view-container .image-view-item:nth-child(4n) {
border-right: 1px solid #EBEFF2;
}
.image-view-container .image-view-item:nth-last-child(-n + 4):nth-child(4n + 1),
.image-view-container .image-view-item:nth-last-child(-n + 4):nth-child(4n + 1) ~ .image-view-item {
border-bottom: 1px solid #EBEFF2;
}
}
.pswp--svg .pswp__button,
.pswp--svg .pswp__button--arrow--left:before,
.pswp--svg .pswp__button--arrow--right:before {
background-image: url('/assets/frappe/images/default-skin.svg') !important;
}
.pswp--svg .pswp__button--arrow--left,
.pswp--svg .pswp__button--arrow--right {
background: none !important;
}
.pswp__bg {
background-color: #fff !important;
}
.gantt .details-container .heading {
margin-bottom: 10px;
font-size: 12px;
}
.gantt .details-container .avatar-small {
width: 16px;
height: 16px;
}

+ 3
- 0
frappe/public/css/sidebar.css Voir le fichier

@@ -121,6 +121,9 @@ body[data-route^="Module"] .main-menu .form-sidebar {
.form-sidebar .form-viewers .shared-with-everyone .octicon {
color: #36414C !important;
}
.form-sidebar .liked-by {
margin-left: -4px;
}
.form-sidebar .liked-by .octicon-heart {
font-size: 16px;
cursor: pointer;


+ 1
- 0
frappe/public/images/default-skin.svg Voir le fichier

@@ -0,0 +1 @@
<svg width="264" height="88" viewBox="0 0 264 88" xmlns="http://www.w3.org/2000/svg"><title>default-skin 2</title><g fill="none" fill-rule="evenodd"><g><path d="M67.002 59.5v3.768c-6.307.84-9.184 5.75-10.002 9.732 2.22-2.83 5.564-5.098 10.002-5.098V71.5L73 65.585 67.002 59.5z" id="Shape" fill="#fff"/><g fill="#fff"><path d="M13 29v-5h2v3h3v2h-5zM13 15h5v2h-3v3h-2v-5zM31 15v5h-2v-3h-3v-2h5zM31 29h-5v-2h3v-3h2v5z" id="Shape"/></g><g fill="#fff"><path d="M62 24v5h-2v-3h-3v-2h5zM62 20h-5v-2h3v-3h2v5zM70 20v-5h2v3h3v2h-5zM70 24h5v2h-3v3h-2v-5z"/></g><path d="M20.586 66l-5.656-5.656 1.414-1.414L22 64.586l5.656-5.656 1.414 1.414L23.414 66l5.656 5.656-1.414 1.414L22 67.414l-5.656 5.656-1.414-1.414L20.586 66z" fill="#fff"/><path d="M111.785 65.03L110 63.5l3-3.5h-10v-2h10l-3-3.5 1.785-1.468L117 59l-5.215 6.03z" fill="#fff"/><path d="M152.215 65.03L154 63.5l-3-3.5h10v-2h-10l3-3.5-1.785-1.468L147 59l5.215 6.03z" fill="#fff"/><g><path id="Rectangle-11" fill="#fff" d="M160.957 28.543l-3.25-3.25-1.413 1.414 3.25 3.25z"/><path d="M152.5 27c3.038 0 5.5-2.462 5.5-5.5s-2.462-5.5-5.5-5.5-5.5 2.462-5.5 5.5 2.462 5.5 5.5 5.5z" id="Oval-1" stroke="#fff" stroke-width="1.5"/><path fill="#fff" d="M150 21h5v1h-5z"/></g><g><path d="M116.957 28.543l-1.414 1.414-3.25-3.25 1.414-1.414 3.25 3.25z" fill="#fff"/><path d="M108.5 27c3.038 0 5.5-2.462 5.5-5.5s-2.462-5.5-5.5-5.5-5.5 2.462-5.5 5.5 2.462 5.5 5.5 5.5z" stroke="#fff" stroke-width="1.5"/><path fill="#fff" d="M106 21h5v1h-5z"/><path fill="#fff" d="M109.043 19.008l-.085 5-1-.017.085-5z"/></g></g></g></svg>

+ 1
- 1
frappe/public/js/frappe/form/sidebar.js Voir le fichier

@@ -128,7 +128,7 @@ frappe.ui.form.Sidebar = Class.extend({
make_like: function() {
this.like_wrapper = this.sidebar.find(".liked-by");
this.like_icon = this.sidebar.find(".liked-by .octicon-heart");
this.like_count = this.sidebar.find(".liked-by .like-count");
this.like_count = this.sidebar.find(".liked-by .likes-count");
frappe.ui.setup_like_popover(this.sidebar.find(".liked-by-parent"), ".liked-by");
},



+ 2
- 2
frappe/public/js/frappe/form/templates/form_sidebar.html Voir le fichier

@@ -67,8 +67,8 @@
<ul class="list-unstyled sidebar-menu text-muted">
<li class="liked-by-parent">
<span class="liked-by">
<i class="octicon octicon-heart like-action text-extra-muted"></i>
<span class="like-count"></span>
<i class="octicon octicon-heart like-action text-extra-muted fa-fw"></i>
<span class="likes-count"></span>
</span>
</li>
<li class="modified-by"></li>


+ 0
- 9
frappe/public/js/frappe/list/blueimp-gallery.html Voir le fichier

@@ -1,9 +0,0 @@
<div id="blueimp-gallery" class="blueimp-gallery blueimp-gallery-controls">
<div class="slides"></div>
<h3 class="title"></h3>
<a class="prev">‹</a>
<a class="next">›</a>
<a class="close">×</a>
<a class="play-pause"></a>
<ol class="indicator"></ol>
</div>

+ 0
- 1007
frappe/public/js/frappe/list/doclistview.js
Fichier diff supprimé car celui-ci est trop grand
Voir le fichier


+ 5
- 4
frappe/public/js/frappe/list/header_select_all_like_filter.html Voir le fichier

@@ -1,7 +1,8 @@
{% if (_checkbox) { %}
<input class="list-select-all" type="checkbox" style="margin-right: 7px; margin-top: 2px;"
<input class="list-select-all" type="checkbox"
title="{%= __("Select All") %}">
{% } %}

<i class="fa-fw octicon octicon-heart text-extra-muted not-liked like-action list-liked-by-me"
title="{%= __("Likes") %}"></i>
<span style="display: inline-block; width: 34px">
<i class="fa-fw octicon octicon-heart text-extra-muted not-liked like-action list-liked-by-me"
title="{%= __("Likes") %}"></i>
</span>

+ 0
- 44
frappe/public/js/frappe/list/image_view_item_row.html Voir le fichier

@@ -1,44 +0,0 @@
<div class="col-xs-6 col-sm-4 col-md-3 doclist-row has-checkbox image-view ellipsis">
<div class="row">
<div class="col-xs-12 image-view-col">
{%= subject %}
</div>
</div>
<!-- Image -->
<div class="row">
<div class="col-xs-12 image-view-col" align="center">
<a data-name="{{ data.name }}" title="{{ data.name }}" href="#Form/{{ data.doctype }}/{{ data.name }}">
<div class="pos-item-image image-field" data-name="{{ data.name }}" style="{% if (item_image) { %} background-image: {%= item_image %}; {% }
else { %} background-color: {{ color }}; {% } %} border: 0px;">
{% if (!item_image) { %}{%= frappe.get_abbr(data.name) %}{% } %}
<button class="btn btn-info zoom-view">
<i class="fa fa-zoom-in"></i>
</button>
<table class="table table-condensed field-info">
{% for (var i=0, l=columns.length; i < l; i++ ) {
var col = columns[i], value=data[col.fieldname]; %}
{% if(in_list(allowed_type, col.fieldtype)) { %}
<tr>
<td align="right" width="40%">{%= col.title %}</td>
<td align="left" width="60%">{%= value %}</td>
</tr>
{% } %}
{% } %}
</table>
</div>
</a>
</div>
</div>
<div class="row">
<!-- comment -->
<div class="col-xs-12 image-view-col">
<div class="row">
<div class="col-xs-4">{%= list.get_indicator(data) %}</div>
<div class="col-xs-8 text-right">
<!-- comments count and assigned to section -->
{%= frappe.render_template("item_assigned_to_comment_count", { data: data }) %}
</div>
</div>
</div>
</div>
</div>

+ 0
- 146
frappe/public/js/frappe/list/imageview.js Voir le fichier

@@ -1,146 +0,0 @@
frappe.views.ImageView = Class.extend({
init: function(opts){
this.docnames = []
this.doc_images = {}
this.doctype = opts.doctype;
this.docname = opts.docname;
this.container = opts.container;

this.get_images(this.doctype, this.docname);
},

get_images: function(doctype, docname){
/* get the list of all the Images associated with doc */
var me = this;
if(Object.keys(this.doc_images).length == 0){
frappe.call({
method: "frappe.client.get_list",
args: {
doctype: "File",
order_by: "attached_to_name",
fields: [
"'image/*' as type", "ifnull(thumbnail_url, file_url) as thumbnail",
"concat(attached_to_name, ' - ', file_name) as title", "file_url as href",
"attached_to_name as name"
],
filters: [
["File", "attached_to_doctype", "=", this.doctype],
["File", "attached_to_name", "in", this.get_docname_list()],
["File", "is_folder", "!=", 1]
]
},
freeze: true,
freeze_message: "Fetching Images ..",
callback: function(r){
if(!r.message){
msgprint("No Images found")
} else{
// filter image files from other
images = r.message.filter(function(image){
return frappe.utils.is_image_file(image.title? image.title: image.href);
});

if(images){
me.prepare_images(images);
me.render();
}
}
}
});
} else {
this.render()
}
},

get_docname_list: function(){
var names = []
$.each(cur_list.data, function(idx, doc){
names.push(doc.name);
});
return names
},

prepare_images: function(images){
var me = this;
this.doc_images = {}

$.each(images, function(idx, image){
name = image.name;
delete image.name;

_images = me.doc_images[name] || [];
_images.push(image)

me.doc_images[name] = _images;
delete _images;
});

this.docnames = $.map(cur_list.data, function(doc, idx){
if(inList(Object.keys(me.doc_images), doc.name)){
return doc.name;
}
});
},

render: function(){
if(this.docname in this.doc_images){
this.gallery = blueimp.Gallery(this.doc_images[this.docname], this.get_options());
this.setup_navigation();
} else {
msgprint("No Images found for <b>"+ this.doctype +"</b> : "+ this.docname);
}
},

setup_navigation: function(){
// extend gallery library to enable document navigation using UP / Down arrow key
var me = this;
var args = {}
$.extend(me.gallery, {
nextSlides:function(){
args.offset = 1;
me.navigate(args)
},

prevSlides:function(){
args.offset = -1;
me.navigate(args)
}
});
},

navigate: function(args){
var index = 0;
var me = this;
var last_idx = this.docnames.length - 1;

$.each(this.docnames, function(idx, name){
if(name == me.docname){
if(idx == last_idx && args.offset == 1){
index = 0
} else if(idx == 0 && args.offset == -1) {
index = last_idx
} else {
index = idx + args.offset
}
me.docname = me.docnames[index];
return false;
}
});
this.gallery.close();
window.setTimeout(function(){
me.get_images(me.doctype, me.docname)
}, 300);
},

get_options: function(){
/* options for the gallery plugin */
return {
indicatorContainer: 'ol',
thumbnailIndicators: true,
thumbnailProperty: 'thumbnail',
activeIndicatorClass: 'active',
container: this.container.find(".blueimp-gallery")
}
}
});

+ 3
- 4
frappe/public/js/frappe/list/list_item_main.html Voir le fichier

@@ -20,7 +20,7 @@
{% if (col.type==="Subject") { %}
{%= subject %}
{% } else if (col.type==="Indicator") { %}
{%= list.get_indicator(data) %}
{%= indicator %}
{% } else if (col.render) { %}
{%= col.render(data) %}
{% } else if (col.fieldtype==="Image") { %}
@@ -36,9 +36,8 @@
<a class="filterable h6 text-muted grey"
data-filter="{%= col.fieldname %},=,{%= value %}">{%= value %}</a>
{% } else { %}
{% if(list.settings.formatters
&& list.settings.formatters[col.fieldname]) { %}
{{ list.settings.formatters[col.fieldname](value, col.df, data) }}
{% if(formatters && formatters[col.fieldname]) { %}
{{ formatters[col.fieldname](value, col.df, data) }}
{% } else { %}
{{ frappe.format(value, col.df, null, data) }}
{% } %}


+ 3
- 2
frappe/public/js/frappe/list/list_item_main_head.html Voir le fichier

@@ -16,12 +16,13 @@
{% } %}
{% if(col.df && ["Int", "Float", "Currency", "Percent"].indexOf(col.df.fieldtype)!==-1) { %}text-right{% } %}">

<span class="list-value">
{% if (col.type==="Subject") { %}
{%= frappe.render_template("header_select_all_like_filter", { _checkbox: _checkbox }) %}
{% } %}
<span class="list-col-title">{{ __(col.title) || __(col.label) || "" }}</span>
</span>

<span class="list-value" {% if i==0 %}style="margin-left: -8px;"{% endif %}>
{{ __(col.title) || __(col.label) || "" }}</span>
</div>
{% } %}
{% } %}


+ 4
- 4
frappe/public/js/frappe/list/list_item_row.html Voir le fichier

@@ -1,6 +1,6 @@
<div class="row doclist-row {% if (data._checkbox) { %} has-checkbox {% } %}">
<div class="col-xs-10
{% if (list.meta.title_field && !list.settings.hide_name_column) { %}
{% if (meta.title_field && !settings.hide_name_column) { %}
col-sm-8
{% } else { %}
col-sm-10
@@ -10,8 +10,8 @@
</div>

<!-- id -->
{% if (list.meta.title_field && !list.settings.hide_name_column) {
var is_different = data.name !== data[list.meta.title_field];
{% if (meta.title_field && !settings.hide_name_column) {
var is_different = data.name !== data[meta.title_field];
%}
<div class="list-col col-sm-2 hidden-xs text-right ellipsis list-row-id">
{% if (is_different) { %}
@@ -24,7 +24,7 @@
<!-- comment -->
<div class="list-col col-sm-2 col-xs-2
text-right list-row-right" style="padding-left:0px">
<div class="visible-xs list-row-indicator">{%= list.get_indicator_dot(data) %}</div>
<div class="visible-xs list-row-indicator">{%= indicator_dot %}</div>
<!-- comments count and assigned to section -->
{%= frappe.render_template("item_assigned_to_comment_count", { data: data }) %}
</div>


+ 1
- 1
frappe/public/js/frappe/list/list_item_row_head.html Voir le fichier

@@ -1,4 +1,4 @@
<div class="list-row list-row-head">
<div class="list-row list-row-head" data-list-renderer="{{list.name}}">
<div class="row doclist-row">
<div class="col-xs-10
{% if (list.meta.title_field && !list.settings.hide_name_column) { %}


+ 1
- 3
frappe/public/js/frappe/list/list_item_subject.html Voir le fichier

@@ -1,6 +1,5 @@
{% if (_checkbox) { %}
<input class="list-delete" type="checkbox"
style="margin: 0 7px 0 0; vertical-align: middle;">
<input class="list-row-checkbox" type="checkbox" data-name="{{name}}">
{% } %}
<span class="liked-by" data-liked-by=\'{{ JSON.stringify(_liked_by) }}\'>
<i class="octicon octicon-heart
@@ -14,7 +13,6 @@
</span>
<a class="grey list-id {{ css_seen }}"
data-name="{{ _name }}"
style="margin-right: 7px; margin-left: -8px;"
href="#Form/{{ _doctype_encoded }}/{{ _name_encoded }}"
title="{{ _full_title }}">{{ strip_html(_title) }}</a>
{% if (_workflow && !_without_workflow) { %}


+ 528
- 0
frappe/public/js/frappe/list/list_renderer.js Voir le fichier

@@ -0,0 +1,528 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

frappe.provide('frappe.views');

// Renders customized list
// usually based on `in_list_view` property

frappe.views.ListRenderer = Class.extend({
name: 'List',
init: function (opts) {
$.extend(this, opts);
this.meta = frappe.get_meta(this.doctype);

this.init_settings();
this.set_defaults();
this.set_fields();
this.set_columns();
this.setup_cache();
},
set_defaults: function () {
var me = this;
this.page_title = __(this.doctype);

this.set_wrapper();
this.prepare_render_view();

// flag to enable/disable realtime updates in list_view
this.no_realtime = false;

// set false to render view even if no results
// e.g Calendar
this.show_no_result = true;

// hide sort selector
this.hide_sort_selector = false;

// default settings
this.order_by = this.order_by || 'modified desc';
this.filters = this.filters || [];
this.page_length = this.page_length || 20;
},
setup_cache: function () {
frappe.provide('frappe.views.list_renderers.' + this.doctype);
frappe.views.list_renderers[this.doctype][this.list_view.current_view] = this;
},
init_settings: function () {
this.settings = frappe.listview_settings[this.doctype] || {};
this.init_user_settings();

this.order_by = this.user_settings.order_by || this.settings.order_by;
this.filters = this.user_settings.filters || this.settings.filters;
this.page_length = this.user_settings.page_length || this.settings.page_length;

// default filter for submittable doctype
if(frappe.model.is_submittable(this.doctype) && (!this.filters || !this.filters.length)) {
this.filters = [[this.doctype, "docstatus", "!=", 2]];
}
},
init_user_settings: function () {
frappe.provide('frappe.model.user_settings.' + this.doctype + '.' + this.name);
this.user_settings = frappe.get_user_settings(this.doctype)[this.name];
},
after_refresh: function() {
// called after refresh in list_view
},
before_refresh: function() {
// called before refresh in list_view
},
should_refresh: function() {
return this.list_view.current_view !== this.list_view.last_view;
},
set_wrapper: function () {
this.wrapper = this.list_view.wrapper.find('.result-list');
},
set_fields: function () {
var me = this;
var tabDoctype = '`tab' + this.doctype + '`.';
this.fields = [];
this.stats = ['_user_tags'];

var add_field = function (fieldname) {
if (!fieldname.includes('`tab')) {
fieldname = tabDoctype + '`' + fieldname + '`';
}
if (!me.fields.includes(fieldname))
me.fields.push(fieldname);
}

var defaults = [
'name',
'owner',
'docstatus',
'_user_tags',
'_comments',
'modified',
'modified_by',
'_assign',
'_liked_by',
'_seen'
];
defaults.map(add_field);

// add title field
if (this.meta.title_field) {
this.title_field = this.meta.title_field;
add_field(this.meta.title_field);
}

// enabled / disabled
if (frappe.meta.has_field(this.doctype, 'enabled')) { add_field('enabled'); };
if (frappe.meta.has_field(this.doctype, 'disabled')) { add_field('disabled'); };

// add workflow field (as priority)
this.workflow_state_fieldname = frappe.workflow.get_state_fieldname(this.doctype);
if (this.workflow_state_fieldname) {
if (!frappe.workflow.workflows[this.doctype]['override_status']) {
add_field(this.workflow_state_fieldname);
}
this.stats.push(this.workflow_state_fieldname);
}

this.meta.fields.forEach(function (df, i) {
if (df.in_list_view && frappe.perm.has_perm(me.doctype, df.permlevel, 'read')) {
if (df.fieldtype == 'Image' && df.options) {
add_field(df.options);
} else {
add_field(df.fieldname);
}
// currency field for symbol (multi-currency)
if (df.fieldtype == 'Currency' && df.options) {
if (df.options.includes(':')) {
add_field(df.options.split(':')[1]);
} else {
add_field(df.options);
};
}
}
});

// additional fields
if (this.settings.add_fields) {
this.settings.add_fields.forEach(add_field);
}
// kanban column fields
if (me.meta.__kanban_column_fields) {
me.fields = me.fields.concat(me.meta.__kanban_column_fields);
}
},
set_columns: function () {
var me = this;
this.columns = [];
var name_column = {
colspan: this.settings.colwidths && this.settings.colwidths.subject || 6,
type: 'Subject',
title: 'Name'
};
if (this.meta.title_field) {
name_column.title = frappe.meta.get_docfield(this.doctype, this.meta.title_field).label;
}
this.columns.push(name_column);

if (frappe.has_indicator(this.doctype)) {
// indicator
this.columns.push({
colspan: this.settings.colwidths && this.settings.colwidths.indicator || 3,
type: 'Indicator',
title: 'Status'
});
}

// total_colspans
this.total_colspans = this.columns.reduce(function (total, curr) {
return total + curr.colspan;
}, 0);

// overridden
var overridden = (this.settings.add_columns || []).map(function (d) {
return d.content;
});

// custom fields in list_view
var docfields_in_list_view =
frappe.get_children('DocType', this.doctype, 'fields', { 'in_list_view': 1 })
.sort(function (a, b) {
return a.idx - b.idx
});

docfields_in_list_view.forEach(function (d) {
if (overridden.includes(d.fieldname) || d.fieldname === me.title_field) {
return;
}
if (me.total_colspans < 12) {
me.add_column(d);
}
});

// additional columns
if (this.settings.add_columns) {
this.settings.add_columns.forEach(function (d) {
if (me.total_colspans < 12) {
if (typeof d === 'string') {
me.add_column(frappe.meta.get_docfield(me.doctype, d));
} else {
me.columns.push(d);
me.total_colspans += parseInt(d.colspan);
}
}
});
}


// distribute remaining columns
var empty_cols = flt(12 - this.total_colspans);
while (empty_cols > 0) {
this.columns = this.columns.map(function (col) {
if (empty_cols > 0) {
col.colspan = cint(col.colspan) + 1;
empty_cols = empty_cols - 1;
}
return col;
});
}
},
add_column: function (df) {
// field width
var colspan = 3;
if (in_list(['Int', 'Percent'], df.fieldtype)) {
colspan = 2;
} else if (in_list(['Check', 'Image'], df.fieldtype)) {
colspan = 1;
} else if (in_list(['name', 'subject', 'title'], df.fieldname)) {
// subjects are longer
colspan = 4;
} else if (df.fieldtype == 'Text Editor' || df.fieldtype == 'Text') {
colspan = 4;
}

if (df.columns && df.columns > 0) {
colspan = df.columns;
} else if (this.settings.column_colspan && this.settings.column_colspan[df.fieldname]) {
colspan = this.settings.column_colspan[df.fieldname];
} else {
colspan = 2;
}
this.total_colspans += parseInt(colspan);
var col = {
colspan: colspan,
content: df.fieldname,
type: df.fieldtype,
df: df,
fieldtype: df.fieldtype,
fieldname: df.fieldname,
title: __(df.label)
};
if (this.settings.column_render && this.settings.column_render[df.fieldname]) {
col.render = this.settings.column_render[df.fieldname];
}
this.columns.push(col);
},

setup_filterable: function () {
var me = this;
this.wrapper.on('click', '.filterable', function (e) {
var filters = $(this).attr('data-filter').split('|');
var added = false;

filters.forEach(function (f) {
f = f.split(',');
if (f[2] === 'Today') {
f[2] = frappe.datetime.get_today();
} else if (f[2] == 'User') {
f[2] = frappe.session.user;
}
var new_filter = me.list_view.filter_list
.add_filter(me.doctype, f[0], f[1], f.slice(2).join(','));

if (new_filter) {
// set it to true if atleast 1 filter is added
added = true;
}
});
if (added) {
me.list_view.refresh(true);
}
});
this.wrapper.on('click', '.list-row-left', function (e) {
// don't open in case of checkbox, like, filterable
if ($(e.target).hasClass('filterable')
|| $(e.target).hasClass('octicon-heart')
|| $(e.target).is(':checkbox')) {
return;
}

var link = $(this).parent().find('a.list-id').get(0);
window.location.href = link.href;
return false;
});
},

render_view: function (values) {
var me = this;
values.map(function (value) {
var row = $('<div class="list-row">')
.data("data", me.meta)
.appendTo(me.wrapper).get(0);
me.render_item(row, value);
});

this.setup_filterable();
},

// renders data(doc) in element
render_item: function (element, data) {

$(element).append(this.get_item_html(data));

if (this.settings.post_render_item) {
this.settings.post_render_item(this, element, data);
}
},

// returns html for a data item,
// usually based on a template
get_item_html: function (data) {
var main = frappe.render_template('list_item_main', {
data: data,
columns: this.columns,
formatters: this.settings.formatters,
subject: this.get_subject_html(data, true),
indicator: this.get_indicator_html(data),
right_column: this.settings.right_column
});

return frappe.render_template('list_item_row', {
data: data,
main: main,
settings: this.settings,
meta: this.meta,
indicator_dot: this.get_indicator_dot(data),
right_column: this.settings.right_column
})
},

get_header_html: function () {
var main = frappe.render_template('list_item_main_head', {
columns: this.columns,
right_column: this.settings.right_column,
_checkbox: ((frappe.model.can_delete(this.doctype) || this.settings.selectable)
&& !this.no_delete)
});

return frappe.render_template('list_item_row_head', { main: main, list: this });
},

render_tags: function (element, data) {
var me = this;
var tag_row = $(`<div class='tag-row'>
<div class='list-tag hidden-xs'></div>
<div class='clearfix'></div>
</div>`).appendTo(element);

if (!me.list_view.tags_shown) {
tag_row.addClass('hide');
}

// add tags
var tag_editor = new frappe.ui.TagEditor({
parent: tag_row.find('.list-tag'),
frm: {
doctype: this.doctype,
docname: data.name
},
list_sidebar: me.list_view.list_sidebar,
user_tags: data._user_tags,
on_change: function (user_tags) {
data._user_tags = user_tags;
}
});
tag_editor.wrapper.on('click', '.tagit-label', function () {
me.list_view.set_filter('_user_tags', $(this).text());
});
},

get_subject_html: function (data, without_workflow) {
data._without_workflow = without_workflow;
return frappe.render_template('list_item_subject', data);
},

get_indicator_html: function (doc) {
var indicator = frappe.get_indicator(doc, this.doctype);
if (indicator) {
return `<span class='indicator ${indicator[1]} filterable'
data-filter='${indicator[2]}'>
${__(indicator[0])}
<span>`;
}
return '';
},

get_indicator_dot: function (doc) {
var indicator = frappe.get_indicator(doc, this.doctype);
if (!indicator) {
return '';
}
return `<span class='indicator ${indicator[1]}' title='${__(indicator[0])}'></span>`;
},

prepare_data: function (data) {
if (data.modified)
this.prepare_when(data, data.modified);

// nulls as strings
for (key in data) {
if (data[key] == null) {
data[key] = '';
}
}

data.doctype = this.doctype;
data._liked_by = JSON.parse(data._liked_by || '[]');
data._checkbox = (frappe.model.can_delete(this.doctype) || this.settings.selectable) && !this.no_delete

data._doctype_encoded = encodeURIComponent(data.doctype);
data._name = data.name.replace(/'/g, '\'');
data._name_encoded = encodeURIComponent(data.name);
data._submittable = frappe.model.is_submittable(this.doctype);

var title_field = frappe.get_meta(this.doctype).title_field || 'name';
data._title = strip_html(data[title_field]) || data.name;
data._full_title = data._title;

if (data._title.length > 35) {
data._title = data._title.slice(0, 35) + '...';
}

data._workflow = null;
if (this.workflow_state_fieldname) {
data._workflow = {
fieldname: this.workflow_state_fieldname,
value: data[this.workflow_state_fieldname],
style: frappe.utils.guess_style(data[this.workflow_state_fieldname])
}
}

data._user = frappe.session.user;

data._tags = data._user_tags.split(',').filter(function (v) {
// filter falsy values
return v;
});

data.css_seen = '';
if (data._seen) {
var seen = JSON.parse(data._seen);
if (seen && in_list(seen, data._user)) {
data.css_seen = 'seen'
}
}

data._assign_list = JSON.parse(data._assign || '[]');

// prepare data in settings
if (this.settings.prepare_data)
this.settings.prepare_data(data);

return data;
},

prepare_when: function (data, date_str) {
if (!date_str) date_str = data.modified;
// when
data.when = (dateutil.str_to_user(date_str)).split(' ')[0];
var diff = dateutil.get_diff(dateutil.get_today(), date_str.split(' ')[0]);
if (diff === 0) {
data.when = comment_when(date_str);
}
if (diff === 1) {
data.when = __('Yesterday')
}
if (diff === 2) {
data.when = __('2 days ago')
}
},

// for views which require 3rd party libs
required_libs: null,

prepare_render_view: function () {
var me = this;
this._render_view = this.render_view;

var lib_exists = (typeof this.required_libs === 'string' && this.required_libs)
|| ($.isArray(this.required_libs) && this.required_libs.length);

this.render_view = function (values) {
// prepare data before rendering view
values = values.map(me.prepare_data.bind(this));

if (lib_exists) {
me.load_lib(function () {
me._render_view(values);
});
} else {
me._render_view(values);
}
}.bind(this);
},

load_lib: function (callback) {
frappe.require(this.required_libs, callback);
},

render_bar_graph: function (parent, data, field, label) {
var args = {
percent: data[field],
label: __(label)
}
$(parent).append(`<span class='progress' style='width: 100 %; float: left; margin: 5px 0px;'> \
<span class='progress- bar' title='${args.percent}% ${args.label}' \
style='width: ${args.percent}%;'></span>\
</span>`);
},
render_icon: function (parent, icon_class, label) {
var icon_html = `<i class='${icon_class}' title='${__(label) || ''}'></i>`;
$(parent).append(icon_html);
}
});

+ 10
- 12
frappe/public/js/frappe/list/list_sidebar.js Voir le fichier

@@ -17,7 +17,7 @@ frappe.views.ListSidebar = Class.extend({
this.cat_tags = [];
},
make: function() {
var sidebar_content = frappe.render_template("list_sidebar", {doctype: this.doclistview.doctype});
var sidebar_content = frappe.render_template("list_sidebar", {doctype: this.list_view.doctype});

this.sidebar = $('<div class="list-sidebar overlay-sidebar hidden-xs hidden-sm"></div>')
.html(sidebar_content)
@@ -63,7 +63,7 @@ frappe.views.ListSidebar = Class.extend({
.attr('disabled', null).removeClass('disabled')

// show image link if image_view
if(this.doclistview.meta.image_field) {
if(this.list_view.meta.image_field) {
this.sidebar.find('.list-link[data-view="Image"]').removeClass('hide');
show_list_link = true;
}
@@ -103,8 +103,8 @@ frappe.views.ListSidebar = Class.extend({
}

// from reference doctype
if(this.doclistview.listview.settings.reports) {
add_reports(this.doclistview.listview.settings.reports)
if(this.list_view.list_renderer.settings.reports) {
add_reports(this.list_view.list_renderer.settings.reports)
}

// from specially tagged reports
@@ -119,12 +119,12 @@ frappe.views.ListSidebar = Class.extend({
var boards = frappe.get_meta(this.doctype).__kanban_boards;
if (!boards) return;
boards.forEach(function(board) {
var route = ["List", board.parent, "Kanban", board.name].join('/');
var route = ["List", board.reference_doctype, "Kanban", board.name].join('/');
if(!divider) {
$('<li role="separator" class="divider"></li>').appendTo($dropdown);
divider = true;
}
$('<li><a href="#'+ route + '">'+board.name+'</a></li>').appendTo($dropdown);
$(`<li><a href="#${route}">${__(board.name)}</a></li>`).appendTo($dropdown);
});

$dropdown.find('.new-kanban-board').click(function() {
@@ -179,10 +179,8 @@ frappe.views.ListSidebar = Class.extend({

me.add_custom_column_field(custom_column)
.then(function(custom_column) {
console.log(custom_column)
var f = custom_column ?
'kanban_column' : values.field_name;
console.log(f)
return me.make_kanban_board(values.board_name, f)
})
.then(function() {
@@ -239,7 +237,7 @@ frappe.views.ListSidebar = Class.extend({
setup_assigned_to_me: function() {
var me = this;
this.page.sidebar.find(".assigned-to-me a").on("click", function() {
me.doclistview.assigned_to_me();
me.list_view.assigned_to_me();
});
},
get_cat_tags:function(){
@@ -280,7 +278,7 @@ frappe.views.ListSidebar = Class.extend({
//render normal stats
me.render_stat("_user_tags", (r.message.stats|| {})["_user_tags"]);
}
me.doclistview.set_sidebar_height();
me.list_view.set_sidebar_height();
}
});
},
@@ -333,8 +331,8 @@ frappe.views.ListSidebar = Class.extend({
var fieldname = $(this).attr('data-field');
var label = $(this).attr('data-label');
if (label == "No Tags") {
me.doclistview.filter_list.add_filter(me.doclistview.doctype, fieldname, 'not like', '%,%')
me.doclistview.run();
me.list_view.filter_list.add_filter(me.list_view.doctype, fieldname, 'not like', '%,%')
me.list_view.run();
} else {
me.set_filter(fieldname, label);
}


+ 853
- 0
frappe/public/js/frappe/list/list_view.js Voir le fichier

@@ -0,0 +1,853 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

frappe.provide('frappe.views.list_view');
frappe.provide('frappe.views.list_renderers');

cur_list = null;
frappe.views.ListFactory = frappe.views.Factory.extend({
make: function (route) {
var me = this;
var doctype = route[1];

frappe.model.with_doctype(doctype, function () {
if (locals['DocType'][doctype].issingle) {
frappe.set_re_route('Form', doctype);
} else {
if (!frappe.views.list_view[doctype]) {
frappe.views.list_view[doctype] = new frappe.views.ListView({
doctype: doctype,
parent: me.make_page(true, 'List/' + doctype)
});
} else {
frappe.container.change_to(frappe.views.list_view[doctype].page_name);
}
me.set_cur_list();
}
});
},
show: function () {
this.set_module_breadcrumb();
this._super();
this.set_cur_list();
cur_list && cur_list.refresh();
},
set_module_breadcrumb: function () {
if (frappe.route_history.length > 1) {
var prev_route = frappe.route_history[frappe.route_history.length - 2];
if (prev_route[0] === 'modules') {
var doctype = frappe.get_route()[1],
module = prev_route[1];
if (frappe.module_links[module] && frappe.module_links[module].includes(doctype)) {
// save the last page from the breadcrumb was accessed
frappe.breadcrumbs.set_doctype_module(doctype, module);
}
}
}
},
set_cur_list: function () {
var route = frappe.get_route();
cur_list = frappe.container.page && frappe.container.page.list_view;
if (cur_list && cur_list.doctype !== route[1]) {
// changing...
cur_list = null;
}
}
});

$(document).on('save', function (event, doc) {
frappe.views.set_list_as_dirty(doc.doctype);
});

frappe.views.set_list_as_dirty = function (doctype) {
if (frappe.views.trees[doctype]) {
frappe.views.trees[doctype].tree.refresh();
}

var route = frappe.get_route();
var current_view = route[2] || 'List';

var list_renderer = frappe.views.list_renderers[doctype];
if (list_renderer
&& list_renderer[current_view]
&& list_renderer[current_view].no_realtime) {
return;
}

var list_page = 'List/' + doctype;
if (frappe.pages[list_page]) {
if (frappe.pages[list_page].list_view) {
if (frappe.pages[list_page].list_view.dirty) {
// already refreshing...
return;
}
frappe.pages[list_page].list_view.dirty = true;
}
}
if (route[0] === 'List' && route[1] === doctype) {
setTimeout(function () {
frappe.pages[list_page].list_view.refresh();
}, 100);
}
}

frappe.views.ListView = frappe.ui.BaseList.extend({
init: function (opts) {
$.extend(this, opts);

if (!frappe.boot.user.all_read.includes(this.doctype)) {
frappe.show_not_permitted(frappe.get_route_str());
return;
}

this.page_name = 'List/' + this.doctype;
this.dirty = true;
this.tags_shown = false;

this.page_title = __(this.doctype);
this.page_title =
(this.page_title.toLowerCase().substr(-4) == 'list') && __(this.page_title)
|| __(this.page_title) + ' ' + __('List');

this.make_page();
this.setup();

// refresh on init
this.refresh();
},

make_page: function () {
this.parent.list_view = this;
this.page = this.parent.page;

this.$page = $(this.parent).css({ 'min-height': '400px' });

$(`<div class='frappe-list-area'></div>`)
.appendTo(this.page.main);

this.page.main.addClass('listview-main-section');
var module = locals.DocType[this.doctype].module;

frappe.breadcrumbs.add(module, this.doctype);
},

setup: function () {
this.can_delete = frappe.model.can_delete(this.doctype);
this.meta = frappe.get_meta(this.doctype);
this.wrapper = this.$page.find('.frappe-list-area').empty();
this.allow_delete = true;

this.load_last_view();
this.setup_view_variables();

this.setup_list_renderer();
this.init_base_list(false);
this.list_renderer.set_wrapper();
this.list_renderer_onload();

this.show_match_help();
this.init_menu();
this.init_sort_selector();
this.init_filters();
this.set_title();
this.init_headers();
},

refresh_surroundings: function() {
this.init_sort_selector();
this.init_filters();
this.set_title();
this.init_headers();
},

setup_list_renderer: function () {
frappe.provide('frappe.views.list_renderers.' + this.doctype);

var list_renderer = frappe.views.list_renderers[this.doctype][this.current_view];
if (list_renderer) {
this.list_renderer = list_renderer;
this.list_renderer.init_settings();
return;
}

var opts = {
doctype: this.doctype,
list_view: this
}

if (this.current_view === 'List') {
this.list_renderer = new frappe.views.ListRenderer(opts);
} else if (this.current_view === 'Gantt') {
this.list_renderer = new frappe.views.GanttView(opts);
} else if (this.current_view === 'Calendar') {
this.list_renderer = new frappe.views.CalendarView(opts);
} else if (this.current_view === 'Image') {
this.list_renderer = new frappe.views.ImageView(opts);
} else if (this.current_view === 'Kanban') {
this.list_renderer = new frappe.views.KanbanView(opts);
}
},

render_view: function (values) {
this.list_renderer.render_view(values);
},

set_title: function () {
if (this.list_renderer.page_title) {
this.page.set_title(this.list_renderer.page_title);
} else {
this.page.set_title(this.page_title);
}
},

load_last_view: function () {
var us = frappe.get_user_settings(this.doctype);
var route = ['List', this.doctype];

if (us.last_view && us.last_view !== 'List') {
route.push(us.last_view);

if (us.last_view === 'Kanban') {
route.push(us['Kanban'].last_kanban_board);
}
}

frappe.set_route(route);
},

init_headers: function () {
this.page.main.find('.list-headers > .list-row-head').hide();
this.list_header = this.page.main.find('.list-headers > '
+ '.list-row-head[data-list-renderer="'
+ this.list_renderer.name +'"]');
if(this.list_header.length > 0) {
this.list_header.show();
return;
}


var html = this.list_renderer.get_header_html();
if(!html) {
this.list_header = $();
return;
}

this.list_header = $(html).appendTo(this.page.main.find('.list-headers'));

this.setup_like();
this.setup_select_all();
this.setup_delete();
},

list_renderer_onload: function () {
if (this.list_renderer.settings.onload) {
this.list_renderer.settings.onload(this);
}
},

set_sidebar_height: function () {
var h_main = this.page.sidebar.height();
var h_side = this.$page.find('.layout-side-section').height();
if (h_side > h_main)
this.$page.find('.layout-main-section').css({ 'min-height': h_side });
},

init_filters: function () {
this.filter_list = new frappe.ui.FilterList({
base_list: this,
parent: this.wrapper.find('.list-filters').show(),
doctype: this.doctype,
default_filters: []
});
this.set_filters(this.list_renderer.filters);
},

set_filters: function (filters) {
var me = this;
$.each(filters, function (i, f) {
if (f.length === 3) {
f = [me.doctype, f[0], f[1], f[2]]
}
me.filter_list.add_filter(f[0], f[1], f[2], f[3]);
});
},

init_sort_selector: function () {
var me = this;
var order_by = this.list_renderer.order_by;

this.sort_selector = new frappe.ui.SortSelector({
parent: this.wrapper.find('.list-filters'),
doctype: this.doctype,
args: order_by,
change: function () { me.run(); }
});
},

show_match_help: function () {
var me = this;
var match_rules_list = frappe.perm.get_match_rules(this.doctype);
var perm = frappe.perm.get_perm(this.doctype);

if (match_rules_list.length) {
this.footnote_area =
frappe.utils.set_footnote(this.footnote_area, this.$page.find('.layout-main-section'),
frappe.render_template('list_permission_footer', {
condition_list: match_rules_list
}));
$(this.footnote_area).css({ 'margin-top': '0px' });
}
},

setup_view_variables: function () {
var route = frappe.get_route();
this.last_view = this.current_view || '';
this.current_view = route[2] || 'List';
},

init_base_list: function (auto_run) {
var me = this;
// init list
this.make({
method: 'frappe.desk.reportview.get',
save_user_settings: true,
get_args: this.get_args,
parent: this.wrapper,
freeze: true,
start: 0,
page_length: this.list_renderer.page_length,
show_filters: false,
new_doctype: this.doctype,
no_result_message: this.make_no_result(),
show_no_result: function() {
return me.list_renderer.show_no_result;
}
});

this.setup_make_new_doc();

if (auto_run !== false && auto_run !== 0)
this.refresh();
},

setup_make_new_doc: function () {
var me = this;
// make_new_doc can be overridden so that default values can be prefilled
// for example - communication list in customer
if (this.list_renderer.settings.list_view_doc) {
this.list_renderer.settings.list_view_doc(this);
} else {
$(this.wrapper).on('click', `button[list_view_doc='${this.doctype}']`, function () {
me.make_new_doc.apply(me, [me.doctype]);
});
}
},

refresh: function (dirty) {
var me = this;

if (dirty !== undefined) this.dirty = dirty;

this.refresh_sidebar();
this.setup_view_variables();

if (this.list_renderer.should_refresh()) {
this.setup_list_renderer();
this.refresh_surroundings();
this.dirty = true;
}

if (this.list_renderer.settings.refresh) {
this.list_renderer.settings.refresh(this);
}

this.set_filters_before_run();
this.execute_run();
},

execute_run: function () {
if (this.dirty) {
this.run();
if (this.clean_dash != true) {
this.filter_list.reload_stats();
}
} else {
if (new Date() - (this.last_updated_on || 0) > 30000) {
// older than 5 mins, refresh
this.run();
}
}
},

save_user_settings_locally: function (args) {

frappe.provide('frappe.model.user_settings.' + this.doctype + '.' + this.list_renderer.name);
var user_settings_common = frappe.model.user_settings[this.doctype];
var user_settings = frappe.model.user_settings[this.doctype][this.list_renderer.name];

if (!user_settings) return;

var different = false;
if (!frappe.utils.arrays_equal(args.filters, user_settings.filters)) {
// settings are dirty if filters change
user_settings.filters = args.filters;
different = true;
}

if (user_settings.order_by !== args.order_by) {
user_settings.order_by = args.order_by;
different = true;
}

if (user_settings.page_length !== args.page_length) {
user_settings.page_length = args.page_length || 20
different = true;
}

// save fields in list settings
if (args.save_user_settings_fields) {
user_settings.fields = args.fields;
}

// save last view
if (user_settings_common.last_view !== this.current_view) {
user_settings_common.last_view = this.current_view;
different = true;
}

if (different) {
user_settings_common.updated_on = moment().toString();
}
},

set_filters_before_run: function () {
// set filters from frappe.route_options
// before switching pages, frappe.route_options can have pre-set filters
// for the list view
var me = this;

if (frappe.route_options) {
this.set_filters_from_route_options();
this.dirty = true;
}
},

run: function (more) {
// set filter from route
var me = this;

if (this.fresh && !more) {
return;
}

if (this.list_renderer.settings.before_run) {
this.list_renderer.settings.before_run(this);
}

if (!this.list_renderer.settings.use_route) {
var route = frappe.get_route();
if (route[2] && !in_list(['Image', 'Gantt', 'Kanban', 'Calendar'], route[2])) {
$.each(frappe.utils.get_args_dict_from_url(route[2]), function (key, val) {
me.set_filter(key, val, true);
});
}
}

this.list_header.find('.list-liked-by-me')
.toggleClass('text-extra-muted not-liked', !this.is_star_filtered());

this.last_updated_on = new Date();
this.dirty = false;
this.clean_dash = false;
// set a fresh so that multiple refreshes do not happen
// at the same time. This is true when deleting.
// AJAX response will try to refresh and list_update notification
// via async will also try to update.
// It is not possible to guess which will reach first
// (most probably async will) but this is a forced way
// to prevent instant refreshes on mutilple triggers
// in a loosly coupled way.
this.fresh = true;
setTimeout(function () {
me.fresh = false;
}, 1000);

this._super(more);

if (this.list_renderer.settings.post_render) {
this.list_renderer.settings.post_render(this);
}

this.wrapper.on('render-complete', function() {
me.list_renderer.after_refresh();
})
},

make_no_result: function () {
var new_button = frappe.boot.user.can_create.includes(this.doctype)
? (`<p><button class='btn btn-primary btn-sm'
list_view_doc='${this.doctype}'>
${__('Make a new ' + __(this.doctype))}
</button></p>`)
: '';
var no_result_message =
`<div class='msg-box no-border'>
<p>${__('No {0} found', [__(this.doctype)])}</p>
${new_button}
</div>`;

return no_result_message;
},

get_args: function () {
var args = {
doctype: this.doctype,
fields: this.list_renderer.fields,
filters: this.get_filters_args(),
order_by: this.get_order_by_args(),
with_comment_count: true
}
return args;
},
get_filters_args: function() {
var filters = [];
if(this.filter_list) {
// get filters from filter_list
filters = this.filter_list.get_filters();
} else {
filters = this.list_renderer.filters;
}
// remove duplicates
var uniq = filters.uniqBy(JSON.stringify);
return uniq;
},
get_order_by_args: function() {
var order_by = '';
if(this.sort_selector) {
// get order_by from sort_selector
order_by = this.sort_selector.sort_by + ' ' + this.sort_selector.sort_order;
} else {
order_by = this.list_renderer.order_by;
}
return order_by;
},
assigned_to_me: function () {
this.filter_list.add_filter(this.doctype, '_assign', 'like', '%' + user + '%');
this.run();
},
liked_by_me: function () {
this.filter_list.add_filter(this.doctype, '_liked_by', 'like', '%' + user + '%');
this.run();
},
remove_liked_by_me: function () {
this.filter_list.get_filter('_liked_by').remove();
},
is_star_filtered: function () {
return this.filter_list.filter_exists(this.doctype, '_liked_by', 'like', '%' + user + '%');
},
init_menu: function () {
var me = this;
this.$page.on('click', '.list-tag-preview', function () { me.toggle_tags(); });

// Refresh button for large screens
this.page.set_secondary_action(__('Refresh'), function () {
me.refresh(true);
}, 'octicon octicon-sync')
.addClass('hidden-xs');

// Refresh button as menu item in small screens
this.page.add_menu_item(__('Refresh'), function () {
me.refresh(true);
}, 'octicon octicon-sync')
.addClass('visible-xs');

if (frappe.model.can_import(this.doctype)) {
this.page.add_menu_item(__('Import'), function () {
frappe.set_route('data-import-tool', {
doctype: me.doctype
});
}, true);
}
if (frappe.model.can_set_user_permissions(this.doctype)) {
this.page.add_menu_item(__('User Permissions Manager'), function () {
frappe.set_route('user-permissions', {
doctype: me.doctype
});
}, true);
}
if (user_roles.includes('System Manager')) {
this.page.add_menu_item(__('Role Permissions Manager'), function () {
frappe.set_route('permission-manager', {
doctype: me.doctype
});
}, true);
this.page.add_menu_item(__('Customize'), function () {
frappe.set_route('Form', 'Customize Form', {
doc_type: me.doctype
})
}, true);
}

this.make_bulk_assignment();
this.make_bulk_printing();

// add to desktop
this.page.add_menu_item(__('Add to Desktop'), function () {
frappe.add_to_desktop(me.doctype, me.doctype);
}, true);

if (user_roles.includes('System Manager') && frappe.boot.developer_mode === 1) {
// edit doctype
this.page.add_menu_item(__('Edit DocType'), function () {
frappe.set_route('Form', 'DocType', me.doctype);
}, true);
}

},
make_bulk_assignment: function () {

var me = this;

//bulk assignment
me.page.add_menu_item(__('Assign To'), function () {

var docnames = me.get_checked_items().map(function (doc) {
return doc.name;
});

if (docnames.length >= 1) {
me.dialog = new frappe.ui.form.AssignToDialog({
obj: me,
method: 'frappe.desk.form.assign_to.add_multiple',
doctype: me.doctype,
docname: docnames,
bulk_assign: true,
re_assign: true,
callback: function () {
me.refresh(true);
}
});
me.dialog.clear();
me.dialog.show();
}
else {
frappe.msgprint(__('Select records for assignment'))
}
}, true);

},
make_bulk_printing: function () {
var me = this;
var print_settings = frappe.model.get_doc(':Print Settings', 'Print Settings')
var allow_print_for_draft = cint(print_settings.allow_print_for_draft)
var is_submittable = frappe.model.is_submittable(me.doctype)
var allow_print_for_cancelled = cint(print_settings.allow_print_for_cancelled)

//bulk priting
me.page.add_menu_item(__('Print'), function () {
var items = me.get_checked_items();

var valid_docs =
items.filter(function (doc) {
return !is_submittable || doc.docstatus === 1 ||
(allow_print_for_cancelled && doc.docstatus == 2) ||
(allow_print_for_draft && doc.docstatus == 0) ||
user_roles.includes('Administrator')
}).map(function (doc) {
return doc.name
});

var invalid_docs = items.filter(function (doc) {
return !valid_docs.includes(doc.name);
});

if (invalid_docs.length >= 1) {
frappe.msgprint('You selected Draft or Cancelled documents')
return;
}

if (valid_docs.length >= 1) {

var dialog = new frappe.ui.Dialog({
title: 'Print Documents',
fields: [
{ 'fieldtype': 'Check', 'label': __('With Letterhead'), 'fieldname': 'with_letterhead' },
{ 'fieldtype': 'Select', 'label': __('Print Format'), 'fieldname': 'print_sel' },
]
});

dialog.set_primary_action(__('Print'), function () {
args = dialog.get_values();
if (!args) return;
var default_print_format = locals.DocType[me.doctype].default_print_format;
with_letterhead = args.with_letterhead ? 1 : 0;
print_format = args.print_sel ? args.print_sel : default_print_format;

var json_string = JSON.stringify(valid_docs);
var w = window.open('/api/method/frappe.utils.print_format.download_multi_pdf?'
+ 'doctype=' + encodeURIComponent(me.doctype)
+ '&name=' + encodeURIComponent(json_string)
+ '&format=' + encodeURIComponent(print_format)
+ '&no_letterhead=' + (with_letterhead ? '0' : '1'));
if (!w) {
frappe.msgprint(__('Please enable pop-ups')); return;
}
});

print_formats = frappe.meta.get_print_formats(me.doctype);
dialog.fields_dict.print_sel.$input.empty().add_options(print_formats);

dialog.show();
}
else {
frappe.msgprint(__('Select atleast 1 record for printing'))
}
}, true);
},

setup_like: function () {
var me = this;
this.$page.find('.result-list').on('click', '.like-action', frappe.ui.click_toggle_like);
this.list_header.find('.list-liked-by-me').on('click', function () {
if (me.is_star_filtered()) {
me.remove_liked_by_me();
} else {
me.liked_by_me();
}
});

if (!frappe.dom.is_touchscreen()) {
frappe.ui.setup_like_popover(this.$page.find('.result-list'), '.liked-by');
}
},

setup_select_all: function () {
var me = this;

if (this.can_delete || this.list_renderer.settings.selectable) {
this.list_header.find('.list-select-all').on('click', function () {
me.$page.find('.list-row-checkbox').prop('checked', $(this).prop('checked'));
});

this.$page.on('click', '.list-row-checkbox', function (event) {
// multi-select using shift key
var $this = $(this);
if (event.shiftKey && $this.prop('checked')) {
var $end_row = $this.parents('.list-row');
var $start_row = $end_row.prevAll('.list-row')
.find('.list-row-checkbox:checked').last().parents('.list-row');
if ($start_row) {
$start_row.nextUntil($end_row).find('.list-row-checkbox').prop('checked', true);
}
}
});
}
},

setup_delete: function () {
var me = this;
if (!(this.can_delete || this.list_renderer.settings.selectable)) {
return;
}
this.$page.find('.list-row-checkbox').change(function () {
me.toggle_delete();
});
// after delete, hide delete button
this.wrapper.on('render-complete', function () {
me.toggle_delete();
});
},

toggle_delete: function () {
var checked_items = this.get_checked_items();
var checked_items_status = this.$page.find('.checked-items-status');

if (checked_items.length > 0) {
this.page.set_primary_action(__('Delete'), function () {
me.delete_items()
}, 'octicon octicon-trashcan')
.addClass('btn-danger');

checked_items_status.text(
no_of_checked_items == 1
? __('1 item selected')
: __('{0} items selected', [checked_items.length])
)
checked_items_status.removeClass('hide');
} else {
this.page.btn_primary.removeClass('btn-danger');
this.set_primary_action();
checked_items_status.addClass('hide');
}
},

toggle_tags: function () {
if (this.tags_shown) {
$('.tag-row').addClass('hide');
this.tags_shown = false;
} else {
$('.tag-row').removeClass('hide');
this.tags_shown = true;
}
},

get_checked_items: function () {
var names = this.$page.find('.list-row-checkbox:checked').map(function (i, item) {
return $(item).data().name;
}).toArray();

return this.data.filter(function (doc) {
return names.includes(doc.name);
});
},

set_primary_action: function () {
if (this.list_renderer.settings.set_primary_action) {
this.list_renderer.settings.set_primary_action(this);
} else {
this._super();
}
},

delete_items: function () {
var me = this;
var to_delete = this.get_checked_items();
if (!to_delete.length)
return;

var docnames = to_delete.map(function (doc) {
return doc.name;
});

frappe.confirm(__('Delete {0} items permanently?', [to_delete.length]),
function () {
return frappe.call({
method: 'frappe.desk.reportview.delete_items',
freeze: true,
args: {
items: docnames,
doctype: me.doctype
},
callback: function () {
me.$page.find('.list-select-all').prop('checked', false);
frappe.utils.play_sound('delete');
me.refresh(true);
}
})
}
);
},
refresh_sidebar: function () {
//TODO: refresh if already exist
this.list_sidebar = new frappe.views.ListSidebar({
doctype: this.doctype,
stats: this.list_renderer.stats,
parent: this.$page.find('.layout-side-section'),
set_filter: this.set_filter.bind(this),
default_filters: this.list_renderer.filters,
page: this.page,
list_view: this
})
}
});

+ 0
- 403
frappe/public/js/frappe/list/listview.js Voir le fichier

@@ -1,403 +0,0 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

frappe.views.get_listview = function(doctype, parent) {
if(frappe.listviews[doctype]) {
var listview = new frappe.listviews[doctype](parent);
} else {
var listview = new frappe.views.ListView(parent, doctype);
}
return listview;
}

// Renders customized list
// usually based on `in_list_view` property

frappe.views.ListView = Class.extend({
init: function(doclistview, doctype) {
this.doclistview = doclistview;
this.doctype = doctype;
this.meta = frappe.get_doc("DocType", this.doctype);
this.image_field = this.meta.image_field || 'image';
this.settings = frappe.listview_settings[this.doctype] || {};
if(this.meta.__listview_template) {
this.template_name = doctype + "_listview";
frappe.templates[this.template_name] = this.meta.__listview_template;
}
this.set_fields();
this.set_columns();
this.id_list = [];
if(this.settings.group_by)
this.group_by = this.settings.group_by;

var me = this;
this.doclistview.onreset = function() {
me.id_list = [];
}
this.order_by = this.settings.order_by;
this.group_by = this.settings.group_by;
},
set_fields: function() {
var me = this;
var t = "`tab"+this.doctype+"`.";
this.fields = [];
this.stats = ['_user_tags'];

var add_field = function(fieldname) {
field = t + "`" + fieldname + "`"
if(me.fields.indexOf(field)=== -1)
me.fields.push(field);
}

$.each(['name', 'owner', 'docstatus', '_user_tags', '_comments', 'modified',
'modified_by', '_assign', '_liked_by', '_seen'],
function(i, fieldname) { add_field(fieldname); })

// add title field
if(this.meta.title_field) {
this.title_field = this.meta.title_field;
add_field(this.meta.title_field);
}

// endabled / disabled
if(frappe.meta.has_field(this.doctype, 'enabled')) { add_field('enabled'); };
if(frappe.meta.has_field(this.doctype, 'disabled')) { add_field('disabled'); };

// add workflow field (as priority)
this.workflow_state_fieldname = frappe.workflow.get_state_fieldname(this.doctype);
if(this.workflow_state_fieldname) {
if (!frappe.workflow.workflows[this.doctype]["override_status"]) {
add_field(this.workflow_state_fieldname);
}
this.stats.push(this.workflow_state_fieldname);
}

$.each(this.meta.fields, function(i,d) {
if(d.in_list_view && frappe.perm.has_perm(me.doctype, d.permlevel, "read")) {
if(d.fieldtype=="Image" && d.options) {
add_field(d.options);
} else {
add_field(d.fieldname);
}
// currency field for symbol (multi-currency)
if(d.fieldtype=="Currency" && d.options) {
if(d.options.indexOf(":")!=-1) {
add_field(d.options.split(":")[1]);
} else {
add_field(d.options);
};
}
}
});

// additional fields
if(this.settings.add_fields) {
$.each(this.settings.add_fields, function(i, d) {
if(d.indexOf("`tab")===-1) {
d = "`tab" + me.doctype + "`." + d;
}
if(me.fields.indexOf(d)==-1)
me.fields.push(d);
});
}

if(me.meta.__kanban_column_fields)
me.fields = me.fields.concat(me.meta.__kanban_column_fields);
},
set_columns: function() {
var me = this;
this.columns = [];
var name_column = {
colspan: this.settings.colwidths && this.settings.colwidths.subject || 6,
type: "Subject",
title: "Name"
};
if (this.meta.title_field) {
name_column.title = frappe.meta.get_docfield(this.doctype, this.meta.title_field).label;
}
this.columns.push(name_column);
this.total_colspans = this.columns[0].colspan;


if(frappe.has_indicator(this.doctype)) {
// indicator
this.columns.push({
colspan: this.settings.colwidths && this.settings.colwidths.indicator || 3,
type: "Indicator",
title: "Status"
});
this.total_colspans += this.columns[1].colspan;
}

// overridden
var overridden = $.map(this.settings.add_columns || [], function(d) {
return d.content;
});
var docfields_in_list_view = frappe.get_children("DocType", this.doctype, "fields",
{"in_list_view":1}).sort(function(a, b) { return a.idx - b.idx })

$.each(docfields_in_list_view, function(i,d) {
if(in_list(overridden, d.fieldname) || d.fieldname === me.title_field) {
return;
}
if(me.total_colspans < 12) {
me.add_column(d);
}
});

// additional columns
if(this.settings.add_columns) {
$.each(this.settings.add_columns, function(i, d) {
if(me.total_colspans < 12) {
if(typeof d==="string") {
me.add_column(frappe.meta.get_docfield(me.doctype, d));
} else {
me.columns.push(d);
me.total_colspans += parseInt(d.colspan);
}
}
});
}

var empty_cols = flt(12 - this.total_colspans);
while(empty_cols > 0) {
for(var i=0, l=this.columns.length; i < l && empty_cols > 0; i++) {
this.columns[i].colspan = cint(this.columns[i].colspan) + 1;
empty_cols = empty_cols - 1;
}
}
},
add_column: function(df) {
// field width
var colspan = 3;
if(in_list(["Int", "Percent"], df.fieldtype)) {
colspan = 2;
} else if(in_list(["Check", "Image"], df.fieldtype)) {
colspan = 1;
} else if(in_list(["name", "subject", "title"], df.fieldname)) { // subjects are longer
colspan = 4;
} else if(df.fieldtype=="Text Editor" || df.fieldtype=="Text") {
colspan = 4;
}
if(df.columns && df.columns>0){
colspan = df.columns;
}
else if(this.settings.column_colspan && this.settings.column_colspan[df.fieldname]) {
colspan = this.settings.column_colspan[df.fieldname];
}
this.total_colspans += parseInt(colspan);
var col = {
colspan: colspan,
content: df.fieldname,
type: df.fieldtype,
df:df,
fieldtype: df.fieldtype,
fieldname: df.fieldname,
title:__(df.label)
};
if(this.settings.column_render && this.settings.column_render[df.fieldname]) {
col.render = this.settings.column_render[df.fieldname];
}
this.columns.push(col);

},
render: function(row, data) {
this.prepare_data(data);

// maintain id_list to avoid duplication incase
// of filtering by child table
if(in_list(this.id_list, data.name)) {
$(row).toggle(false);
return;
} else {
this.id_list.push(data.name);
}

this['render_row_' + this.doclistview.current_view](row, data);

if(this.settings.post_render_item) {
this.settings.post_render_item(this, row, data);
}

this.render_tags(row, data);

},
render_row_List: function(row, data) {
var main = frappe.render_template("list_item_main", {
data: data,
columns: this.columns,
subject: this.get_avatar_and_id(data, true),
list: this,
right_column: this.settings.right_column
});

$(frappe.render_template("list_item_row", {
data: data,
main: main,
list: this,
right_column: this.settings.right_column
})).appendTo(row);
},
render_row_Image: function(row, data) {
this.allowed_type = [
"Check", "Currency", "Data", "Date",
"Datetime", "Float", "Int", "Link",
"Percent", "Select", "Read Only", "Time"
];
var image_url = (data.image && window.cordova && data.image.indexOf('http')===-1) ?
frappe.base_url + data[this.image_field] : data[this.image_field];

img_col = $(frappe.render_template("image_view_item_row", {
data: data,
list: this,
columns: this.columns,
allowed_type: this.allowed_type,
item_image: image_url ? "url('" + image_url + "')" : null,
color: frappe.get_palette(data.item_name),
subject: this.get_avatar_and_id(data, true),
right_column: this.settings.right_column
}))
.data("data", data)
.appendTo($(row).find(".image-view-marker"));
},
render_tags: function(row, data) {
var me = this;
var row2 = $('<div class="tag-row">\
<div class="list-tag xs-hidden"></div>\
<div class="clearfix"></div>\
</div>').appendTo(row);

if(!me.doclistview.tags_shown) {
row2.addClass("hide");
}

// add tags
var tag_editor = new frappe.ui.TagEditor({
parent: row2.find(".list-tag"),
frm: {
doctype: this.doctype,
docname: data.name
},
list_sidebar: me.doclistview.list_sidebar,
user_tags: data._user_tags,
on_change: function(user_tags) {
data._user_tags = user_tags;
//me.render_timestamp_and_comments(row, data);
}
});
tag_editor.wrapper.on("click", ".tagit-label", function() {
me.doclistview.set_filter("_user_tags",
$(this).text());
});
},

get_avatar_and_id: function(data, without_workflow) {
data._without_workflow = without_workflow;
data.css_seen = '';

if(data._seen) {
var seen = JSON.parse(data._seen);
if(seen && seen.indexOf(frappe.session.user) !== -1) {
data.css_seen = 'seen'
}
}

return frappe.render_template("list_item_subject", data);
},

get_indicator: function(doc) {
var indicator = frappe.get_indicator(doc, this.doctype);
if(indicator) {
return '<span class="indicator '+indicator[1]+' filterable" data-filter="'
+indicator[2]+'">'+__(indicator[0])+'<span>';
} else {
return "";
}
},

get_indicator_dot: function(doc) {
var indicator = frappe.get_indicator(doc, this.doctype);
if (!indicator) {
return "";
}
return '<span class="indicator '+indicator[1]+'" title="'+__(indicator[0])+'"></span>';
},

prepare_data: function(data) {
if(data.modified)
this.prepare_when(data, data.modified);

data._liked_by = data._liked_by ?
JSON.parse(data._liked_by) : [];

data._checkbox = (frappe.model.can_delete(this.doctype) || this.settings.selectable) && !this.no_delete

data._doctype_encoded = encodeURIComponent(data.doctype);
data._name = data.name.replace(/"/g, '\"');
data._name_encoded = encodeURIComponent(data.name);
data._submittable = frappe.model.is_submittable(this.doctype);

data._title = strip_html(data[this.title_field || "name"] || data["name"]);
data._full_title = data._title;

if(data._title.length > 40) {
data._title = data._title.slice(0, 40) + "...";
}

data._workflow = null;
if(this.workflow_state_fieldname) {
data._workflow = {
fieldname: this.workflow_state_fieldname,
value: data[this.workflow_state_fieldname],
style: frappe.utils.guess_style(data[this.workflow_state_fieldname])
}
}
data._user = user;

data._tags = $.map((data._user_tags || "").split(","),
function(v) { return v ? v : null; });
data._assign_list = data._assign ? JSON.parse(data._assign) : [];

// nulls as strings
for(key in data) {
if(data[key]==null) {
data[key]='';
}
}

// prepare data in settings
if(this.settings.prepare_data)
this.settings.prepare_data(data);
},

prepare_when: function(data, date_str) {
if (!date_str) date_str = data.modified;
// when
data.when = (dateutil.str_to_user(date_str)).split(' ')[0];
var diff = dateutil.get_diff(dateutil.get_today(), date_str.split(' ')[0]);
if(diff==0) {
data.when = comment_when(date_str);
}
if(diff == 1) {
data.when = __('Yesterday')
}
if(diff == 2) {
data.when = __('2 days ago')
}
},

render_bar_graph: function(parent, data, field, label) {
var args = {
percent: data[field],
label: __(label)
}
$(parent).append(repl('<span class="progress" style="width: 100%; float: left; margin: 5px 0px;"> \
<span class="progress-bar" title="%(percent)s% %(label)s" \
style="width: %(percent)s%;"></span>\
</span>', args));
},
render_icon: function(parent, icon_class, label) {
var icon_html = "<i class='%(icon_class)s' title='%(label)s'></i>";
$(parent).append(repl(icon_html, {icon_class: icon_class, label: __(label) || ''}));
}
});

+ 62
- 8
frappe/public/js/frappe/misc/utils.js Voir le fichier

@@ -185,20 +185,21 @@ frappe.utils = {
me.intro_area = null;
}
},
set_footnote: function(me, wrapper, txt) {
if(!me.footnote_area) {
me.footnote_area = $('<div class="text-muted footnote-area">')
set_footnote: function(footnote_area, wrapper, txt) {
if(!footnote_area) {
footnote_area = $('<div class="text-muted footnote-area">')
.appendTo(wrapper);
}

if(txt) {
if(txt.search(/<p>/)==-1) txt = '<p>' + txt + '</p>';
me.footnote_area.html(txt);
if(!txt.includes('<p>'))
txt = '<p>' + txt + '</p>';
footnote_area.html(txt);
} else {
me.footnote_area.remove();
me.footnote_area = null;
footnote_area.remove();
footnote_area = null;
}
return me.footnote_area;
return footnote_area;
},
get_args_dict_from_url: function(txt) {
var args = {};
@@ -593,3 +594,56 @@ frappe.utils = {
return email_list;
}
};

// String.prototype.includes polyfill
// https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/String/includes
if (!String.prototype.includes) {
String.prototype.includes = function(search, start) {
'use strict';
if (typeof start !== 'number') {
start = 0;
}
if (start + search.length > this.length) {
return false;
} else {
return this.indexOf(search, start) !== -1;
}
};
}
// Array.prototype.includes polyfill
// https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
if (!Array.prototype.includes) {
Object.defineProperty(Array.prototype, 'includes', {
value: function(searchElement, fromIndex) {
if (this == null) {
throw new TypeError('"this" is null or not defined');
}
var o = Object(this);
var len = o.length >>> 0;
if (len === 0) {
return false;
}
var n = fromIndex | 0;
var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
while (k < len) {
if (o[k] === searchElement) {
return true;
}
k++;
}
return false;
}
});
}
// Array de duplicate
if (!Array.prototype.uniqBy) {
Object.defineProperty(Array.prototype, 'uniqBy', {
value: function (key) {
var seen = {};
return this.filter(function (item) {
var k = key(item);
return seen.hasOwnProperty(k) ? false : (seen[k] = true);
})
}
})
}

+ 4
- 5
frappe/public/js/frappe/model/model.js Voir le fichier

@@ -33,7 +33,7 @@ $.extend(frappe.model, {

new_names: {},
events: {},
list_settings: {},
user_settings: {},

init: function() {
// setup refresh if the document is updated somewhere else
@@ -105,7 +105,6 @@ $.extend(frappe.model, {
if(r.exc) {
msgprint(__("Unable to load: {0}", [__(doctype)]));
throw "No doctype";
return;
}
if(r.message=="use_cache") {
frappe.model.sync(cached_doc);
@@ -115,10 +114,10 @@ $.extend(frappe.model, {
frappe.model.init_doctype(doctype);
frappe.defaults.set_user_permissions(r.user_permissions);

if(r.list_settings) {
if(r.user_settings) {
// remember filters and other settings from last view
frappe.model.list_settings[doctype] = JSON.parse(r.list_settings);
frappe.model.list_settings[doctype].updated_on = moment().toString();
frappe.model.user_settings[doctype] = JSON.parse(r.user_settings);
frappe.model.user_settings[doctype].updated_on = moment().toString();
}
callback && callback(r);
}


+ 41
- 0
frappe/public/js/frappe/model/user_settings.js Voir le fichier

@@ -0,0 +1,41 @@
frappe.provide('frappe.model.user_settings');

$.extend(frappe.model.user_settings, {
save: function(doctype, key, value) {
var user_settings = frappe.model.user_settings[doctype] || {};

if ($.isPlainObject(value)) {
$.extend(user_settings[key], value);
} else {
user_settings[key] = value;
}

return this.update(doctype, user_settings);
},
remove: function(doctype, key) {
var user_settings = frappe.model.user_settings[doctype] || {};
delete user_settings[key];

return this.update(doctype, user_settings);
},
update: function(doctype, user_settings) {
return frappe.call({
method: 'frappe.model.utils.user_settings.save',
args: {
doctype: doctype,
user_settings: user_settings
},
callback: function(r) {
frappe.model.user_settings[doctype] = r.message;
}
})
}
});

frappe.get_user_settings = function(doctype, key) {
var settings = frappe.model.user_settings[doctype] || {};
if(key) {
settings = settings[key] || {};
}
return settings;
}

+ 2
- 1
frappe/public/js/frappe/router.js Voir le fichier

@@ -124,7 +124,8 @@ frappe.set_route = function() {
frappe.route_options = a;
return null;
} else {
return a ? encodeURIComponent(a) : null;
return a;
// return a ? encodeURIComponent(a) : null;
}
}).join('/');



+ 434
- 0
frappe/public/js/frappe/ui/base_list.js Voir le fichier

@@ -0,0 +1,434 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

// new re-re-factored Listing object
// now called BaseList
//
// opts:
// parent

// method (method to call on server)
// args (additional args to method)
// get_args (method to return args as dict)

// show_filters [false]
// doctype
// filter_fields (if given, this list is rendered, else built from doctype)

// query or get_query (will be deprecated)
// query_max
// buttons_in_frame

// no_result_message ("No result")

// page_length (20)
// hide_refresh (False)
// no_toolbar
// new_doctype
// [function] render_row(parent, data)
// [function] onrun
// no_loading (no ajax indicator)

frappe.provide('frappe.ui');

frappe.ui.BaseList = Class.extend({
init: function (opts) {
this.opts = opts || {};
this.set_defaults();
if (opts) {
this.make();
}
},
set_defaults: function () {
this.page_length = 20;
this.start = 0;
this.data = [];
},
make: function (opts) {
if (opts) {
this.opts = opts;
}
this.prepare_opts();

$.extend(this, this.opts);

// make dom
this.wrapper = $(frappe.render_template('listing', this.opts));
this.parent.append(this.wrapper);

this.set_events();

if (this.page) {
this.wrapper.find('.list-toolbar-wrapper').hide();
}

if (this.show_filters) {
this.make_filters();
}
},
prepare_opts: function () {
if (this.opts.new_doctype) {
if (!frappe.boot.user.can_create.includes(this.opts.new_doctype)) {
this.opts.new_doctype = null;
}
}
if (!this.opts.no_result_message) {
this.opts.no_result_message = __('Nothing to show');
}
if (!this.opts.page_length) {
this.opts.page_length = this.user_settings && this.user_settings.limit || 20;
}
this.opts._more = __('More');
},
add_button: function (label, click, icon) {
if (this.page) {
return this.page.add_menu_item(label, click, icon)
} else {
this.wrapper.find('.list-toolbar-wrapper').removeClass('hide');
return $('<button class="btn btn-default"></button>')
.appendTo(this.wrapper.find('.list-toolbar'))
.html((icon ? ('<i class="' + icon + '"></i> ') : '') + label)
.click(click);
}
},
set_events: function () {
var me = this;

// next page
this.wrapper.find('.btn-more').click(function () {
me.run(true);
});

this.wrapper.find(".btn-group-paging").on('click', '.btn', function () {
me.page_length = cint($(this).attr("data-value"));

me.wrapper.find(".btn-group-paging .btn-info").removeClass("btn-info");
$(this).addClass("btn-info");

// always reset when changing list page length
me.run();
});

// select the correct page length
if (this.opts.page_length !== 20) {
this.wrapper.find(".btn-group-paging .btn-info").removeClass("btn-info");
this.wrapper
.find(".btn-group-paging .btn[data-value='" + this.opts.page_length + "']")
.addClass('btn-info');
}

// title
if (this.title) {
this.wrapper.find('h3').html(this.title).show();
}

// new
this.set_primary_action();

if (me.no_toolbar || me.hide_toolbar) {
me.wrapper.find('.list-toolbar-wrapper').hide();
}
},

set_primary_action: function () {
var me = this;
if (this.new_doctype) {
this.page.set_primary_action(
__("New"),
me.make_new_doc.bind(me, me.new_doctype),
"octicon octicon-plus"
);
} else {
this.page.clear_primary_action();
}
},

make_new_doc: function (doctype) {
var me = this;
frappe.model.with_doctype(doctype, function () {
if (me.custom_new_doc) {
me.custom_new_doc(doctype);
} else {
if (me.filter_list) {
frappe.route_options = {};
me.filter_list.get_filters().forEach(function (f, i) {
if (f[2] === "=" && !frappe.model.std_fields_list.includes(f[1])) {
frappe.route_options[f[1]] = f[3];
}
});
}
frappe.new_doc(doctype, true);
}
});
},

make_filters: function () {
this.filter_list = new frappe.ui.FilterList({
base_list: this,
parent: this.wrapper.find('.list-filters').show(),
doctype: this.doctype,
filter_fields: this.filter_fields,
default_filters: this.default_filters || []
});
// default filter for submittable doctype
if (frappe.model.is_submittable(this.doctype)) {
this.filter_list.add_filter(this.doctype, "docstatus", "!=", 2);
};
},

clear: function () {
this.data = [];
this.wrapper.find('.result-list').empty();
this.wrapper.find('.result').show();
this.wrapper.find('.no-result').hide();
this.start = 0;
this.onreset && this.onreset();
},

set_filters_from_route_options: function () {
var me = this;
this.filter_list.clear_filters();

for(var field in frappe.route_options) {
var value = frappe.route_options[field];
var doctype = null;

// if `Child DocType.fieldname`
if (field.includes(".")) {
doctype = field.split(".")[0];
field = field.split(".")[1];
}

// find the table in which the key exists
// for example the filter could be {"item_code": "X"}
// where item_code is in the child table.

// we can search all tables for mapping the doctype
if (!doctype) {
doctype = frappe.meta.get_doctype_for_field(me.doctype, field);
}

if (doctype) {
if ($.isArray(value)) {
me.filter_list.add_filter(doctype, field, value[0], value[1]);
} else {
me.filter_list.add_filter(doctype, field, "=", value);
}
}
}
frappe.route_options = null;
},

run: function (more) {
var me = this;
if (!more) {
this.start = 0;
this.onreset && this.onreset();
}

var args = this.get_call_args();
this.save_user_settings_locally(args);

// user_settings are saved by db_query.py when dirty
$.extend(args, {
user_settings: frappe.model.user_settings[this.doctype]
});

return frappe.call({
method: this.opts.method || 'frappe.desk.query_builder.runquery',
type: "GET",
freeze: this.opts.freeze !== undefined ? this.opts.freeze : true,
args: args,
callback: function (r) {
me.dirty = false;
me.render_results(r);
},
no_spinner: this.opts.no_loading
});
},
save_user_settings_locally: function (args) {
if (this.opts.save_user_settings && this.doctype && !this.docname) {
// save list settings locally
var user_settings = frappe.model.user_settings[this.doctype];
var different = false;

if (!user_settings) {
return;
}

if (!frappe.utils.arrays_equal(args.filters, user_settings.filters)) {
// settings are dirty if filters change
user_settings.filters = args.filters;
different = true;
}

if (user_settings.order_by !== args.order_by) {
user_settings.order_by = args.order_by;
different = true;
}

if (user_settings.limit !== args.limit_page_length) {
user_settings.limit = args.limit_page_length || 20
different = true;
}

// save fields in list settings
if (args.save_user_settings_fields) {
user_settings.fields = args.fields;
}

if (different) {
user_settings.updated_on = moment().toString();
}
}
},
get_call_args: function () {
// load query
if (!this.method) {
var query = this.get_query && this.get_query() || this.query;
query = this.add_limits(query);
var args = {
query_max: this.query_max,
as_dict: 1
}
args.simple_query = query;
} else {
var args = {
start: this.start,
page_length: this.page_length
}
}

// append user-defined arguments
if (this.args)
$.extend(args, this.args)

if (this.get_args) {
$.extend(args, this.get_args());
}
return args;
},
render_results: function (r) {
if (this.start === 0)
this.clear();

this.wrapper.find('.btn-more, .list-loading').hide();

var values = [];

if (r.message) {
values = this.get_values_from_response(r.message);
}

if (values.length || !this.show_no_result()) {
this.data = this.data.concat(values);
this.render_view(values);
this.update_paging(values);
} else if (this.start === 0) {
// show no result message
this.wrapper.find('.result').hide();

var msg = '';
var no_result_message = this.no_result_message;
if(no_result_message && $.isFunction(no_result_message)) {
msg = no_result_message();
} else if(typeof no_result_message === 'string') {
msg = no_result_message;
} else {
msg = __('No Results')
}

this.wrapper.find('.no-result').html(msg).show();
}

this.wrapper.find('.list-paging-area')
.toggle(values.length || this.start > 0);

// callbacks
if (this.onrun) this.onrun();
if (this.callback) this.callback(r);
this.wrapper.trigger("render-complete");
},

get_values_from_response: function (data) {
// make dictionaries from keys and values
if (data.keys && $.isArray(data.keys)) {
return frappe.utils.dict(data.keys, data.values);
} else {
return data;
}
},

render_view: function (values) {
// override this method in derived class
},

update_paging: function (values) {
if (values.length >= this.page_length) {
this.wrapper.find('.btn-more').show();
this.start += this.page_length;
}
},
refresh: function () {
this.run();
},
add_limits: function (query) {
return query + ' LIMIT ' + this.start + ',' + (this.page_length + 1);
},
set_filter: function (fieldname, label, no_run, no_duplicate) {
var filter = this.filter_list.get_filter(fieldname);
if (filter) {
var value = cstr(filter.field.get_parsed_value());
if (value.includes(label)) {
// already set
return false

} else if (no_duplicate) {
filter.set_values(this.doctype, fieldname, "=", label);
} else {
// second filter set for this field
if (fieldname == '_user_tags' || fieldname == "_liked_by") {
// and for tags
this.filter_list.add_filter(this.doctype, fieldname, 'like', '%' + label + '%');
} else {
// or for rest using "in"
filter.set_values(this.doctype, fieldname, 'in', value + ', ' + label);
}
}
} else {
// no filter for this item,
// setup one
if (['_user_tags', '_comments', '_assign', '_liked_by'].includes(fieldname)) {
this.filter_list.add_filter(this.doctype, fieldname, 'like', '%' + label + '%');
} else {
this.filter_list.add_filter(this.doctype, fieldname, '=', label);
}
}
if (!no_run)
this.run();
},
init_user_settings: function () {
this.user_settings = frappe.model.user_settings[this.doctype] || {};
},
call_for_selected_items: function (method, args) {
var me = this;
args.names = this.get_checked_items().map(function (item) {
return item.name;
});

frappe.call({
method: method,
args: args,
freeze: true,
callback: function (r) {
if (!r.exc) {
if (me.list_header) {
me.list_header.find(".list-select-all").prop("checked", false);
}
me.refresh();
}
}
});
}
});

+ 29
- 17
frappe/public/js/frappe/ui/filters/filters.js Voir le fichier

@@ -5,7 +5,7 @@ frappe.ui.FilterList = Class.extend({
init: function(opts) {
$.extend(this, opts);
this.filters = [];
this.wrapper = this.$parent;
this.wrapper = this.parent;
this.stats = [];
this.make();
this.set_events();
@@ -13,6 +13,20 @@ frappe.ui.FilterList = Class.extend({
make: function() {
var me = this;

this.wrapper.find('.show_filters').remove();
this.wrapper.append(`
<div class="show_filters">
<div class="set-filters">
<button
class="btn btn-default btn-xs show-filters text-muted"
style="margin-right: 10px;">
${__("Show Filters")}
</button>
<button style="margin-left: -5px;"
class="btn btn-default btn-xs new-filter text-muted">
<i class="octicon octicon-plus"></i></button>
</div>
</div>`);
$(frappe.render_template("filter_dashboard", {})).appendTo(this.wrapper.find('.show_filters'));

//show filter dashboard
@@ -85,7 +99,7 @@ frappe.ui.FilterList = Class.extend({
args: {
stats: me.stats,
doctype: me.doctype,
filters:me.default_filters
filters: me.default_filters
},
callback: function(r) {
// This gives a predictable stats order
@@ -163,9 +177,9 @@ frappe.ui.FilterList = Class.extend({
var noduplicate = true
}
if (label=="No Data"){
me.listobj.set_filter(fieldname, '', false, noduplicate);
me.base_list.set_filter(fieldname, '', false, noduplicate);
}else{
me.listobj.set_filter(fieldname, label, false, noduplicate);
me.base_list.set_filter(fieldname, label, false, noduplicate);
}
return false;
})
@@ -205,9 +219,9 @@ frappe.ui.FilterList = Class.extend({
var noduplicate = true
}
if (item.label == "No Data") {
me.listobj.set_filter(item.value, '', false, noduplicate);
me.base_list.set_filter(item.value, '', false, noduplicate);
} else {
me.listobj.set_filter(item.value, item.label, false, noduplicate);
me.base_list.set_filter(item.value, item.label, false, noduplicate);
}
}
});
@@ -222,8 +236,8 @@ frappe.ui.FilterList = Class.extend({

this.wrapper.find('.clear-filters').bind('click', function() {
me.clear_filters();
$('.date-range-picker').val('');
me.listobj.run();
$('.date-range-picker').val('')
me.base_list.run();
$(this).addClass("hide");
});

@@ -247,8 +261,7 @@ frappe.ui.FilterList = Class.extend({
df: {
fieldtype: "Check",
fieldname: "is_date_range",
label: __("Date Range"),
input_css: { "margin-top": "-2px" }
label: __("Date Range")
}
});
check.change = function() {
@@ -283,11 +296,11 @@ frappe.ui.FilterList = Class.extend({
filt && filt.remove(true);
if(!dateObj.length && dateObj && date.datepicker.opts.range===false) {
me.add_filter(me.doctype, name, '=', moment(dateObj).format('YYYY-MM-DD'));
me.listobj.run();
me.base_list.run();
} else if(dateObj.length===2 && date.datepicker.opts.range===true) {
me.add_filter(me.doctype, name, 'Between',
[moment(dateObj[0]).format('YYYY-MM-DD'), moment(dateObj[1]).format('YYYY-MM-DD')]);
me.listobj.run();
me.base_list.run();
}
});
}
@@ -444,7 +457,7 @@ frappe.ui.Filter = Class.extend({

this.wrapper.find(".set-filter-and-run").on("click", function() {
me.wrapper.removeClass("is-new-filter");
me.flist.listobj.run();
me.flist.base_list.run();
});

// add help for "in" codition
@@ -482,9 +495,8 @@ frappe.ui.Filter = Class.extend({
this.flist.update_filters();

if(!dont_run) {
this.flist.listobj.dirty = true;
this.flist.listobj.clean_dash = true;
this.flist.listobj.refresh();
this.flist.base_list.clean_dash = true;
this.flist.base_list.refresh(true);
}
},

@@ -559,7 +571,7 @@ frappe.ui.Filter = Class.extend({
// run on enter
$(me.field.wrapper).find(':input').keydown(function(ev) {
if(ev.which==13) {
me.flist.listobj.run();
me.flist.base_list.run();
}
})
},


+ 0
- 13
frappe/public/js/frappe/ui/listing.html Voir le fichier

@@ -1,18 +1,5 @@
<div class="frappe-list">
<div class="list-filters" style="display: none;">
<div class="show_filters">
<div class="set-filters">
<button class="btn btn-default btn-xs show-filters text-muted"
style="margin-right: 10px;">
{{ __("Show Filters") }}</button>
<button style="margin-left: -5px;"
class="btn btn-default btn-xs new-filter text-muted">
<i class="octicon octicon-plus"></i></button>
<button class="btn btn-default btn-xs clear-filters text-muted hide"
style="margin-left: 5px;">
{{ __("Clear Filters") }}</button>
</div>
</div>
</div>

<div style="margin-bottom:9px" class="list-toolbar-wrapper hide">


+ 23
- 35
frappe/public/js/frappe/ui/listing.js Voir le fichier

@@ -224,6 +224,11 @@ frappe.ui.Listing = Class.extend({
var args = this.get_call_args();
this.save_list_settings_locally(args);

// list_settings are saved by db_query.py when dirty
$.extend(args, {
list_settings: frappe.model.list_settings[this.doctype]
});

return frappe.call({
method: this.opts.method || 'frappe.desk.query_builder.runquery',
type: "GET",
@@ -251,7 +256,7 @@ frappe.ui.Listing = Class.extend({

if(!frappe.utils.arrays_equal(args.filters, list_settings.filters)) {
//dont save filters in Kanban view
if(!frappe.get_route()[2]==="Kanban") {
if(this.current_view!=="Kanban") {
// settings are dirty if filters change
list_settings.filters = args.filters || [];
different = true;
@@ -271,7 +276,7 @@ frappe.ui.Listing = Class.extend({
// save fields in list settings
if(args.save_list_settings_fields) {
list_settings.fields = args.fields;
};
}

if(different) {
list_settings.updated_on = moment().toString();
@@ -318,9 +323,10 @@ frappe.ui.Listing = Class.extend({
r.values = this.get_values_from_response(r.message);
}

if(r.values.length) {
if(r.values.length || this.force_render_view) {
this.data = this.data.concat(r.values);
this.render_list(r.values);
this.render_view(r.values);
// this.render_list(r.values);
this.update_paging(r.values);
} else {
if(this.start===0) {
@@ -355,11 +361,20 @@ frappe.ui.Listing = Class.extend({
}
},

render_view: function(values) {
this.list_view = new frappe.views.ListView({
doctype: this.doctype,
values: values,
});
},

render_list: function(values) {
this.last_page = values;
if(this.filter_list) {
this.filter_values = this.filter_list.get_filters();
}
// TODO: where is this used?
// this.last_page = values;
// if(this.filter_list) {
// // and this?
// this.filter_values = this.filter_list.get_filters();
// }

this.render_rows(values);
},
@@ -370,33 +385,6 @@ frappe.ui.Listing = Class.extend({
this.render_row(this.add_row(values[i]), values[i], this, i);
}
},
render_image_gallery: function(){
var me = this;
frappe.require(
[
"assets/frappe/js/frappe/list/imageview.js",
"assets/frappe/js/lib/gallery/js/blueimp-gallery.js",
"assets/frappe/js/lib/gallery/css/blueimp-gallery.css",
"assets/frappe/js/lib/gallery/js/blueimp-gallery-indicator.js",
"assets/frappe/js/lib/gallery/css/blueimp-gallery-indicator.css"
], function(){
// remove previous gallery container
me.wrapper.find(".blueimp-gallery").remove();
// append gallery div
var gallery = frappe.render_template("blueimp-gallery", {});
$(gallery).appendTo(me.wrapper);

me.wrapper.find(".zoom-view").click(function(event){
event.preventDefault();
opts = {
doctype: me.doctype,
docname: $(this).parent().attr('data-name'),
container: me.wrapper
};
new frappe.views.ImageView(opts);
});
});
},
update_paging: function(values) {
if(values.length >= this.page_length) {
this.wrapper.find('.btn-more').toggle(true);


+ 0
- 1
frappe/public/js/frappe/ui/page.js Voir le fichier

@@ -80,7 +80,6 @@ frappe.ui.Page = Class.extend({

this.page_actions = this.wrapper.find(".page-actions");

this.checked_items_status = this.page_actions.find(".checked-items-status");
this.btn_primary = this.page_actions.find(".primary-action");
this.btn_secondary = this.page_actions.find(".btn-secondary");



+ 23
- 7
frappe/public/js/frappe/ui/sort_selector.js Voir le fichier

@@ -14,6 +14,7 @@ frappe.ui.SortSelector = Class.extend({
},
make: function() {
this.prepare_args();
this.parent.find('.sort-selector').remove();
this.wrapper = $(frappe.render_template('sort_selector', this.args)).appendTo(this.parent);
this.bind_events();
},
@@ -46,6 +47,26 @@ frappe.ui.SortSelector = Class.extend({
if(!this.args) {
this.args = {};
}

// args as string
if(this.args && typeof this.args === 'string') {
var order_by = this.args;
this.args = {}

if (order_by.includes('`.`')) {
// scrub table name (separated by dot), like `tabTime Log`.`modified` desc`
order_by = order_by.split('.')[1];
}

var parts = order_by.split(' ');
if (parts.length === 2) {
var fieldname = strip(parts[0], '`');

this.args.sort_by = fieldname;
this.args.sort_order = parts[1];
}
}

if(this.args.options) {
this.args.options.forEach(function(o) {
me.labels[o.fieldname] = o.label;
@@ -114,13 +135,8 @@ frappe.ui.SortSelector = Class.extend({
_options.push({'fieldname': 'idx'});

// de-duplicate
var added = [];
this.args.options = [];
_options.forEach(function(o) {
if(added.indexOf(o.fieldname)===-1) {
me.args.options.push(o);
added.push(o.fieldname);
}
this.args.options = _options.uniqBy(function(obj) {
return obj.fieldname;
});

// add missing labels


+ 1
- 1
frappe/public/js/frappe/ui/toolbar/awesome_bar.js Voir le fichier

@@ -350,7 +350,7 @@ frappe.search.AwesomeBar = Class.extend({
var route = frappe.get_route();
if(route[0]==="List" && txt.indexOf(" in") === -1) {
// search in title field
var meta = frappe.get_meta(frappe.container.page.doclistview.doctype);
var meta = frappe.get_meta(frappe.container.page.list_view.doctype);
var search_field = meta.title_field || "name";
var options = {};
options[search_field] = ["like", "%" + txt + "%"];


+ 1
- 1
frappe/public/js/frappe/ui/toolbar/notifications.js Voir le fichier

@@ -109,7 +109,7 @@ frappe.views.show_open_count_list = function(element) {

var route = frappe.get_route();
if(route[0]==="List" && route[1]===doctype) {
frappe.pages["List/" + doctype].doclistview.refresh();
frappe.pages["List/" + doctype].list_view.refresh();
} else {
frappe.set_route("List", doctype);
}


frappe/public/js/frappe/views/calendar.js → frappe/public/js/frappe/views/calendar/calendar.js Voir le fichier

@@ -4,6 +4,35 @@
frappe.provide("frappe.views.calendar");
frappe.provide("frappe.views.calendars");

frappe.views.CalendarView = frappe.views.ListRenderer.extend({
name: 'Calendar',
render_view: function() {
var me = this;
var options = {
doctype: this.doctype,
parent: this.wrapper,
page: this.list_view.page,
filter_vals: this.list_view.filter_list.get_filters()
}
$.extend(options, frappe.views.calendar[this.doctype]);
this.calendar = new frappe.views.Calendar(options);
},
set_defaults: function() {
this._super();
this.page_title = this.page_title + ' ' + __('Calendar');
this.no_realtime = true;
this.show_no_result = false;
this.hide_sort_selector = true;
},
get_header_html: function() {
return null;
},
required_libs: [
'assets/frappe/js/lib/fullcalendar/fullcalendar.min.css',
'assets/frappe/js/lib/fullcalendar/fullcalendar.min.js'
]
})

frappe.views.Calendar = Class.extend({
init: function(options) {
$.extend(this, options);
@@ -14,20 +43,6 @@ frappe.views.Calendar = Class.extend({
make_page: function() {
var me = this;

$(this.parent).on("show", function() {
me.set_filters_from_route_options();
});

var module = locals.DocType[this.doctype].module;
this.page.set_title(__("Calendar") + " - " + __(this.doctype));

frappe.breadcrumbs.add(module, this.doctype);

this.page.set_primary_action(__("New"), function() {
var doc = frappe.model.get_new_doc(me.doctype);
frappe.set_route("Form", me.doctype, doc.name);
});

// add links to other calendars
$.each(frappe.boot.calendars, function(i, doctype) {
if(frappe.model.can_read(doctype)) {
@@ -46,9 +61,9 @@ frappe.views.Calendar = Class.extend({
var me = this;
this.$wrapper = this.parent;
this.$cal = $("<div>").appendTo(this.$wrapper);
footnote = frappe.utils.set_footnote(this, this.$wrapper,
this.footnote_area = frappe.utils.set_footnote(this.footnote_area, this.$wrapper,
__("Select or drag across time slots to create a new event."));
footnote.css({"border-top": "0px"});
this.footnote_area.css({"border-top": "0px"});

this.$cal.fullCalendar(this.cal_options);
this.set_css();

+ 4
- 4
frappe/public/js/frappe/views/container.js Voir le fichier

@@ -12,7 +12,7 @@ frappe.views.Container = Class.extend({
init: function() {
this.container = $('#body_div').get(0);
this.page = null; // current page
this.pagewidth = $('#body_div').width();
this.pagewidth = $(this.container).width();
this.pagemargin = 50;

var me = this;
@@ -36,7 +36,7 @@ frappe.views.Container = Class.extend({
var page = $('<div class="content page-container"></div>')
.attr('id', "page-" + label)
.attr("data-page-route", label)
.toggle(false)
.hide()
.appendTo(this.container).get(0);
page.label = label;
frappe.pages[label] = page;
@@ -69,7 +69,7 @@ frappe.views.Container = Class.extend({

// hide current
if(this.page && this.page != page) {
$(this.page).toggle(false);
$(this.page).hide();
$(this.page).trigger('hide');
}

@@ -77,7 +77,7 @@ frappe.views.Container = Class.extend({
if(!this.page || this.page != page) {
this.page = page;
// $(this.page).fadeIn(300);
$(this.page).toggle(true);
$(this.page).show();
}

$(document).trigger("page-change");


+ 1
- 1
frappe/public/js/frappe/views/factory.js Voir le fichier

@@ -11,7 +11,7 @@ frappe.views.Factory = Class.extend({
show: function() {
var page_name = frappe.get_route_str(),
me = this;
if(frappe.pages[page_name] && page_name.indexOf("Form/")===-1) {
if(frappe.pages[page_name] && !page_name.includes("Form/")) {
frappe.container.change_to(frappe.pages[page_name]);
if(me.on_show) {
me.on_show();


+ 201
- 0
frappe/public/js/frappe/views/gantt/gantt_view.js Voir le fichier

@@ -0,0 +1,201 @@
frappe.provide('frappe.views');

frappe.views.GanttView = frappe.views.ListRenderer.extend({
name: 'Gantt',
prepare: function(values) {
this.items = values;
this.prepare_tasks();
this.prepare_dom();
},

render_view: function(values) {
var me = this;
this.prepare(values);
this.render_gantt();
},

set_defaults: function() {
this._super();
this.no_realtime = true;
this.page_title = this.page_title + ' ' + __('Gantt');
},

init_settings: function() {
this._super();
this.field_map = frappe.views.calendar[this.doctype].field_map;
this.order_by = this.order_by || this.field_map.start + ' asc';
},

prepare_dom: function() {
this.wrapper.css('overflow', 'auto')
.append('<svg class="gantt-container" width="20" height="20"></svg>')
},

render_gantt: function(tasks) {
var me = this;
this.gantt_view_mode = this.user_settings.gantt_view_mode || 'Day';
var field_map = frappe.views.calendar[this.doctype].field_map;

this.gantt = new Gantt(".gantt-container", this.tasks, {
view_mode: this.gantt_view_mode,
date_format: "YYYY-MM-DD",
on_click: function (task) {
frappe.set_route('Form', task.doctype, task.id);
},
on_date_change: function(task, start, end) {
if(!me.can_write()) return;
me.update_gantt_task(task, start, end);
},
on_progress_change: function(task, progress) {
if(!me.can_write()) return;
var progress_fieldname = 'progress';

if($.isFunction(field_map.progress)) {
progress_fieldname = null;
} else if(field_map.progress) {
progress_fieldname = field_map.progress;
}

if(progress_fieldname) {
frappe.db.set_value(task.doctype, task.id,
progress_fieldname, parseInt(progress));
}
},
on_view_change: function(mode) {
// save view mode
frappe.model.user_settings.save(me.doctype, 'Gantt', {
gantt_view_mode: mode
});
},
custom_popup_html: function(task) {
var item = me.get_item(task.id);
var list_item_subject = frappe.render_template('list_item_subject', item);
var html = '<div class="heading">'+
list_item_subject +'</div>';

// custom html in {doctype}_list.js
var custom = me.settings.gantt_custom_popup_html;
if(custom) {
html = custom(item, html);
}

return '<div class="details-container">'+ html +'</div>';
}
});
this.render_dropdown();
},

render_dropdown: function() {
var me = this;
var view_modes = this.gantt.config.view_modes || [];
var dropdown = "<div class='dropdown pull-right'>" +
"<a class='text-muted dropdown-toggle' data-toggle='dropdown'>" +
"<span class='dropdown-text'>"+__(this.gantt_view_mode)+"</span><i class='caret'></i></a>" +
"<ul class='dropdown-menu'></ul>" +
"</div>";

// view modes (for translation) __("Day"), __("Week"), __("Month"),
//__("Half Day"), __("Quarter Day")

var dropdown_list = "";
view_modes.forEach(function(view_mode) {
dropdown_list += "<li>" +
"<a class='option' data-value='" + view_mode + "'>" +
__(view_mode) + "</a></li>";
});
var $dropdown = $(dropdown)
$dropdown.find(".dropdown-menu")
.append(dropdown_list);
me.list_view.$page.find(`.list-row-head[data-list-renderer='Gantt'] .list-row-right`).css("margin-top", 0).html($dropdown)
$dropdown.on("click", ".option", function() {
var mode = $(this).data('value');
me.gantt.change_view_mode(mode);
$dropdown.find(".dropdown-text").text(mode);
});
},

prepare_tasks: function() {
var me = this;
var meta = frappe.get_meta(this.doctype);
var field_map = frappe.views.calendar[this.doctype].field_map;
this.tasks = this.items.map(function(item) {
// set progress
var progress = 0;
if(field_map.progress && $.isFunction(field_map.progress)) {
progress = field_map.progress(item);
} else if(field_map.progress) {
progress = item[field_map.progress]
}

// title
if(meta.title_field) {
var label = $.format("{0} ({1})", [item[meta.title_field], item.name]);
} else {
var label = item[field_map.title];
}

return {
start: item[field_map.start],
end: item[field_map.end],
name: label,
id: item[field_map.id || 'name'],
doctype: me.doctype,
progress: progress,
dependencies: item.depends_on_tasks || ""
};
});
},
get_item: function(name) {
return this.items.find(function(item) {
return item.name === name;
});
},
update_gantt_task: function(task, start, end) {
var me = this;
if(me.gantt.updating_task) {
setTimeout(me.update_gantt_task.bind(me, task, start, end), 200)
return;
}
me.gantt.updating_task = true;

var field_map = frappe.views.calendar[this.doctype].field_map;
frappe.call({
method: 'frappe.desk.gantt.update_task',
args: {
args: {
doctype: task.doctype,
name: task.id,
start: start.format('YYYY-MM-DD'),
end: end.format('YYYY-MM-DD')
},
field_map: field_map
},
callback: function() {
me.gantt.updating_task = false;
show_alert({message:__("Saved"), indicator: 'green'}, 1);
}
});
},
get_header_html: function() {
return frappe.render_template('list_item_row_head', { main: '', list: this });
},
refresh: function(values) {
this.prepare(values);
this.render();
},
can_write: function() {
if(frappe.model.can_write(this.doctype)) {
return true;
} else {
// reset gantt state
this.gantt.change_view_mode(this.gantt_view_mode);
show_alert({message: __("Not permitted"), indicator: 'red'}, 1);
return false;
}
},
set_columns: function() {},
required_libs: [
"assets/frappe/js/lib/snap.svg-min.js",
"assets/frappe/js/lib/frappe-gantt/frappe-gantt.js"
]
});

+ 180
- 0
frappe/public/js/frappe/views/image/image_view.js Voir le fichier

@@ -0,0 +1,180 @@
/**
* frappe.views.ImageView
*/
frappe.provide("frappe.views");

frappe.views.ImageView = frappe.views.ListRenderer.extend({
name: 'Image',
render_view: function (values) {
this.items = values;
this.render_image_view();
this.setup_gallery();
},
set_defaults: function() {
this._super();
this.page_title = this.page_title + ' ' + __('Images');
},
render_image_view: function () {
var html = this.items.map(this.render_item.bind(this)).join("");
this.container = $('<div>')
.addClass('image-view-container')
.appendTo(this.wrapper);
this.container.append(html);
},
render_item: function (item) {
var image_url = this.get_image_url(item);
var indicator = this.get_indicator_html(item);
return frappe.render_template("image_view_item_row", {
data: item,
indicator: indicator,
additional_columns: this.additional_columns,
item_image: image_url,
color: frappe.get_palette(item.item_name)
});
},
get_image_url: function (item) {
var url;
url = item.image ? item.image : item[this.meta.image_field];

// absolute url for mobile
if (window.cordova && !frappe.utils.is_url(url)) {
url = frappe.base_url + url;
}
if (url) {
return url
// return "url('" + url + "')";
}
return null;
},
get_header_html: function () {
var main = frappe.render_template('image_view_item_main_head', {
columns: this.columns,
right_column: this.settings.right_column,
_checkbox: ((frappe.model.can_delete(this.doctype) || this.settings.selectable)
&& !this.no_delete)
});
return frappe.render_template('list_item_row_head', { main: main, list: this });
},
setup_gallery: function() {
var me = this;
var gallery = new frappe.views.GalleryView({
doctype: this.doctype,
items: this.items,
wrapper: this.container
});
this.container.on('click', '.btn.zoom-view', function(e) {
e.preventDefault();
e.stopPropagation();
var name = $(this).data().name;
gallery.show(name);
return false;
});
},
refresh: this.render_view
});

frappe.views.GalleryView = Class.extend({
init: function(opts) {
$.extend(this, opts);
var me = this;

this.ready = false;
this.load_lib(function() {
me.prepare();
me.ready = true;
});
},
prepare: function() {
// keep only one pswp dom element
this.pswp_root = $('body > .pswp');
if(this.pswp_root.length === 0) {
var pswp = frappe.render_template('photoswipe_dom');
this.pswp_root = $(pswp).appendTo('body');
}
},
show: function(docname) {
var me = this;
if(!this.ready) {
setTimeout(this.show.bind(this), 200);
return;
}
var items = this.items.map(function(i) {
var query = 'img[data-name="'+i.name+'"]';
var el = me.wrapper.find(query).get(0);
return {
src: i.image,
msrc: i.image,
name: i.name,
w: el.naturalWidth,
h: el.naturalHeight,
el: el
}
});

var index;
items.map(function(item, i) {
if(item.name === docname)
index = i;
});

var options = {
index: index,
getThumbBoundsFn: function(index) {
var thumbnail = items[index].el, // find thumbnail
pageYScroll = window.pageYOffset || document.documentElement.scrollTop,
rect = thumbnail.getBoundingClientRect();

return {x:rect.left, y:rect.top + pageYScroll, w:rect.width};
},
history: false,
shareEl: false,
}
var pswp = new PhotoSwipe(
this.pswp_root.get(0),
PhotoSwipeUI_Default,
items,
options
);
pswp.init();
},
get_image_urls: function() {
// not implemented yet
return frappe.call({
method: "frappe.client.get_list",
args: {
doctype: "File",
order_by: "attached_to_name",
fields: [
"'image/*' as type", "ifnull(thumbnail_url, file_url) as thumbnail",
"concat(attached_to_name, ' - ', file_name) as title", "file_url as src",
"attached_to_name as name"
],
filters: [
["File", "attached_to_doctype", "=", this.doctype],
["File", "attached_to_name", "in", this.docnames],
["File", "is_folder", "!=", 1]
]
},
freeze: true,
freeze_message: "Fetching Images.."
}).then(function(r) {
if (!r.message) {
msgprint("No Images found")
} else {
// filter image files from other
var images = r.message.filter(function(image) {
return frappe.utils.is_image_file(image.title || image.href);
});
}
});
},
load_lib: function(callback) {
var asset_dir = 'assets/frappe/js/lib/photoswipe/';
frappe.require([
asset_dir + 'photoswipe.css',
asset_dir + 'default-skin.css',
asset_dir + 'photoswipe.js',
asset_dir + 'photoswipe-ui-default.js'
], callback);
}
});

frappe/public/js/frappe/list/image_view_item_main_head.html → frappe/public/js/frappe/views/image/image_view_item_main_head.html Voir le fichier

@@ -1,5 +1,7 @@
<div class="row">
<div class="col-xs-12">
<div class="list-value">
{%= frappe.render_template("header_select_all_like_filter", { _checkbox: _checkbox }) %}
</div>
</div>
</div>

+ 44
- 0
frappe/public/js/frappe/views/image/image_view_item_row.html Voir le fichier

@@ -0,0 +1,44 @@
<div class="image-view-item has-checkbox ellipsis">
<div class="image-view-header doclist-row">
<div class="list-value">
{{ frappe.render_template("list_item_subject", data) }}
</div>
</div>
<!-- Image -->
<div class="image-view-body">
<a data-name="{{ data.name }}"
title="{{ data.name }}"
href="#Form/{{ data.doctype }}/{{ data.name }}"
>
<div class="image-field"
data-name="{{ data.name }}"
style="
{% if (!item_image) { %}
background-color: {{ color }};
{% } %}
border: 0px;"
>
{% if (!item_image) { %}
<span class="placeholder-text">
{%= frappe.get_abbr(data._title) %}
</span>
{% } %}
{% if (item_image) { %}
<img data-name="{{ data.name }}" src="{{ item_image }}" alt="{{data.title}}">
{% } %}
<button class="btn btn-default zoom-view" data-name="{{data.name}}">
<i class="fa fa-search-plus"></i>
</button>
</div>
</a>
</div>
<div class="image-view-footer hide">
<div class="row">
<div class="col-xs-4">{%= indicator %}</div>
<div class="col-xs-8 text-right">
<!-- comments count and assigned to section -->
{%= frappe.render_template("item_assigned_to_comment_count", { data: data }) %}
</div>
</div>
</div>
</div>

+ 69
- 0
frappe/public/js/frappe/views/image/photoswipe_dom.html Voir le fichier

@@ -0,0 +1,69 @@


<!-- http://photoswipe.com/documentation/getting-started.html -->
<!-- Root element of PhotoSwipe. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

<!-- Background of PhotoSwipe.
It's a separate element as animating opacity is faster than rgba(). -->
<div class="pswp__bg"></div>

<!-- Slides wrapper with overflow:hidden. -->
<div class="pswp__scroll-wrap">

<!-- Container that holds slides.
PhotoSwipe keeps only 3 of them in the DOM to save memory.
Don't modify these 3 pswp__item elements, data is added later on. -->
<div class="pswp__container">
<div class="pswp__item"></div>
<div class="pswp__item"></div>
<div class="pswp__item"></div>
</div>

<!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
<div class="pswp__ui pswp__ui--hidden">

<div class="pswp__top-bar">

<!-- Controls are self-explanatory. Order can be changed. -->

<div class="pswp__counter"></div>

<button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

<button class="pswp__button pswp__button--share" title="Share"></button>

<button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

<button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

<!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
<!-- element will get class pswp__preloader--active when preloader is running -->
<div class="pswp__preloader">
<div class="pswp__preloader__icn">
<div class="pswp__preloader__cut">
<div class="pswp__preloader__donut"></div>
</div>
</div>
</div>
</div>

<div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
<div class="pswp__share-tooltip"></div>
</div>

<button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
</button>

<button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
</button>

<div class="pswp__caption">
<div class="pswp__caption__center"></div>
</div>

</div>

</div>

</div>

+ 1048
- 0
frappe/public/js/frappe/views/kanban/kanban_board.js
Fichier diff supprimé car celui-ci est trop grand
Voir le fichier


+ 68
- 1032
frappe/public/js/frappe/views/kanban/kanban_view.js
Fichier diff supprimé car celui-ci est trop grand
Voir le fichier


+ 14
- 14
frappe/public/js/frappe/views/reports/reportview.js Voir le fichier

@@ -62,7 +62,7 @@ frappe.views.ReportViewPage = Class.extend({
}
});

frappe.views.ReportView = frappe.ui.Listing.extend({
frappe.views.ReportView = frappe.ui.BaseList.extend({
init: function(opts) {
var me = this;
$.extend(this, opts);
@@ -79,11 +79,11 @@ frappe.views.ReportView = frappe.ui.Listing.extend({
this._body = $('<div>').appendTo(this.page.main);
this.page_title = __('Report')+ ': ' + __(this.docname ? (this.doctype + ' - ' + this.docname) : this.doctype);
this.page.set_title(this.page_title);
this.init_list_settings();
this.init_user_settings();
this.make({
page: this.parent.page,
method: 'frappe.desk.reportview.get',
save_list_settings: true,
save_user_settings: true,
get_args: this.get_args,
parent: this._body,
start: 0,
@@ -139,8 +139,8 @@ frappe.views.ReportView = frappe.ui.Listing.extend({
// pre-select mandatory columns
var me = this;
var columns = [];
if(this.list_settings.fields && !this.docname) {
this.list_settings.fields.forEach(function(field) {
if(this.user_settings.fields && !this.docname) {
this.user_settings.fields.forEach(function(field) {
var coldef = me.get_column_info_from_field(field);
if(!in_list(['_seen', '_comments', '_user_tags', '_assign', '_liked_by', 'docstatus'], coldef[0])) {
columns.push(coldef);
@@ -226,21 +226,21 @@ frappe.views.ReportView = frappe.ui.Listing.extend({

set_route_filters: function(first_load) {
var me = this;
if(frappe.route_options && !this.list_settings.filters) {
if(frappe.route_options && !this.user_settings.filters) {
this.set_filters_from_route_options();
return true;
} else if(this.list_settings
&& this.list_settings.filters
} else if(this.user_settings
&& this.user_settings.filters
&& !this.docname
&& (this.list_settings.updated_on != this.list_settings_updated_on)) {
&& (this.user_settings.updated_on != this.user_settings_updated_on)) {
// list settings (previous settings)
this.filter_list.clear_filters();
$.each(this.list_settings.filters, function(i, f) {
$.each(this.user_settings.filters, function(i, f) {
me.filter_list.add_filter(f[0], f[1], f[2], f[3]);
});
return true;
}
this.list_settings_updated_on = this.list_settings.updated_on;
this.user_settings_updated_on = this.user_settings.updated_on;
},

setup_print: function() {
@@ -263,7 +263,7 @@ frappe.views.ReportView = frappe.ui.Listing.extend({
order_by: this.get_order_by(),
add_total_row: this.add_total_row,
filters: this.filter_list.get_filters(),
save_list_settings_fields: 1,
save_user_settings_fields: 1,
with_childnames: 1,
file_format_type: this.file_format_type
}
@@ -378,7 +378,7 @@ frappe.views.ReportView = frappe.ui.Listing.extend({
},

// render data
render_list: function() {
render_view: function() {
var me = this;
var data = this.get_unique_data(this.column_info);

@@ -598,7 +598,7 @@ frappe.views.ReportView = frappe.ui.Listing.extend({

this.page.add_inner_button(__('Show Totals'), function() {
me.add_totals_row = 1 - (me.add_totals_row ? me.add_totals_row : 0);
me.render_list();
me.render_view();
});
},



+ 1
- 1
frappe/public/js/legacy/form.js Voir le fichier

@@ -887,7 +887,7 @@ _f.Frm.prototype.set_intro = function(txt, append) {
}

_f.Frm.prototype.set_footnote = function(txt) {
frappe.utils.set_footnote(this, this.body, txt);
this.footnote_area = frappe.utils.set_footnote(this.footnote_area, this.body, txt);
}




+ 59
- 21
frappe/public/js/lib/frappe-gantt/frappe-gantt.js
Fichier diff supprimé car celui-ci est trop grand
Voir le fichier


+ 0
- 72
frappe/public/js/lib/gallery/css/blueimp-gallery-indicator.css Voir le fichier

@@ -1,72 +0,0 @@
@charset "UTF-8";
/*
* blueimp Gallery Indicator CSS
* https://github.com/blueimp/Gallery
*
* Copyright 2013, Sebastian Tschan
* https://blueimp.net
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/MIT
*/

.blueimp-gallery > .indicator {
position: absolute;
top: auto;
right: 15px;
bottom: 15px;
left: 15px;
margin: 0 40px;
padding: 0;
list-style: none;
text-align: center;
line-height: 10px;
display: none;
}
.blueimp-gallery > .indicator > li {
display: inline-block;
width: 40px;
height: 40px;
margin: 6px 3px 0 3px;
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
box-sizing: content-box;
border: 1px solid transparent;
background: #ccc;
background: rgba(255, 255, 255, 0.25) center no-repeat;
border-radius: 5px;
box-shadow: 0 0 2px #000;
opacity: 0.5;
cursor: pointer;
background-size: 40px 40px;
}
.blueimp-gallery > .indicator > li:hover,
.blueimp-gallery > .indicator > .active {
background-color: #fff;
border-color: #fff;
opacity: 1;
}
.blueimp-gallery-controls > .indicator {
display: block;
/* Fix z-index issues (controls behind slide element) on Android: */
-webkit-transform: translateZ(0);
-moz-transform: translateZ(0);
-ms-transform: translateZ(0);
-o-transform: translateZ(0);
transform: translateZ(0);
}
.blueimp-gallery-single > .indicator {
display: none;
}
.blueimp-gallery > .indicator {
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}

/* IE7 fixes */
*+html .blueimp-gallery > .indicator > li {
display: inline;
}

+ 0
- 226
frappe/public/js/lib/gallery/css/blueimp-gallery.css Voir le fichier

@@ -1,226 +0,0 @@
@charset "UTF-8";
/*
* blueimp Gallery CSS
* https://github.com/blueimp/Gallery
*
* Copyright 2013, Sebastian Tschan
* https://blueimp.net
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/MIT
*/

.blueimp-gallery,
.blueimp-gallery > .slides > .slide > .slide-content {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
/* Prevent artifacts in Mozilla Firefox: */
-moz-backface-visibility: hidden;
}
.blueimp-gallery > .slides > .slide > .slide-content {
margin: auto;
width: auto;
height: auto;
max-width: 100%;
max-height: 100%;
opacity: 1;
}
.blueimp-gallery {
position: fixed;
z-index: 999999;
overflow: hidden;
background: #000;
background: rgba(0, 0, 0, 0.9);
opacity: 0;
display: none;
direction: ltr;
-ms-touch-action: none;
touch-action: none;
}
.blueimp-gallery-carousel {
position: relative;
z-index: auto;
margin: 1em auto;
/* Set the carousel width/height ratio to 16/9: */
padding-bottom: 56.25%;
box-shadow: 0 0 10px #000;
-ms-touch-action: pan-y;
touch-action: pan-y;
}
.blueimp-gallery-display {
display: block;
opacity: 1;
}
.blueimp-gallery > .slides {
position: relative;
height: 100%;
overflow: hidden;
}
.blueimp-gallery-carousel > .slides {
position: absolute;
}
.blueimp-gallery > .slides > .slide {
position: relative;
float: left;
height: 100%;
text-align: center;
-webkit-transition-timing-function: cubic-bezier(0.645, 0.045, 0.355, 1.000);
-moz-transition-timing-function: cubic-bezier(0.645, 0.045, 0.355, 1.000);
-ms-transition-timing-function: cubic-bezier(0.645, 0.045, 0.355, 1.000);
-o-transition-timing-function: cubic-bezier(0.645, 0.045, 0.355, 1.000);
transition-timing-function: cubic-bezier(0.645, 0.045, 0.355, 1.000);
}
.blueimp-gallery,
.blueimp-gallery > .slides > .slide > .slide-content {
-webkit-transition: opacity 0.2s linear;
-moz-transition: opacity 0.2s linear;
-ms-transition: opacity 0.2s linear;
-o-transition: opacity 0.2s linear;
transition: opacity 0.2s linear;
}
.blueimp-gallery > .slides > .slide-loading {
background: url(../img/loading.gif) center no-repeat;
background-size: 64px 64px;
}
.blueimp-gallery > .slides > .slide-loading > .slide-content {
opacity: 0;
}
.blueimp-gallery > .slides > .slide-error {
background: url(../img/error.png) center no-repeat;
}
.blueimp-gallery > .slides > .slide-error > .slide-content {
display: none;
}
.blueimp-gallery > .prev,
.blueimp-gallery > .next {
position: absolute;
top: 50%;
left: 15px;
width: 40px;
height: 40px;
margin-top: -23px;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 60px;
font-weight: 100;
line-height: 30px;
color: #fff;
text-decoration: none;
text-shadow: 0 0 2px #000;
text-align: center;
background: #222;
background: rgba(0, 0, 0, 0.5);
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
box-sizing: content-box;
border: 3px solid #fff;
-webkit-border-radius: 23px;
-moz-border-radius: 23px;
border-radius: 23px;
opacity: 0.5;
cursor: pointer;
display: none;
}
.blueimp-gallery > .next {
left: auto;
right: 15px;
}
.blueimp-gallery > .close,
.blueimp-gallery > .title {
position: absolute;
top: 15px;
left: 15px;
margin: 0 40px 0 0;
font-size: 20px;
line-height: 30px;
color: #fff;
text-shadow: 0 0 2px #000;
opacity: 0.8;
display: none;
}
.blueimp-gallery > .close {
padding: 15px;
right: 15px;
left: auto;
margin: -15px;
font-size: 30px;
text-decoration: none;
cursor: pointer;
}
.blueimp-gallery > .play-pause {
position: absolute;
right: 15px;
bottom: 15px;
width: 15px;
height: 15px;
background: url(../img/play-pause.png) 0 0 no-repeat;
cursor: pointer;
opacity: 0.5;
display: none;
}
.blueimp-gallery-playing > .play-pause {
background-position: -15px 0;
}
.blueimp-gallery > .prev:hover,
.blueimp-gallery > .next:hover,
.blueimp-gallery > .close:hover,
.blueimp-gallery > .title:hover,
.blueimp-gallery > .play-pause:hover {
color: #fff;
opacity: 1;
}
.blueimp-gallery-controls > .prev,
.blueimp-gallery-controls > .next,
.blueimp-gallery-controls > .close,
.blueimp-gallery-controls > .title,
.blueimp-gallery-controls > .play-pause {
display: block;
/* Fix z-index issues (controls behind slide element) on Android: */
-webkit-transform: translateZ(0);
-moz-transform: translateZ(0);
-ms-transform: translateZ(0);
-o-transform: translateZ(0);
transform: translateZ(0);
}
.blueimp-gallery-single > .prev,
.blueimp-gallery-left > .prev,
.blueimp-gallery-single > .next,
.blueimp-gallery-right > .next,
.blueimp-gallery-single > .play-pause {
display: none;
}
.blueimp-gallery > .slides > .slide > .slide-content,
.blueimp-gallery > .prev,
.blueimp-gallery > .next,
.blueimp-gallery > .close,
.blueimp-gallery > .play-pause {
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}

/* Replace PNGs with SVGs for capable browsers (excluding IE<9) */
body:last-child .blueimp-gallery > .slides > .slide-error {
background-image: url(../img/error.svg);
}
body:last-child .blueimp-gallery > .play-pause {
width: 20px;
height: 20px;
background-size: 40px 20px;
background-image: url(../img/play-pause.svg);
}
body:last-child .blueimp-gallery-playing > .play-pause {
background-position: -20px 0;
}

/* IE7 fixes */
*+html .blueimp-gallery > .slides > .slide {
min-height: 300px;
}
*+html .blueimp-gallery > .slides > .slide > .slide-content {
position: relative;
}

+ 0
- 155
frappe/public/js/lib/gallery/js/blueimp-gallery-indicator.js Voir le fichier

@@ -1,155 +0,0 @@
/*
* blueimp Gallery Indicator JS
* https://github.com/blueimp/Gallery
*
* Copyright 2013, Sebastian Tschan
* https://blueimp.net
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/MIT
*/

/* global define, window, document */

;(function (factory) {
'use strict'
if (typeof define === 'function' && define.amd) {
// Register as an anonymous AMD module:
define([
'./blueimp-helper',
'./blueimp-gallery'
], factory)
} else {
// Browser globals:
factory(
window.blueimp.helper || window.jQuery,
window.blueimp.Gallery
)
}
}(function ($, Gallery) {
'use strict'

$.extend(Gallery.prototype.options, {
// The tag name, Id, element or querySelector of the indicator container:
indicatorContainer: 'ol',
// The class for the active indicator:
activeIndicatorClass: 'active',
// The list object property (or data attribute) with the thumbnail URL,
// used as alternative to a thumbnail child element:
thumbnailProperty: 'thumbnail',
// Defines if the gallery indicators should display a thumbnail:
thumbnailIndicators: true
})

var initSlides = Gallery.prototype.initSlides
var addSlide = Gallery.prototype.addSlide
var resetSlides = Gallery.prototype.resetSlides
var handleClick = Gallery.prototype.handleClick
var handleSlide = Gallery.prototype.handleSlide
var handleClose = Gallery.prototype.handleClose

$.extend(Gallery.prototype, {
createIndicator: function (obj) {
var indicator = this.indicatorPrototype.cloneNode(false)
var title = this.getItemProperty(obj, this.options.titleProperty)
var thumbnailProperty = this.options.thumbnailProperty
var thumbnailUrl
var thumbnail
if (this.options.thumbnailIndicators) {
if (thumbnailProperty) {
thumbnailUrl = this.getItemProperty(obj, thumbnailProperty)
}
if (thumbnailUrl === undefined) {
thumbnail = obj.getElementsByTagName && $(obj).find('img')[0]
if (thumbnail) {
thumbnailUrl = thumbnail.src
}
}
if (thumbnailUrl) {
indicator.style.backgroundImage = 'url("' + thumbnailUrl + '")'
}
}
if (title) {
indicator.title = title
}
return indicator
},

addIndicator: function (index) {
if (this.indicatorContainer.length) {
var indicator = this.createIndicator(this.list[index])
indicator.setAttribute('data-index', index)
this.indicatorContainer[0].appendChild(indicator)
this.indicators.push(indicator)
}
},

setActiveIndicator: function (index) {
if (this.indicators) {
if (this.activeIndicator) {
this.activeIndicator
.removeClass(this.options.activeIndicatorClass)
}
this.activeIndicator = $(this.indicators[index])
this.activeIndicator
.addClass(this.options.activeIndicatorClass)
}
},

initSlides: function (reload) {
if (!reload) {
this.indicatorContainer = this.container.find(
this.options.indicatorContainer
)
if (this.indicatorContainer.length) {
this.indicatorPrototype = document.createElement('li')
this.indicators = this.indicatorContainer[0].children
}
}
initSlides.call(this, reload)
},

addSlide: function (index) {
addSlide.call(this, index)
this.addIndicator(index)
},

resetSlides: function () {
resetSlides.call(this)
this.indicatorContainer.empty()
this.indicators = []
},

handleClick: function (event) {
var target = event.target || event.srcElement
var parent = target.parentNode
if (parent === this.indicatorContainer[0]) {
// Click on indicator element
this.preventDefault(event)
this.slide(this.getNodeIndex(target))
} else if (parent.parentNode === this.indicatorContainer[0]) {
// Click on indicator child element
this.preventDefault(event)
this.slide(this.getNodeIndex(parent))
} else {
return handleClick.call(this, event)
}
},

handleSlide: function (index) {
handleSlide.call(this, index)
this.setActiveIndicator(index)
},

handleClose: function () {
if (this.activeIndicator) {
this.activeIndicator
.removeClass(this.options.activeIndicatorClass)
}
handleClose.call(this)
}

})

return Gallery
}))

+ 0
- 1399
frappe/public/js/lib/gallery/js/blueimp-gallery.js
Fichier diff supprimé car celui-ci est trop grand
Voir le fichier


+ 483
- 0
frappe/public/js/lib/photoswipe/default-skin.css Voir le fichier

@@ -0,0 +1,483 @@
/*! PhotoSwipe Default UI CSS by Dmitry Semenov | photoswipe.com | MIT license */
/*

Contents:

1. Buttons
2. Share modal and links
3. Index indicator ("1 of X" counter)
4. Caption
5. Loading indicator
6. Additional styles (root element, top bar, idle state, hidden state, etc.)

*/
/*
1. Buttons

*/
/* <button> css reset */
.pswp__button {
width: 44px;
height: 44px;
position: relative;
background: none;
cursor: pointer;
overflow: visible;
-webkit-appearance: none;
display: block;
border: 0;
padding: 0;
margin: 0;
float: right;
opacity: 0.75;
-webkit-transition: opacity 0.2s;
transition: opacity 0.2s;
-webkit-box-shadow: none;
box-shadow: none; }
.pswp__button:focus,
.pswp__button:hover {
opacity: 1; }
.pswp__button:active {
outline: none;
opacity: 0.9; }
.pswp__button::-moz-focus-inner {
padding: 0;
border: 0; }

/* pswp__ui--over-close class it added when mouse is over element that should close gallery */
.pswp__ui--over-close .pswp__button--close {
opacity: 1; }

.pswp__button,
.pswp__button--arrow--left:before,
.pswp__button--arrow--right:before {
background: url(default-skin.png) 0 0 no-repeat;
background-size: 264px 88px;
width: 44px;
height: 44px; }

@media (-webkit-min-device-pixel-ratio: 1.1), (-webkit-min-device-pixel-ratio: 1.09375), (min-resolution: 105dpi), (min-resolution: 1.1dppx) {
/* Serve SVG sprite if browser supports SVG and resolution is more than 105dpi */
.pswp--svg .pswp__button,
.pswp--svg .pswp__button--arrow--left:before,
.pswp--svg .pswp__button--arrow--right:before {
background-image: url(default-skin.svg); }
.pswp--svg .pswp__button--arrow--left,
.pswp--svg .pswp__button--arrow--right {
background: none; } }

.pswp__button--close {
background-position: 0 -44px; }

.pswp__button--share {
background-position: -44px -44px; }

.pswp__button--fs {
display: none; }

.pswp--supports-fs .pswp__button--fs {
display: block; }

.pswp--fs .pswp__button--fs {
background-position: -44px 0; }

.pswp__button--zoom {
display: none;
background-position: -88px 0; }

.pswp--zoom-allowed .pswp__button--zoom {
display: block; }

.pswp--zoomed-in .pswp__button--zoom {
background-position: -132px 0; }

/* no arrows on touch screens */
.pswp--touch .pswp__button--arrow--left,
.pswp--touch .pswp__button--arrow--right {
visibility: hidden; }

/*
Arrow buttons hit area
(icon is added to :before pseudo-element)
*/
.pswp__button--arrow--left,
.pswp__button--arrow--right {
background: none;
top: 50%;
margin-top: -50px;
width: 70px;
height: 100px;
position: absolute; }

.pswp__button--arrow--left {
left: 0; }

.pswp__button--arrow--right {
right: 0; }

.pswp__button--arrow--left:before,
.pswp__button--arrow--right:before {
content: '';
top: 35px;
background-color: rgba(0, 0, 0, 0.3);
height: 30px;
width: 32px;
position: absolute; }

.pswp__button--arrow--left:before {
left: 6px;
background-position: -138px -44px; }

.pswp__button--arrow--right:before {
right: 6px;
background-position: -94px -44px; }

/*

2. Share modal/popup and links

*/
.pswp__counter,
.pswp__share-modal {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none; }

.pswp__share-modal {
display: block;
background: rgba(0, 0, 0, 0.5);
width: 100%;
height: 100%;
top: 0;
left: 0;
padding: 10px;
position: absolute;
z-index: 1600;
opacity: 0;
-webkit-transition: opacity 0.25s ease-out;
transition: opacity 0.25s ease-out;
-webkit-backface-visibility: hidden;
will-change: opacity; }

.pswp__share-modal--hidden {
display: none; }

.pswp__share-tooltip {
z-index: 1620;
position: absolute;
background: #FFF;
top: 56px;
border-radius: 2px;
display: block;
width: auto;
right: 44px;
-webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.25);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.25);
-webkit-transform: translateY(6px);
-ms-transform: translateY(6px);
transform: translateY(6px);
-webkit-transition: -webkit-transform 0.25s;
transition: transform 0.25s;
-webkit-backface-visibility: hidden;
will-change: transform; }
.pswp__share-tooltip a {
display: block;
padding: 8px 12px;
color: #000;
text-decoration: none;
font-size: 14px;
line-height: 18px; }
.pswp__share-tooltip a:hover {
text-decoration: none;
color: #000; }
.pswp__share-tooltip a:first-child {
/* round corners on the first/last list item */
border-radius: 2px 2px 0 0; }
.pswp__share-tooltip a:last-child {
border-radius: 0 0 2px 2px; }

.pswp__share-modal--fade-in {
opacity: 1; }
.pswp__share-modal--fade-in .pswp__share-tooltip {
-webkit-transform: translateY(0);
-ms-transform: translateY(0);
transform: translateY(0); }

/* increase size of share links on touch devices */
.pswp--touch .pswp__share-tooltip a {
padding: 16px 12px; }

a.pswp__share--facebook:before {
content: '';
display: block;
width: 0;
height: 0;
position: absolute;
top: -12px;
right: 15px;
border: 6px solid transparent;
border-bottom-color: #FFF;
-webkit-pointer-events: none;
-moz-pointer-events: none;
pointer-events: none; }

a.pswp__share--facebook:hover {
background: #3E5C9A;
color: #FFF; }
a.pswp__share--facebook:hover:before {
border-bottom-color: #3E5C9A; }

a.pswp__share--twitter:hover {
background: #55ACEE;
color: #FFF; }

a.pswp__share--pinterest:hover {
background: #CCC;
color: #CE272D; }

a.pswp__share--download:hover {
background: #DDD; }

/*

3. Index indicator ("1 of X" counter)

*/
.pswp__counter {
position: absolute;
left: 0;
top: 0;
height: 44px;
font-size: 13px;
line-height: 44px;
color: #FFF;
opacity: 0.75;
padding: 0 10px; }

/*
4. Caption

*/
.pswp__caption {
position: absolute;
left: 0;
bottom: 0;
width: 100%;
min-height: 44px; }
.pswp__caption small {
font-size: 11px;
color: #BBB; }

.pswp__caption__center {
text-align: left;
max-width: 420px;
margin: 0 auto;
font-size: 13px;
padding: 10px;
line-height: 20px;
color: #CCC; }

.pswp__caption--empty {
display: none; }

/* Fake caption element, used to calculate height of next/prev image */
.pswp__caption--fake {
visibility: hidden; }

/*

5. Loading indicator (preloader)

You can play with it here - http://codepen.io/dimsemenov/pen/yyBWoR

*/
.pswp__preloader {
width: 44px;
height: 44px;
position: absolute;
top: 0;
left: 50%;
margin-left: -22px;
opacity: 0;
-webkit-transition: opacity 0.25s ease-out;
transition: opacity 0.25s ease-out;
will-change: opacity;
direction: ltr; }

.pswp__preloader__icn {
width: 20px;
height: 20px;
margin: 12px; }

.pswp__preloader--active {
opacity: 1; }
.pswp__preloader--active .pswp__preloader__icn {
/* We use .gif in browsers that don't support CSS animation */
background: url(preloader.gif) 0 0 no-repeat; }

.pswp--css_animation .pswp__preloader--active {
opacity: 1; }
.pswp--css_animation .pswp__preloader--active .pswp__preloader__icn {
-webkit-animation: clockwise 500ms linear infinite;
animation: clockwise 500ms linear infinite; }
.pswp--css_animation .pswp__preloader--active .pswp__preloader__donut {
-webkit-animation: donut-rotate 1000ms cubic-bezier(0.4, 0, 0.22, 1) infinite;
animation: donut-rotate 1000ms cubic-bezier(0.4, 0, 0.22, 1) infinite; }

.pswp--css_animation .pswp__preloader__icn {
background: none;
opacity: 0.75;
width: 14px;
height: 14px;
position: absolute;
left: 15px;
top: 15px;
margin: 0; }

.pswp--css_animation .pswp__preloader__cut {
/*
The idea of animating inner circle is based on Polymer ("material") loading indicator
by Keanu Lee https://blog.keanulee.com/2014/10/20/the-tale-of-three-spinners.html
*/
position: relative;
width: 7px;
height: 14px;
overflow: hidden; }

.pswp--css_animation .pswp__preloader__donut {
-webkit-box-sizing: border-box;
box-sizing: border-box;
width: 14px;
height: 14px;
border: 2px solid #FFF;
border-radius: 50%;
border-left-color: transparent;
border-bottom-color: transparent;
position: absolute;
top: 0;
left: 0;
background: none;
margin: 0; }

@media screen and (max-width: 1024px) {
.pswp__preloader {
position: relative;
left: auto;
top: auto;
margin: 0;
float: right; } }

@-webkit-keyframes clockwise {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg); }
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg); } }

@keyframes clockwise {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg); }
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg); } }

@-webkit-keyframes donut-rotate {
0% {
-webkit-transform: rotate(0);
transform: rotate(0); }
50% {
-webkit-transform: rotate(-140deg);
transform: rotate(-140deg); }
100% {
-webkit-transform: rotate(0);
transform: rotate(0); } }

@keyframes donut-rotate {
0% {
-webkit-transform: rotate(0);
transform: rotate(0); }
50% {
-webkit-transform: rotate(-140deg);
transform: rotate(-140deg); }
100% {
-webkit-transform: rotate(0);
transform: rotate(0); } }

/*
6. Additional styles

*/
/* root element of UI */
.pswp__ui {
-webkit-font-smoothing: auto;
visibility: visible;
opacity: 1;
z-index: 1550; }

/* top black bar with buttons and "1 of X" indicator */
.pswp__top-bar {
position: absolute;
left: 0;
top: 0;
height: 44px;
width: 100%; }

.pswp__caption,
.pswp__top-bar,
.pswp--has_mouse .pswp__button--arrow--left,
.pswp--has_mouse .pswp__button--arrow--right {
-webkit-backface-visibility: hidden;
will-change: opacity;
-webkit-transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1);
transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1); }

/* pswp--has_mouse class is added only when two subsequent mousemove events occur */
.pswp--has_mouse .pswp__button--arrow--left,
.pswp--has_mouse .pswp__button--arrow--right {
visibility: visible; }

.pswp__top-bar,
.pswp__caption {
background-color: rgba(0, 0, 0, 0.5); }

/* pswp__ui--fit class is added when main image "fits" between top bar and bottom bar (caption) */
.pswp__ui--fit .pswp__top-bar,
.pswp__ui--fit .pswp__caption {
background-color: rgba(0, 0, 0, 0.3); }

/* pswp__ui--idle class is added when mouse isn't moving for several seconds (JS option timeToIdle) */
.pswp__ui--idle .pswp__top-bar {
opacity: 0; }

.pswp__ui--idle .pswp__button--arrow--left,
.pswp__ui--idle .pswp__button--arrow--right {
opacity: 0; }

/*
pswp__ui--hidden class is added when controls are hidden
e.g. when user taps to toggle visibility of controls
*/
.pswp__ui--hidden .pswp__top-bar,
.pswp__ui--hidden .pswp__caption,
.pswp__ui--hidden .pswp__button--arrow--left,
.pswp__ui--hidden .pswp__button--arrow--right {
/* Force paint & create composition layer for controls. */
opacity: 0.001; }

/* pswp__ui--one-slide class is added when there is just one item in gallery */
.pswp__ui--one-slide .pswp__button--arrow--left,
.pswp__ui--one-slide .pswp__button--arrow--right,
.pswp__ui--one-slide .pswp__counter {
display: none; }

.pswp__element--disabled {
display: none !important; }

.pswp--minimal--dark .pswp__top-bar {
background: none; }

+ 861
- 0
frappe/public/js/lib/photoswipe/photoswipe-ui-default.js Voir le fichier

@@ -0,0 +1,861 @@
/*! PhotoSwipe Default UI - 4.1.1 - 2015-12-24
* http://photoswipe.com
* Copyright (c) 2015 Dmitry Semenov; */
/**
*
* UI on top of main sliding area (caption, arrows, close button, etc.).
* Built just using public methods/properties of PhotoSwipe.
*
*/
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(factory);
} else if (typeof exports === 'object') {
module.exports = factory();
} else {
root.PhotoSwipeUI_Default = factory();
}
})(this, function () {

'use strict';



var PhotoSwipeUI_Default =
function(pswp, framework) {

var ui = this;
var _overlayUIUpdated = false,
_controlsVisible = true,
_fullscrenAPI,
_controls,
_captionContainer,
_fakeCaptionContainer,
_indexIndicator,
_shareButton,
_shareModal,
_shareModalHidden = true,
_initalCloseOnScrollValue,
_isIdle,
_listen,

_loadingIndicator,
_loadingIndicatorHidden,
_loadingIndicatorTimeout,

_galleryHasOneSlide,

_options,
_defaultUIOptions = {
barsSize: {top:44, bottom:'auto'},
closeElClasses: ['item', 'caption', 'zoom-wrap', 'ui', 'top-bar'],
timeToIdle: 4000,
timeToIdleOutside: 1000,
loadingIndicatorDelay: 1000, // 2s
addCaptionHTMLFn: function(item, captionEl /*, isFake */) {
if(!item.title) {
captionEl.children[0].innerHTML = '';
return false;
}
captionEl.children[0].innerHTML = item.title;
return true;
},

closeEl:true,
captionEl: true,
fullscreenEl: true,
zoomEl: true,
shareEl: true,
counterEl: true,
arrowEl: true,
preloaderEl: true,

tapToClose: false,
tapToToggleControls: true,

clickToCloseNonZoomable: true,

shareButtons: [
{id:'facebook', label:'Share on Facebook', url:'https://www.facebook.com/sharer/sharer.php?u={{url}}'},
{id:'twitter', label:'Tweet', url:'https://twitter.com/intent/tweet?text={{text}}&url={{url}}'},
{id:'pinterest', label:'Pin it', url:'http://www.pinterest.com/pin/create/button/'+
'?url={{url}}&media={{image_url}}&description={{text}}'},
{id:'download', label:'Download image', url:'{{raw_image_url}}', download:true}
],
getImageURLForShare: function( /* shareButtonData */ ) {
return pswp.currItem.src || '';
},
getPageURLForShare: function( /* shareButtonData */ ) {
return window.location.href;
},
getTextForShare: function( /* shareButtonData */ ) {
return pswp.currItem.title || '';
},
indexIndicatorSep: ' / ',
fitControlsWidth: 1200

},
_blockControlsTap,
_blockControlsTapTimeout;



var _onControlsTap = function(e) {
if(_blockControlsTap) {
return true;
}


e = e || window.event;

if(_options.timeToIdle && _options.mouseUsed && !_isIdle) {
// reset idle timer
_onIdleMouseMove();
}


var target = e.target || e.srcElement,
uiElement,
clickedClass = target.getAttribute('class') || '',
found;

for(var i = 0; i < _uiElements.length; i++) {
uiElement = _uiElements[i];
if(uiElement.onTap && clickedClass.indexOf('pswp__' + uiElement.name ) > -1 ) {
uiElement.onTap();
found = true;

}
}

if(found) {
if(e.stopPropagation) {
e.stopPropagation();
}
_blockControlsTap = true;

// Some versions of Android don't prevent ghost click event
// when preventDefault() was called on touchstart and/or touchend.
//
// This happens on v4.3, 4.2, 4.1,
// older versions strangely work correctly,
// but just in case we add delay on all of them)
var tapDelay = framework.features.isOldAndroid ? 600 : 30;
_blockControlsTapTimeout = setTimeout(function() {
_blockControlsTap = false;
}, tapDelay);
}

},
_fitControlsInViewport = function() {
return !pswp.likelyTouchDevice || _options.mouseUsed || screen.width > _options.fitControlsWidth;
},
_togglePswpClass = function(el, cName, add) {
framework[ (add ? 'add' : 'remove') + 'Class' ](el, 'pswp__' + cName);
},

// add class when there is just one item in the gallery
// (by default it hides left/right arrows and 1ofX counter)
_countNumItems = function() {
var hasOneSlide = (_options.getNumItemsFn() === 1);

if(hasOneSlide !== _galleryHasOneSlide) {
_togglePswpClass(_controls, 'ui--one-slide', hasOneSlide);
_galleryHasOneSlide = hasOneSlide;
}
},
_toggleShareModalClass = function() {
_togglePswpClass(_shareModal, 'share-modal--hidden', _shareModalHidden);
},
_toggleShareModal = function() {

_shareModalHidden = !_shareModalHidden;
if(!_shareModalHidden) {
_toggleShareModalClass();
setTimeout(function() {
if(!_shareModalHidden) {
framework.addClass(_shareModal, 'pswp__share-modal--fade-in');
}
}, 30);
} else {
framework.removeClass(_shareModal, 'pswp__share-modal--fade-in');
setTimeout(function() {
if(_shareModalHidden) {
_toggleShareModalClass();
}
}, 300);
}
if(!_shareModalHidden) {
_updateShareURLs();
}
return false;
},

_openWindowPopup = function(e) {
e = e || window.event;
var target = e.target || e.srcElement;

pswp.shout('shareLinkClick', e, target);

if(!target.href) {
return false;
}

if( target.hasAttribute('download') ) {
return true;
}

window.open(target.href, 'pswp_share', 'scrollbars=yes,resizable=yes,toolbar=no,'+
'location=yes,width=550,height=420,top=100,left=' +
(window.screen ? Math.round(screen.width / 2 - 275) : 100) );

if(!_shareModalHidden) {
_toggleShareModal();
}
return false;
},
_updateShareURLs = function() {
var shareButtonOut = '',
shareButtonData,
shareURL,
image_url,
page_url,
share_text;

for(var i = 0; i < _options.shareButtons.length; i++) {
shareButtonData = _options.shareButtons[i];

image_url = _options.getImageURLForShare(shareButtonData);
page_url = _options.getPageURLForShare(shareButtonData);
share_text = _options.getTextForShare(shareButtonData);

shareURL = shareButtonData.url.replace('{{url}}', encodeURIComponent(page_url) )
.replace('{{image_url}}', encodeURIComponent(image_url) )
.replace('{{raw_image_url}}', image_url )
.replace('{{text}}', encodeURIComponent(share_text) );

shareButtonOut += '<a href="' + shareURL + '" target="_blank" '+
'class="pswp__share--' + shareButtonData.id + '"' +
(shareButtonData.download ? 'download' : '') + '>' +
shareButtonData.label + '</a>';

if(_options.parseShareButtonOut) {
shareButtonOut = _options.parseShareButtonOut(shareButtonData, shareButtonOut);
}
}
_shareModal.children[0].innerHTML = shareButtonOut;
_shareModal.children[0].onclick = _openWindowPopup;

},
_hasCloseClass = function(target) {
for(var i = 0; i < _options.closeElClasses.length; i++) {
if( framework.hasClass(target, 'pswp__' + _options.closeElClasses[i]) ) {
return true;
}
}
},
_idleInterval,
_idleTimer,
_idleIncrement = 0,
_onIdleMouseMove = function() {
clearTimeout(_idleTimer);
_idleIncrement = 0;
if(_isIdle) {
ui.setIdle(false);
}
},
_onMouseLeaveWindow = function(e) {
e = e ? e : window.event;
var from = e.relatedTarget || e.toElement;
if (!from || from.nodeName === 'HTML') {
clearTimeout(_idleTimer);
_idleTimer = setTimeout(function() {
ui.setIdle(true);
}, _options.timeToIdleOutside);
}
},
_setupFullscreenAPI = function() {
if(_options.fullscreenEl && !framework.features.isOldAndroid) {
if(!_fullscrenAPI) {
_fullscrenAPI = ui.getFullscreenAPI();
}
if(_fullscrenAPI) {
framework.bind(document, _fullscrenAPI.eventK, ui.updateFullscreen);
ui.updateFullscreen();
framework.addClass(pswp.template, 'pswp--supports-fs');
} else {
framework.removeClass(pswp.template, 'pswp--supports-fs');
}
}
},
_setupLoadingIndicator = function() {
// Setup loading indicator
if(_options.preloaderEl) {
_toggleLoadingIndicator(true);

_listen('beforeChange', function() {

clearTimeout(_loadingIndicatorTimeout);

// display loading indicator with delay
_loadingIndicatorTimeout = setTimeout(function() {

if(pswp.currItem && pswp.currItem.loading) {

if( !pswp.allowProgressiveImg() || (pswp.currItem.img && !pswp.currItem.img.naturalWidth) ) {
// show preloader if progressive loading is not enabled,
// or image width is not defined yet (because of slow connection)
_toggleLoadingIndicator(false);
// items-controller.js function allowProgressiveImg
}
} else {
_toggleLoadingIndicator(true); // hide preloader
}

}, _options.loadingIndicatorDelay);
});
_listen('imageLoadComplete', function(index, item) {
if(pswp.currItem === item) {
_toggleLoadingIndicator(true);
}
});

}
},
_toggleLoadingIndicator = function(hide) {
if( _loadingIndicatorHidden !== hide ) {
_togglePswpClass(_loadingIndicator, 'preloader--active', !hide);
_loadingIndicatorHidden = hide;
}
},
_applyNavBarGaps = function(item) {
var gap = item.vGap;

if( _fitControlsInViewport() ) {
var bars = _options.barsSize;
if(_options.captionEl && bars.bottom === 'auto') {
if(!_fakeCaptionContainer) {
_fakeCaptionContainer = framework.createEl('pswp__caption pswp__caption--fake');
_fakeCaptionContainer.appendChild( framework.createEl('pswp__caption__center') );
_controls.insertBefore(_fakeCaptionContainer, _captionContainer);
framework.addClass(_controls, 'pswp__ui--fit');
}
if( _options.addCaptionHTMLFn(item, _fakeCaptionContainer, true) ) {

var captionSize = _fakeCaptionContainer.clientHeight;
gap.bottom = parseInt(captionSize,10) || 44;
} else {
gap.bottom = bars.top; // if no caption, set size of bottom gap to size of top
}
} else {
gap.bottom = bars.bottom === 'auto' ? 0 : bars.bottom;
}
// height of top bar is static, no need to calculate it
gap.top = bars.top;
} else {
gap.top = gap.bottom = 0;
}
},
_setupIdle = function() {
// Hide controls when mouse is used
if(_options.timeToIdle) {
_listen('mouseUsed', function() {
framework.bind(document, 'mousemove', _onIdleMouseMove);
framework.bind(document, 'mouseout', _onMouseLeaveWindow);

_idleInterval = setInterval(function() {
_idleIncrement++;
if(_idleIncrement === 2) {
ui.setIdle(true);
}
}, _options.timeToIdle / 2);
});
}
},
_setupHidingControlsDuringGestures = function() {

// Hide controls on vertical drag
_listen('onVerticalDrag', function(now) {
if(_controlsVisible && now < 0.95) {
ui.hideControls();
} else if(!_controlsVisible && now >= 0.95) {
ui.showControls();
}
});

// Hide controls when pinching to close
var pinchControlsHidden;
_listen('onPinchClose' , function(now) {
if(_controlsVisible && now < 0.9) {
ui.hideControls();
pinchControlsHidden = true;
} else if(pinchControlsHidden && !_controlsVisible && now > 0.9) {
ui.showControls();
}
});

_listen('zoomGestureEnded', function() {
pinchControlsHidden = false;
if(pinchControlsHidden && !_controlsVisible) {
ui.showControls();
}
});

};



var _uiElements = [
{
name: 'caption',
option: 'captionEl',
onInit: function(el) {
_captionContainer = el;
}
},
{
name: 'share-modal',
option: 'shareEl',
onInit: function(el) {
_shareModal = el;
},
onTap: function() {
_toggleShareModal();
}
},
{
name: 'button--share',
option: 'shareEl',
onInit: function(el) {
_shareButton = el;
},
onTap: function() {
_toggleShareModal();
}
},
{
name: 'button--zoom',
option: 'zoomEl',
onTap: pswp.toggleDesktopZoom
},
{
name: 'counter',
option: 'counterEl',
onInit: function(el) {
_indexIndicator = el;
}
},
{
name: 'button--close',
option: 'closeEl',
onTap: pswp.close
},
{
name: 'button--arrow--left',
option: 'arrowEl',
onTap: pswp.prev
},
{
name: 'button--arrow--right',
option: 'arrowEl',
onTap: pswp.next
},
{
name: 'button--fs',
option: 'fullscreenEl',
onTap: function() {
if(_fullscrenAPI.isFullscreen()) {
_fullscrenAPI.exit();
} else {
_fullscrenAPI.enter();
}
}
},
{
name: 'preloader',
option: 'preloaderEl',
onInit: function(el) {
_loadingIndicator = el;
}
}

];

var _setupUIElements = function() {
var item,
classAttr,
uiElement;

var loopThroughChildElements = function(sChildren) {
if(!sChildren) {
return;
}

var l = sChildren.length;
for(var i = 0; i < l; i++) {
item = sChildren[i];
classAttr = item.className;

for(var a = 0; a < _uiElements.length; a++) {
uiElement = _uiElements[a];

if(classAttr.indexOf('pswp__' + uiElement.name) > -1 ) {

if( _options[uiElement.option] ) { // if element is not disabled from options
framework.removeClass(item, 'pswp__element--disabled');
if(uiElement.onInit) {
uiElement.onInit(item);
}
//item.style.display = 'block';
} else {
framework.addClass(item, 'pswp__element--disabled');
//item.style.display = 'none';
}
}
}
}
};
loopThroughChildElements(_controls.children);

var topBar = framework.getChildByClass(_controls, 'pswp__top-bar');
if(topBar) {
loopThroughChildElements( topBar.children );
}
};



ui.init = function() {

// extend options
framework.extend(pswp.options, _defaultUIOptions, true);

// create local link for fast access
_options = pswp.options;

// find pswp__ui element
_controls = framework.getChildByClass(pswp.scrollWrap, 'pswp__ui');

// create local link
_listen = pswp.listen;


_setupHidingControlsDuringGestures();

// update controls when slides change
_listen('beforeChange', ui.update);

// toggle zoom on double-tap
_listen('doubleTap', function(point) {
var initialZoomLevel = pswp.currItem.initialZoomLevel;
if(pswp.getZoomLevel() !== initialZoomLevel) {
pswp.zoomTo(initialZoomLevel, point, 333);
} else {
pswp.zoomTo(_options.getDoubleTapZoom(false, pswp.currItem), point, 333);
}
});

// Allow text selection in caption
_listen('preventDragEvent', function(e, isDown, preventObj) {
var t = e.target || e.srcElement;
if(
t &&
t.getAttribute('class') && e.type.indexOf('mouse') > -1 &&
( t.getAttribute('class').indexOf('__caption') > 0 || (/(SMALL|STRONG|EM)/i).test(t.tagName) )
) {
preventObj.prevent = false;
}
});

// bind events for UI
_listen('bindEvents', function() {
framework.bind(_controls, 'pswpTap click', _onControlsTap);
framework.bind(pswp.scrollWrap, 'pswpTap', ui.onGlobalTap);

if(!pswp.likelyTouchDevice) {
framework.bind(pswp.scrollWrap, 'mouseover', ui.onMouseOver);
}
});

// unbind events for UI
_listen('unbindEvents', function() {
if(!_shareModalHidden) {
_toggleShareModal();
}

if(_idleInterval) {
clearInterval(_idleInterval);
}
framework.unbind(document, 'mouseout', _onMouseLeaveWindow);
framework.unbind(document, 'mousemove', _onIdleMouseMove);
framework.unbind(_controls, 'pswpTap click', _onControlsTap);
framework.unbind(pswp.scrollWrap, 'pswpTap', ui.onGlobalTap);
framework.unbind(pswp.scrollWrap, 'mouseover', ui.onMouseOver);

if(_fullscrenAPI) {
framework.unbind(document, _fullscrenAPI.eventK, ui.updateFullscreen);
if(_fullscrenAPI.isFullscreen()) {
_options.hideAnimationDuration = 0;
_fullscrenAPI.exit();
}
_fullscrenAPI = null;
}
});


// clean up things when gallery is destroyed
_listen('destroy', function() {
if(_options.captionEl) {
if(_fakeCaptionContainer) {
_controls.removeChild(_fakeCaptionContainer);
}
framework.removeClass(_captionContainer, 'pswp__caption--empty');
}

if(_shareModal) {
_shareModal.children[0].onclick = null;
}
framework.removeClass(_controls, 'pswp__ui--over-close');
framework.addClass( _controls, 'pswp__ui--hidden');
ui.setIdle(false);
});

if(!_options.showAnimationDuration) {
framework.removeClass( _controls, 'pswp__ui--hidden');
}
_listen('initialZoomIn', function() {
if(_options.showAnimationDuration) {
framework.removeClass( _controls, 'pswp__ui--hidden');
}
});
_listen('initialZoomOut', function() {
framework.addClass( _controls, 'pswp__ui--hidden');
});

_listen('parseVerticalMargin', _applyNavBarGaps);
_setupUIElements();

if(_options.shareEl && _shareButton && _shareModal) {
_shareModalHidden = true;
}

_countNumItems();

_setupIdle();

_setupFullscreenAPI();

_setupLoadingIndicator();
};

ui.setIdle = function(isIdle) {
_isIdle = isIdle;
_togglePswpClass(_controls, 'ui--idle', isIdle);
};

ui.update = function() {
// Don't update UI if it's hidden
if(_controlsVisible && pswp.currItem) {
ui.updateIndexIndicator();

if(_options.captionEl) {
_options.addCaptionHTMLFn(pswp.currItem, _captionContainer);

_togglePswpClass(_captionContainer, 'caption--empty', !pswp.currItem.title);
}

_overlayUIUpdated = true;

} else {
_overlayUIUpdated = false;
}

if(!_shareModalHidden) {
_toggleShareModal();
}

_countNumItems();
};

ui.updateFullscreen = function(e) {

if(e) {
// some browsers change window scroll position during the fullscreen
// so PhotoSwipe updates it just in case
setTimeout(function() {
pswp.setScrollOffset( 0, framework.getScrollY() );
}, 50);
}
// toogle pswp--fs class on root element
framework[ (_fullscrenAPI.isFullscreen() ? 'add' : 'remove') + 'Class' ](pswp.template, 'pswp--fs');
};

ui.updateIndexIndicator = function() {
if(_options.counterEl) {
_indexIndicator.innerHTML = (pswp.getCurrentIndex()+1) +
_options.indexIndicatorSep +
_options.getNumItemsFn();
}
};
ui.onGlobalTap = function(e) {
e = e || window.event;
var target = e.target || e.srcElement;

if(_blockControlsTap) {
return;
}

if(e.detail && e.detail.pointerType === 'mouse') {

// close gallery if clicked outside of the image
if(_hasCloseClass(target)) {
pswp.close();
return;
}

if(framework.hasClass(target, 'pswp__img')) {
if(pswp.getZoomLevel() === 1 && pswp.getZoomLevel() <= pswp.currItem.fitRatio) {
if(_options.clickToCloseNonZoomable) {
pswp.close();
}
} else {
pswp.toggleDesktopZoom(e.detail.releasePoint);
}
}
} else {

// tap anywhere (except buttons) to toggle visibility of controls
if(_options.tapToToggleControls) {
if(_controlsVisible) {
ui.hideControls();
} else {
ui.showControls();
}
}

// tap to close gallery
if(_options.tapToClose && (framework.hasClass(target, 'pswp__img') || _hasCloseClass(target)) ) {
pswp.close();
return;
}
}
};
ui.onMouseOver = function(e) {
e = e || window.event;
var target = e.target || e.srcElement;

// add class when mouse is over an element that should close the gallery
_togglePswpClass(_controls, 'ui--over-close', _hasCloseClass(target));
};

ui.hideControls = function() {
framework.addClass(_controls,'pswp__ui--hidden');
_controlsVisible = false;
};

ui.showControls = function() {
_controlsVisible = true;
if(!_overlayUIUpdated) {
ui.update();
}
framework.removeClass(_controls,'pswp__ui--hidden');
};

ui.supportsFullscreen = function() {
var d = document;
return !!(d.exitFullscreen || d.mozCancelFullScreen || d.webkitExitFullscreen || d.msExitFullscreen);
};

ui.getFullscreenAPI = function() {
var dE = document.documentElement,
api,
tF = 'fullscreenchange';

if (dE.requestFullscreen) {
api = {
enterK: 'requestFullscreen',
exitK: 'exitFullscreen',
elementK: 'fullscreenElement',
eventK: tF
};

} else if(dE.mozRequestFullScreen ) {
api = {
enterK: 'mozRequestFullScreen',
exitK: 'mozCancelFullScreen',
elementK: 'mozFullScreenElement',
eventK: 'moz' + tF
};


} else if(dE.webkitRequestFullscreen) {
api = {
enterK: 'webkitRequestFullscreen',
exitK: 'webkitExitFullscreen',
elementK: 'webkitFullscreenElement',
eventK: 'webkit' + tF
};

} else if(dE.msRequestFullscreen) {
api = {
enterK: 'msRequestFullscreen',
exitK: 'msExitFullscreen',
elementK: 'msFullscreenElement',
eventK: 'MSFullscreenChange'
};
}

if(api) {
api.enter = function() {
// disable close-on-scroll in fullscreen
_initalCloseOnScrollValue = _options.closeOnScroll;
_options.closeOnScroll = false;

if(this.enterK === 'webkitRequestFullscreen') {
pswp.template[this.enterK]( Element.ALLOW_KEYBOARD_INPUT );
} else {
return pswp.template[this.enterK]();
}
};
api.exit = function() {
_options.closeOnScroll = _initalCloseOnScrollValue;

return document[this.exitK]();

};
api.isFullscreen = function() { return document[this.elementK]; };
}

return api;
};



};
return PhotoSwipeUI_Default;


});

+ 178
- 0
frappe/public/js/lib/photoswipe/photoswipe.css Voir le fichier

@@ -0,0 +1,178 @@
/*! PhotoSwipe main CSS by Dmitry Semenov | photoswipe.com | MIT license */
/*
Styles for basic PhotoSwipe functionality (sliding area, open/close transitions)
*/
/* pswp = photoswipe */
.pswp {
display: none;
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
overflow: hidden;
-ms-touch-action: none;
touch-action: none;
z-index: 1500;
-webkit-text-size-adjust: 100%;
/* create separate layer, to avoid paint on window.onscroll in webkit/blink */
-webkit-backface-visibility: hidden;
outline: none; }
.pswp * {
-webkit-box-sizing: border-box;
box-sizing: border-box; }
.pswp img {
max-width: none; }

/* style is added when JS option showHideOpacity is set to true */
.pswp--animate_opacity {
/* 0.001, because opacity:0 doesn't trigger Paint action, which causes lag at start of transition */
opacity: 0.001;
will-change: opacity;
/* for open/close transition */
-webkit-transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1);
transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1); }

.pswp--open {
display: block; }

.pswp--zoom-allowed .pswp__img {
/* autoprefixer: off */
cursor: -webkit-zoom-in;
cursor: -moz-zoom-in;
cursor: zoom-in; }

.pswp--zoomed-in .pswp__img {
/* autoprefixer: off */
cursor: -webkit-grab;
cursor: -moz-grab;
cursor: grab; }

.pswp--dragging .pswp__img {
/* autoprefixer: off */
cursor: -webkit-grabbing;
cursor: -moz-grabbing;
cursor: grabbing; }

/*
Background is added as a separate element.
As animating opacity is much faster than animating rgba() background-color.
*/
.pswp__bg {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: #000;
opacity: 0;
transform: translateZ(0);
-webkit-backface-visibility: hidden;
will-change: opacity; }

.pswp__scroll-wrap {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: hidden; }

.pswp__container,
.pswp__zoom-wrap {
-ms-touch-action: none;
touch-action: none;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0; }

/* Prevent selection and tap highlights */
.pswp__container,
.pswp__img {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-tap-highlight-color: transparent;
-webkit-touch-callout: none; }

.pswp__zoom-wrap {
position: absolute;
width: 100%;
-webkit-transform-origin: left top;
-ms-transform-origin: left top;
transform-origin: left top;
/* for open/close transition */
-webkit-transition: -webkit-transform 333ms cubic-bezier(0.4, 0, 0.22, 1);
transition: transform 333ms cubic-bezier(0.4, 0, 0.22, 1); }

.pswp__bg {
will-change: opacity;
/* for open/close transition */
-webkit-transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1);
transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1); }

.pswp--animated-in .pswp__bg,
.pswp--animated-in .pswp__zoom-wrap {
-webkit-transition: none;
transition: none; }

.pswp__container,
.pswp__zoom-wrap {
-webkit-backface-visibility: hidden; }

.pswp__item {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
overflow: hidden; }

.pswp__img {
position: absolute;
width: auto;
height: auto;
top: 0;
left: 0; }

/*
stretched thumbnail or div placeholder element (see below)
style is added to avoid flickering in webkit/blink when layers overlap
*/
.pswp__img--placeholder {
-webkit-backface-visibility: hidden; }

/*
div element that matches size of large image
large image loads on top of it
*/
.pswp__img--placeholder--blank {
background: #222; }

.pswp--ie .pswp__img {
width: 100% !important;
height: auto !important;
left: 0;
top: 0; }

/*
Error message appears when image is not loaded
(JS option errorMsg controls markup)
*/
.pswp__error-msg {
position: absolute;
left: 0;
top: 50%;
width: 100%;
text-align: center;
font-size: 14px;
line-height: 16px;
margin-top: -8px;
color: #CCC; }

.pswp__error-msg a {
color: #CCC;
text-decoration: underline; }

+ 3718
- 0
frappe/public/js/lib/photoswipe/photoswipe.js
Fichier diff supprimé car celui-ci est trop grand
Voir le fichier


+ 28
- 0
frappe/public/less/desk.less Voir le fichier

@@ -768,3 +768,31 @@ textarea.form-control {

.c3-tooltip td.value {
text-align: right; }

// custom font awesome checkbox
input[type="checkbox"] {
visibility: hidden;
position: relative;

&:before {
position: absolute;
font-family: 'FontAwesome';
content: '\f096';
visibility: visible;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 14px;
display: inline-block;
font-size: 14px;
color: @text-extra-muted;
.transition(150ms color);
}

&:checked:before {
content: '\f14a';
font-size: 13px;
color: @checkbox-color;
}
}

+ 1
- 0
frappe/public/less/form_grid.less Voir le fichier

@@ -102,6 +102,7 @@
.checkbox {
margin: 0px;
text-align: center;
margin-top: 9px;
}

textarea {


+ 150
- 70
frappe/public/less/list.less Voir le fichier

@@ -141,7 +141,26 @@
}

.list-value {
display: table;
vertical-align: middle;

.list-row-checkbox, .liked-by, .list-id, .list-select-all {
display: table-cell;
vertical-align: middle;
}

.list-row-checkbox, .list-select-all {
margin: 0;
margin-right: 7px;
}

.liked-by {
padding-top: 2px;
}

.list-col-title {
vertical-align: middle;
}
}

.progress {
@@ -150,14 +169,14 @@

.doclist-row {
font-size: 12px;
}

.likes-count {
display: inline-block;
width: 15px;
margin-left: -5px;
color: @text-muted;
font-size: 12px;
}
.likes-count {
display: inline-block;
width: 15px;
margin-left: -5px;
color: @text-muted;
font-size: 12px;
}

.doclist-row .docstatus .octicon {
@@ -210,10 +229,6 @@
cursor: pointer;
}

.list-row-head .octicon-heart {
margin-right: 13px;
}

.like-action.octicon-heart {
// color: #ffdb4c;
color: @heart-color;
@@ -255,65 +270,6 @@
text-align: left;
}

.image-view {
float: left;
}

.image-view-subject {
padding: inherit;
}

.image-view-col {
padding-bottom: 5px;
padding-top: 5px;

a {
text-decoration: none !important;
}
}

table.field-info {
opacity: 0;
bottom: -20px;
font-size: 8pt;
color: white;
background-color: @text-color;
position: absolute;
padding-bottom: 0px !important;
border: 0px;
}

table.field-info tr td {
border: none !important;
}

.zoom-view {
top: 10px !important;
right: 10px !important;
width: 36px;
height: 36px;
opacity: 0;
font-size: 16px;
color: @text-color;
position: absolute;
padding: 0px !important;
border-radius: 5px;
border: 0px;
}

.image-field {
background-size: contain;
position: relative;
}

.image-field:hover .field-info {
opacity: 0.7;
}

.image-field:hover .zoom-view {
opacity: 0.6;
}

// tags

.tag-row {
@@ -367,4 +323,128 @@ table.field-info tr td {
margin-top: 3px;
font-size: 11px;
max-width: 100px;
}

// Image view

.image-view-container {

display: flex;
flex-wrap: wrap;

.image-view-item {
flex: 0 0 100%/4;
padding: 15px;
border-bottom: 1px solid @light-border-color;
border-right: 1px solid @light-border-color;
}

.image-view-item:nth-child(4n) {
border-right: none;
}
.image-view-item:nth-last-child(-n + 4):nth-child(4n + 1),
.image-view-item:nth-last-child(-n + 4):nth-child(4n + 1) ~ .image-view-item {
border-bottom: none;
}

.image-view-header {
margin-bottom: 10px;
}

.image-view-body {
&:hover .zoom-view {
opacity: 0.7;
}

a {
text-decoration: none;
}
}

.image-field {
display: flex;
align-content: center;
align-items: center;
justify-content: center;
position: relative;
height: 200px;
padding: 15px;

img {
max-height: 100%;
}
}

.placeholder-text {
font-size: 72px;
color: @text-extra-muted;
}

.zoom-view {
bottom: 10px !important;
right: 10px !important;
width: 36px;
height: 36px;
opacity: 0;
font-size: 16px;
color: @text-color;
position: absolute;

// show zoom button on mobile devices
@media (max-width: @screen-xs) {
opacity: 0.5
}
}

// 3 columns for small screen
@media(max-width: @screen-sm) {
.image-view-item {
flex: 0 0 100%/3;
}

.image-view-item:nth-child(3n) {
border-right: none;
}
.image-view-item:nth-last-child(-n + 3):nth-child(3n + 1),
.image-view-item:nth-last-child(-n + 3):nth-child(3n + 1) ~ .image-view-item {
border-bottom: none;
}

.image-view-item:nth-child(4n) {
border-right: 1px solid @light-border-color;
}
.image-view-item:nth-last-child(-n + 4):nth-child(4n + 1),
.image-view-item:nth-last-child(-n + 4):nth-child(4n + 1) ~ .image-view-item {
border-bottom: 1px solid @light-border-color;
}
}
}

.pswp--svg .pswp__button,
.pswp--svg .pswp__button--arrow--left:before,
.pswp--svg .pswp__button--arrow--right:before {
background-image: url('/assets/frappe/images/default-skin.svg') !important;
}
.pswp--svg .pswp__button--arrow--left,
.pswp--svg .pswp__button--arrow--right {
background: none !important;
}

.pswp__bg {
background-color: #fff !important;
}

// gantt
.gantt {
.details-container {
.heading {
margin-bottom: 10px;
font-size: 12px;
}

.avatar-small {
width: 16px;
height: 16px;
}
}
}

+ 4
- 0
frappe/public/less/sidebar.less Voir le fichier

@@ -166,6 +166,10 @@ body[data-route^="Module"] .main-menu {
}
}

.liked-by {
margin-left: -4px;
}

.liked-by .octicon-heart {
font-size: 16px;
cursor: pointer;


Chargement…
Annuler
Enregistrer