* [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 fixversion-14
@@ -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"]] | |||
}; |
@@ -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); | |||
@@ -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" | |||
@@ -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): | |||
@@ -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 | |||
@@ -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); | |||
}); | |||
}, | |||
@@ -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> | |||
@@ -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, | |||
@@ -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 = "" | |||
@@ -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)) |
@@ -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 |
@@ -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 +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") |
@@ -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" | |||
@@ -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; | |||
} |
@@ -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; | |||
@@ -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; | |||
} |
@@ -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; | |||
@@ -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> |
@@ -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"); | |||
}, | |||
@@ -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> | |||
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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") | |||
} | |||
} | |||
}); |
@@ -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) }} | |||
{% } %} | |||
@@ -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> | |||
{% } %} | |||
{% } %} | |||
@@ -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,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,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) { %} | |||
@@ -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); | |||
} | |||
}); |
@@ -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); | |||
} | |||
@@ -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 | |||
}) | |||
} | |||
}); |
@@ -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) || ''})); | |||
} | |||
}); |
@@ -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); | |||
}) | |||
} | |||
}) | |||
} |
@@ -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); | |||
} | |||
@@ -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; | |||
} |
@@ -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('/'); | |||
@@ -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(); | |||
} | |||
} | |||
}); | |||
} | |||
}); |
@@ -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(); | |||
} | |||
}) | |||
}, | |||
@@ -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"> | |||
@@ -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); | |||
@@ -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"); | |||
@@ -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 | |||
@@ -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 + "%"]; | |||
@@ -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); | |||
} | |||
@@ -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(); |
@@ -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"); | |||
@@ -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(); | |||
@@ -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" | |||
] | |||
}); |
@@ -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); | |||
} | |||
}); |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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(); | |||
}); | |||
}, | |||
@@ -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); | |||
} | |||
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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,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; } |
@@ -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; | |||
}); |
@@ -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; } |
@@ -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; | |||
} | |||
} |
@@ -102,6 +102,7 @@ | |||
.checkbox { | |||
margin: 0px; | |||
text-align: center; | |||
margin-top: 9px; | |||
} | |||
textarea { | |||
@@ -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; | |||
} | |||
} | |||
} |
@@ -166,6 +166,10 @@ body[data-route^="Module"] .main-menu { | |||
} | |||
} | |||
.liked-by { | |||
margin-left: -4px; | |||
} | |||
.liked-by .octicon-heart { | |||
font-size: 16px; | |||
cursor: pointer; | |||