diff --git a/.eslintrc b/.eslintrc index adc4aebb28..dd9e350b1b 100644 --- a/.eslintrc +++ b/.eslintrc @@ -134,7 +134,6 @@ "Webcam": true, "PhotoSwipe": true, "PhotoSwipeUI_Default": true, - "fluxify": true, "io": true, "JsBarcode": true, "L": true, diff --git a/attributions.md b/attributions.md index 43c6b14912..5afc9f9d46 100644 --- a/attributions.md +++ b/attributions.md @@ -11,7 +11,6 @@ The following 3rd-party software packages may be used by or distributed with ### Icon Fonts diff --git a/cypress/integration/workspace_blocks.js b/cypress/integration/workspace_blocks.js index 3c0a429973..527cacab93 100644 --- a/cypress/integration/workspace_blocks.js +++ b/cypress/integration/workspace_blocks.js @@ -105,7 +105,7 @@ context('Workspace Blocks', () => { // test filter-list cy.get('@todo-quick-list').realHover().find('.widget-control .filter-list').click(); - cy.get_open_dialog().find('.filter-field .input-with-feedback').clear().type('Approved'); + cy.get_open_dialog().find('.filter-field .input-with-feedback').type('{selectall}Approved'); cy.get_open_dialog().find('.modal-header').click(); cy.get_open_dialog().find('.btn-primary').click(); diff --git a/frappe/auth.py b/frappe/auth.py index 3737681d71..455e9ee0c5 100644 --- a/frappe/auth.py +++ b/frappe/auth.py @@ -109,6 +109,9 @@ class HTTPRequest: class LoginManager: + + __slots__ = ("user", "info", "full_name", "user_type", "resume") + def __init__(self): self.user = None self.info = None diff --git a/frappe/boot.py b/frappe/boot.py index a0a93bd497..ad729746fe 100644 --- a/frappe/boot.py +++ b/frappe/boot.py @@ -11,7 +11,6 @@ from frappe.core.doctype.navbar_settings.navbar_settings import get_app_logo, ge from frappe.desk.doctype.route_history.route_history import frequently_visited_links from frappe.desk.form.load import get_meta_bundle from frappe.email.inbox import get_email_accounts -from frappe.geo.country_info import get_all from frappe.model.base_document import get_controller from frappe.query_builder import DocType from frappe.query_builder.functions import Count @@ -21,7 +20,7 @@ from frappe.social.doctype.energy_point_settings.energy_point_settings import ( is_energy_point_enabled, ) from frappe.translate import get_lang_dict -from frappe.utils import add_user_info, get_time_zone +from frappe.utils import add_user_info, cstr, get_time_zone from frappe.utils.change_log import get_versions from frappe.website.doctype.web_page_view.web_page_view import is_tracking_enabled @@ -68,7 +67,6 @@ def get_bootinfo(): bootinfo.home_folder = frappe.db.get_value("File", {"is_home_folder": 1}) bootinfo.navbar_settings = get_navbar_settings() bootinfo.notification_settings = get_notification_settings() - get_country_codes(bootinfo) set_time_zone(bootinfo) # ipinfo @@ -142,6 +140,10 @@ def get_allowed_reports(cache=False): return get_user_pages_or_reports("Report", cache=cache) +def get_allowed_report_names(cache=False) -> set[str]: + return {cstr(report) for report in get_allowed_reports(cache).keys() if report} + + def get_user_pages_or_reports(parent, cache=False): _cache = frappe.cache() @@ -389,11 +391,6 @@ def get_notification_settings(): return frappe.get_cached_doc("Notification Settings", frappe.session.user) -def get_country_codes(bootinfo): - country_codes = get_all() - bootinfo.country_codes = frappe._dict(country_codes) - - @frappe.whitelist() def get_link_title_doctypes(): dts = frappe.get_all("DocType", {"show_title_field_in_link": 1}) diff --git a/frappe/core/doctype/document_naming_settings/document_naming_settings.py b/frappe/core/doctype/document_naming_settings/document_naming_settings.py index 5680b6c823..ad50cb90aa 100644 --- a/frappe/core/doctype/document_naming_settings/document_naming_settings.py +++ b/frappe/core/doctype/document_naming_settings/document_naming_settings.py @@ -8,7 +8,6 @@ from frappe.core.doctype.doctype.doctype import validate_series from frappe.model.document import Document from frappe.model.naming import NamingSeries from frappe.permissions import get_doctypes_with_read -from frappe.utils import cint class NamingSeriesNotSetError(frappe.ValidationError): diff --git a/frappe/core/utils.py b/frappe/core/utils.py index 8581f30f89..b445257b7d 100644 --- a/frappe/core/utils.py +++ b/frappe/core/utils.py @@ -1,6 +1,8 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: MIT. See LICENSE +from markdownify import markdownify as md + import frappe @@ -86,3 +88,8 @@ def ljust_list(_list, length, fill_word=None): _list.extend([fill_word] * fill_length) return _list + + +def html2text(html, strip_links=False, wrap=True): + strip = ["a"] if strip_links else None + return md(html, heading_style="ATX", strip=strip, wrap=wrap) diff --git a/frappe/database/postgres/setup_db.py b/frappe/database/postgres/setup_db.py index 0a40e9eba7..7eee8081c0 100644 --- a/frappe/database/postgres/setup_db.py +++ b/frappe/database/postgres/setup_db.py @@ -116,5 +116,6 @@ def drop_user_and_database(db_name, root_login, root_password): "SELECT pg_terminate_backend (pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = %s", (db_name,), ) + root_conn.sql("end") root_conn.sql(f"DROP DATABASE IF EXISTS {db_name}") root_conn.sql(f"DROP USER IF EXISTS {db_name}") diff --git a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py index 1145873a09..75f230a901 100644 --- a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py +++ b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py @@ -6,7 +6,7 @@ import json import frappe from frappe import _ -from frappe.boot import get_allowed_reports +from frappe.boot import get_allowed_report_names from frappe.config import get_modules_from_all_apps_for_user from frappe.model.document import Document from frappe.model.naming import append_number_if_name_exists @@ -40,10 +40,7 @@ def get_permission_query_conditions(user): allowed_doctypes = [ frappe.db.escape(doctype) for doctype in frappe.permissions.get_doctypes_with_read() ] - allowed_reports = [ - frappe.db.escape(key) if type(key) == str else key.encode("UTF8") - for key in get_allowed_reports() - ] + allowed_reports = [frappe.db.escape(report) for report in get_allowed_report_names()] allowed_modules = [ frappe.db.escape(module.get("module_name")) for module in get_modules_from_all_apps_for_user() ] @@ -83,10 +80,7 @@ def has_permission(doc, ptype, user): return True if doc.chart_type == "Report": - allowed_reports = [ - key if type(key) == str else key.encode("UTF8") for key in get_allowed_reports() - ] - if doc.report_name in allowed_reports: + if doc.report_name in get_allowed_report_names(): return True else: allowed_doctypes = frappe.permissions.get_doctypes_with_read() diff --git a/frappe/desk/doctype/number_card/number_card.py b/frappe/desk/doctype/number_card/number_card.py index 1bffd68940..12a6105c4b 100644 --- a/frappe/desk/doctype/number_card/number_card.py +++ b/frappe/desk/doctype/number_card/number_card.py @@ -3,7 +3,7 @@ import frappe from frappe import _ -from frappe.boot import get_allowed_reports +from frappe.boot import get_allowed_report_names from frappe.config import get_modules_from_all_apps_for_user from frappe.model.document import Document from frappe.model.naming import append_number_if_name_exists @@ -91,10 +91,7 @@ def has_permission(doc, ptype, user): return True if doc.type == "Report": - allowed_reports = [ - key if type(key) == str else key.encode("UTF8") for key in get_allowed_reports() - ] - if doc.report_name in allowed_reports: + if doc.report_name in get_allowed_report_names(): return True else: allowed_doctypes = tuple(frappe.permissions.get_doctypes_with_read()) diff --git a/frappe/desk/form/document_follow.py b/frappe/desk/form/document_follow.py index 86bd712926..2e4bcedf5a 100644 --- a/frappe/desk/form/document_follow.py +++ b/frappe/desk/form/document_follow.py @@ -183,7 +183,7 @@ def get_version(doctype, doc_name, frequency, user): def get_comments(doctype, doc_name, frequency, user): - from html2text import html2text + from frappe.core.utils import html2text timeline = [] filters = get_filters("reference_name", doc_name, frequency, user) @@ -225,7 +225,7 @@ def get_follow_users(doctype, doc_name): def get_row_changed(row_changed, time, doctype, doc_name, v): - from html2text import html2text + from frappe.core.utils import html2text items = [] for d in row_changed: @@ -269,7 +269,7 @@ def get_added_row(added, time, doctype, doc_name, v): def get_field_changed(changed, time, doctype, doc_name, v): - from html2text import html2text + from frappe.core.utils import html2text items = [] for d in changed: diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py index 29c8ed08d3..3faa6d84c1 100644 --- a/frappe/desk/query_report.py +++ b/frappe/desk/query_report.py @@ -407,7 +407,7 @@ def build_xlsx_data(data, visible_idx, include_indentation, ignore_visible_idx=F continue label = column.get("label") fieldname = column.get("fieldname") - cell_value = row.get(fieldname, row.get(label, "")) + cell_value = cstr(row.get(fieldname, row.get(label, ""))) if cint(include_indentation) and "indent" in row and col_idx == 0: cell_value = (" " * cint(row["indent"])) + cstr(cell_value) row_data.append(cell_value) diff --git a/frappe/email/doctype/email_queue/email_queue.py b/frappe/email/doctype/email_queue/email_queue.py index 7b57adf6fb..221f3fbb31 100644 --- a/frappe/email/doctype/email_queue/email_queue.py +++ b/frappe/email/doctype/email_queue/email_queue.py @@ -8,11 +8,11 @@ import traceback from email.parser import Parser from email.policy import SMTPUTF8 -from html2text import html2text from rq.timeouts import JobTimeoutException import frappe from frappe import _, safe_encode, task +from frappe.core.utils import html2text from frappe.email.doctype.email_account.email_account import EmailAccount from frappe.email.email_body import add_attachment, get_email, get_formatted_html from frappe.email.queue import get_unsubcribed_url, get_unsubscribe_message diff --git a/frappe/email/email_body.py b/frappe/email/email_body.py index 3288ca2148..b493ca2cb5 100755 --- a/frappe/email/email_body.py +++ b/frappe/email/email_body.py @@ -332,12 +332,12 @@ class EMail: def set_header(self, key, value): if key in self.msg_root: + # delete key if found + # this is done because adding the same key doesn't override + # the existing key, rather appends another header with same key. del self.msg_root[key] - try: - self.msg_root[key] = value - except ValueError: - self.msg_root[key] = sanitize_email_header(value) + self.msg_root[key] = sanitize_email_header(value) def as_string(self): """validate, build message and convert to string""" @@ -580,8 +580,17 @@ def get_header(header=None): return email_header -def sanitize_email_header(str): - return str.replace("\r", "").replace("\n", "") +def sanitize_email_header(header: str): + """ + Removes all line boundaries in the headers. + + Email Policy (python's std) has some bugs in it which uses splitlines + and raises ValueError (ref: https://github.com/python/cpython/blob/main/Lib/email/policy.py#L143). + Hence removing all line boundaries while sanitization of headers to prevent such faliures. + The line boundaries which are removed can be found here: https://docs.python.org/3/library/stdtypes.html#str.splitlines + """ + + return "".join(header.splitlines()) def get_brand_logo(email_account): diff --git a/frappe/email/test_email_body.py b/frappe/email/test_email_body.py index 9f76ec6a59..3ff4245924 100644 --- a/frappe/email/test_email_body.py +++ b/frappe/email/test_email_body.py @@ -152,20 +152,19 @@ w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

Hey John Doe!

This is embedded image you asked for

""" - email_string = ( - get_email( - recipients=["test@example.com"], - sender="me@example.com", - subject="Test Subject", - content=email_html, - header=["Email Title", "green"], - ) - .as_string() - .replace("\r\n", "\n") - ) + email_string = get_email( + recipients=["test@example.com"], + sender="me@example.com", + subject="Test Subject\u2028, with line break, \nand Line feed \rand carriage return.", + content=email_html, + header=["Email Title", "green"], + ).as_string() # REDESIGN-TODO: Add style for indicators in email self.assertTrue("""""" in email_string) self.assertTrue("Email Title" in email_string) + self.assertIn( + "Subject: Test Subject, with line break, and Line feed and carriage return.", email_string + ) def test_get_email_header(self): html = get_header(["This is test", "orange"]) diff --git a/frappe/geo/country_info.json b/frappe/geo/country_info.json index 94d1f3ed37..ed44b1c7f8 100644 --- a/frappe/geo/country_info.json +++ b/frappe/geo/country_info.json @@ -842,7 +842,7 @@ "El Salvador": { "code": "sv", "currency": "USD", - "currency_fraction": "Centavo", + "currency_fraction": "Cent", "currency_fraction_units": 100, "smallest_currency_fraction_value": 0.01, "currency_name": "Dolar estadounidense", diff --git a/frappe/geo/doctype/currency/test_currency.py b/frappe/geo/doctype/currency/test_currency.py index f93a452462..b02dd4258c 100644 --- a/frappe/geo/doctype/currency/test_currency.py +++ b/frappe/geo/doctype/currency/test_currency.py @@ -4,5 +4,10 @@ # pre loaded import frappe +from frappe.tests.utils import FrappeTestCase -test_records = frappe.get_test_records("Currency") + +class TestUser(FrappeTestCase): + def test_default_currency_on_setup(self): + usd = frappe.get_doc("Currency", "USD") + self.assertDocumentEqual({"enabled": 1, "fraction": "Cent"}, usd) diff --git a/frappe/integrations/frappe_providers/frappecloud.py b/frappe/integrations/frappe_providers/frappecloud.py index 64aa847a0e..bae811d41d 100644 --- a/frappe/integrations/frappe_providers/frappecloud.py +++ b/frappe/integrations/frappe_providers/frappecloud.py @@ -1,8 +1,8 @@ import click import requests -from html2text import html2text import frappe +from frappe.core.utils import html2text def frappecloud_migrator(local_site): diff --git a/frappe/monitor.py b/frappe/monitor.py index e151944b1f..8d5391cb77 100644 --- a/frappe/monitor.py +++ b/frappe/monitor.py @@ -30,6 +30,8 @@ def log_file(): class Monitor: + __slots__ = ("data",) + def __init__(self, transaction_type, method, kwargs): try: self.data = frappe._dict( diff --git a/frappe/permissions.py b/frappe/permissions.py index 5e56071123..acbdf76989 100644 --- a/frappe/permissions.py +++ b/frappe/permissions.py @@ -6,7 +6,7 @@ import frappe import frappe.share from frappe import _, msgprint from frappe.query_builder import DocType -from frappe.utils import cint +from frappe.utils import cint, cstr rights = ( "select", @@ -360,9 +360,7 @@ def has_controller_permissions(doc, ptype, user=None): def get_doctypes_with_read(): - return list( - {p.parent if type(p.parent) == str else p.parent.encode("UTF8") for p in get_valid_perms()} - ) + return list({cstr(p.parent) for p in get_valid_perms() if p.parent}) def get_valid_perms(doctype=None, user=None): diff --git a/frappe/public/css/font-awesome.css b/frappe/public/css/font-awesome.css deleted file mode 100644 index afe8a7f458..0000000000 --- a/frappe/public/css/font-awesome.css +++ /dev/null @@ -1,2337 +0,0 @@ -/*! - * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */ -/* FONT PATH - * -------------------------- */ -@font-face { - font-family: 'FontAwesome'; - src: url('/assets/frappe/css/fonts/fontawesome/fontawesome-webfont.eot?v=4.7.0'); - src: url('/assets/frappe/css/fonts/fontawesome/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'), url('/assets/frappe/css/fonts/fontawesome/fontawesome-webfont.woff2?v=4.7.0') format('woff2'), url('/assets/frappe/css/fonts/fontawesome/fontawesome-webfont.woff?v=4.7.0') format('woff'), url('/assets/frappe/css/fonts/fontawesome/fontawesome-webfont.ttf?v=4.7.0') format('truetype'), url('/assets/frappe/css/fonts/fontawesome/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg'); - font-weight: normal; - font-style: normal; -} -.fa { - display: inline-block; - font: normal normal normal 14px/1 FontAwesome; - font-size: inherit; - text-rendering: auto; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} -/* makes the font 33% larger relative to the icon container */ -.fa-lg { - font-size: 1.33333333em; - line-height: 0.75em; - vertical-align: -15%; -} -.fa-2x { - font-size: 2em; -} -.fa-3x { - font-size: 3em; -} -.fa-4x { - font-size: 4em; -} -.fa-5x { - font-size: 5em; -} -.fa-fw { - width: 1.28571429em; - text-align: center; -} -.fa-ul { - padding-left: 0; - margin-left: 2.14285714em; - list-style-type: none; -} -.fa-ul > li { - position: relative; -} -.fa-li { - position: absolute; - left: -2.14285714em; - width: 2.14285714em; - top: 0.14285714em; - text-align: center; -} -.fa-li.fa-lg { - left: -1.85714286em; -} -.fa-border { - padding: .2em .25em .15em; - border: solid 0.08em #eeeeee; - border-radius: .1em; -} -.fa-pull-left { - float: left; -} -.fa-pull-right { - float: right; -} -.fa.fa-pull-left { - margin-right: .3em; -} -.fa.fa-pull-right { - margin-left: .3em; -} -/* Deprecated as of 4.4.0 */ -.pull-right { - float: right; -} -.pull-left { - float: left; -} -.fa.pull-left { - margin-right: .3em; -} -.fa.pull-right { - margin-left: .3em; -} -.fa-spin { - -webkit-animation: fa-spin 2s infinite linear; - animation: fa-spin 2s infinite linear; -} -.fa-pulse { - -webkit-animation: fa-spin 1s infinite steps(8); - animation: fa-spin 1s infinite steps(8); -} -@-webkit-keyframes fa-spin { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(359deg); - transform: rotate(359deg); - } -} -@keyframes fa-spin { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(359deg); - transform: rotate(359deg); - } -} -.fa-rotate-90 { - -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)"; - -webkit-transform: rotate(90deg); - -ms-transform: rotate(90deg); - transform: rotate(90deg); -} -.fa-rotate-180 { - -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)"; - -webkit-transform: rotate(180deg); - -ms-transform: rotate(180deg); - transform: rotate(180deg); -} -.fa-rotate-270 { - -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)"; - -webkit-transform: rotate(270deg); - -ms-transform: rotate(270deg); - transform: rotate(270deg); -} -.fa-flip-horizontal { - -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)"; - -webkit-transform: scale(-1, 1); - -ms-transform: scale(-1, 1); - transform: scale(-1, 1); -} -.fa-flip-vertical { - -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"; - -webkit-transform: scale(1, -1); - -ms-transform: scale(1, -1); - transform: scale(1, -1); -} -:root .fa-rotate-90, -:root .fa-rotate-180, -:root .fa-rotate-270, -:root .fa-flip-horizontal, -:root .fa-flip-vertical { - filter: none; -} -.fa-stack { - position: relative; - display: inline-block; - width: 2em; - height: 2em; - line-height: 2em; - vertical-align: middle; -} -.fa-stack-1x, -.fa-stack-2x { - position: absolute; - left: 0; - width: 100%; - text-align: center; -} -.fa-stack-1x { - line-height: inherit; -} -.fa-stack-2x { - font-size: 2em; -} -.fa-inverse { - color: #ffffff; -} -/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen - readers do not read off random characters that represent icons */ -.fa-glass:before { - content: "\f000"; -} -.fa-music:before { - content: "\f001"; -} -.fa-search:before { - content: "\f002"; -} -.fa-envelope-o:before { - content: "\f003"; -} -.fa-heart:before { - content: "\f004"; -} -.fa-star:before { - content: "\f005"; -} -.fa-star-o:before { - content: "\f006"; -} -.fa-user:before { - content: "\f007"; -} -.fa-film:before { - content: "\f008"; -} -.fa-th-large:before { - content: "\f009"; -} -.fa-th:before { - content: "\f00a"; -} -.fa-th-list:before { - content: "\f00b"; -} -.fa-check:before { - content: "\f00c"; -} -.fa-remove:before, -.fa-close:before, -.fa-times:before { - content: "\f00d"; -} -.fa-search-plus:before { - content: "\f00e"; -} -.fa-search-minus:before { - content: "\f010"; -} -.fa-power-off:before { - content: "\f011"; -} -.fa-signal:before { - content: "\f012"; -} -.fa-gear:before, -.fa-cog:before { - content: "\f013"; -} -.fa-trash-o:before { - content: "\f014"; -} -.fa-home:before { - content: "\f015"; -} -.fa-file-o:before { - content: "\f016"; -} -.fa-clock-o:before { - content: "\f017"; -} -.fa-road:before { - content: "\f018"; -} -.fa-download:before { - content: "\f019"; -} -.fa-arrow-circle-o-down:before { - content: "\f01a"; -} -.fa-arrow-circle-o-up:before { - content: "\f01b"; -} -.fa-inbox:before { - content: "\f01c"; -} -.fa-play-circle-o:before { - content: "\f01d"; -} -.fa-rotate-right:before, -.fa-repeat:before { - content: "\f01e"; -} -.fa-refresh:before { - content: "\f021"; -} -.fa-list-alt:before { - content: "\f022"; -} -.fa-lock:before { - content: "\f023"; -} -.fa-flag:before { - content: "\f024"; -} -.fa-headphones:before { - content: "\f025"; -} -.fa-volume-off:before { - content: "\f026"; -} -.fa-volume-down:before { - content: "\f027"; -} -.fa-volume-up:before { - content: "\f028"; -} -.fa-qrcode:before { - content: "\f029"; -} -.fa-barcode:before { - content: "\f02a"; -} -.fa-tag:before { - content: "\f02b"; -} -.fa-tags:before { - content: "\f02c"; -} -.fa-book:before { - content: "\f02d"; -} -.fa-bookmark:before { - content: "\f02e"; -} -.fa-print:before { - content: "\f02f"; -} -.fa-camera:before { - content: "\f030"; -} -.fa-font:before { - content: "\f031"; -} -.fa-bold:before { - content: "\f032"; -} -.fa-italic:before { - content: "\f033"; -} -.fa-text-height:before { - content: "\f034"; -} -.fa-text-width:before { - content: "\f035"; -} -.fa-align-left:before { - content: "\f036"; -} -.fa-align-center:before { - content: "\f037"; -} -.fa-align-right:before { - content: "\f038"; -} -.fa-align-justify:before { - content: "\f039"; -} -.fa-list:before { - content: "\f03a"; -} -.fa-dedent:before, -.fa-outdent:before { - content: "\f03b"; -} -.fa-indent:before { - content: "\f03c"; -} -.fa-video-camera:before { - content: "\f03d"; -} -.fa-photo:before, -.fa-image:before, -.fa-picture-o:before { - content: "\f03e"; -} -.fa-pencil:before { - content: "\f040"; -} -.fa-map-marker:before { - content: "\f041"; -} -.fa-adjust:before { - content: "\f042"; -} -.fa-tint:before { - content: "\f043"; -} -.fa-edit:before, -.fa-pencil-square-o:before { - content: "\f044"; -} -.fa-share-square-o:before { - content: "\f045"; -} -.fa-check-square-o:before { - content: "\f046"; -} -.fa-arrows:before { - content: "\f047"; -} -.fa-step-backward:before { - content: "\f048"; -} -.fa-fast-backward:before { - content: "\f049"; -} -.fa-backward:before { - content: "\f04a"; -} -.fa-play:before { - content: "\f04b"; -} -.fa-pause:before { - content: "\f04c"; -} -.fa-stop:before { - content: "\f04d"; -} -.fa-forward:before { - content: "\f04e"; -} -.fa-fast-forward:before { - content: "\f050"; -} -.fa-step-forward:before { - content: "\f051"; -} -.fa-eject:before { - content: "\f052"; -} -.fa-chevron-left:before { - content: "\f053"; -} -.fa-chevron-right:before { - content: "\f054"; -} -.fa-plus-circle:before { - content: "\f055"; -} -.fa-minus-circle:before { - content: "\f056"; -} -.fa-times-circle:before { - content: "\f057"; -} -.fa-check-circle:before { - content: "\f058"; -} -.fa-question-circle:before { - content: "\f059"; -} -.fa-info-circle:before { - content: "\f05a"; -} -.fa-crosshairs:before { - content: "\f05b"; -} -.fa-times-circle-o:before { - content: "\f05c"; -} -.fa-check-circle-o:before { - content: "\f05d"; -} -.fa-ban:before { - content: "\f05e"; -} -.fa-arrow-left:before { - content: "\f060"; -} -.fa-arrow-right:before { - content: "\f061"; -} -.fa-arrow-up:before { - content: "\f062"; -} -.fa-arrow-down:before { - content: "\f063"; -} -.fa-mail-forward:before, -.fa-share:before { - content: "\f064"; -} -.fa-expand:before { - content: "\f065"; -} -.fa-compress:before { - content: "\f066"; -} -.fa-plus:before { - content: "\f067"; -} -.fa-minus:before { - content: "\f068"; -} -.fa-asterisk:before { - content: "\f069"; -} -.fa-exclamation-circle:before { - content: "\f06a"; -} -.fa-gift:before { - content: "\f06b"; -} -.fa-leaf:before { - content: "\f06c"; -} -.fa-fire:before { - content: "\f06d"; -} -.fa-eye:before { - content: "\f06e"; -} -.fa-eye-slash:before { - content: "\f070"; -} -.fa-warning:before, -.fa-exclamation-triangle:before { - content: "\f071"; -} -.fa-plane:before { - content: "\f072"; -} -.fa-calendar:before { - content: "\f073"; -} -.fa-random:before { - content: "\f074"; -} -.fa-comment:before { - content: "\f075"; -} -.fa-magnet:before { - content: "\f076"; -} -.fa-chevron-up:before { - content: "\f077"; -} -.fa-chevron-down:before { - content: "\f078"; -} -.fa-retweet:before { - content: "\f079"; -} -.fa-shopping-cart:before { - content: "\f07a"; -} -.fa-folder:before { - content: "\f07b"; -} -.fa-folder-open:before { - content: "\f07c"; -} -.fa-arrows-v:before { - content: "\f07d"; -} -.fa-arrows-h:before { - content: "\f07e"; -} -.fa-bar-chart-o:before, -.fa-bar-chart:before { - content: "\f080"; -} -.fa-twitter-square:before { - content: "\f081"; -} -.fa-facebook-square:before { - content: "\f082"; -} -.fa-camera-retro:before { - content: "\f083"; -} -.fa-key:before { - content: "\f084"; -} -.fa-gears:before, -.fa-cogs:before { - content: "\f085"; -} -.fa-comments:before { - content: "\f086"; -} -.fa-thumbs-o-up:before { - content: "\f087"; -} -.fa-thumbs-o-down:before { - content: "\f088"; -} -.fa-star-half:before { - content: "\f089"; -} -.fa-heart-o:before { - content: "\f08a"; -} -.fa-sign-out:before { - content: "\f08b"; -} -.fa-linkedin-square:before { - content: "\f08c"; -} -.fa-thumb-tack:before { - content: "\f08d"; -} -.fa-external-link:before { - content: "\f08e"; -} -.fa-sign-in:before { - content: "\f090"; -} -.fa-trophy:before { - content: "\f091"; -} -.fa-github-square:before { - content: "\f092"; -} -.fa-upload:before { - content: "\f093"; -} -.fa-lemon-o:before { - content: "\f094"; -} -.fa-phone:before { - content: "\f095"; -} -.fa-square-o:before { - content: "\f096"; -} -.fa-bookmark-o:before { - content: "\f097"; -} -.fa-phone-square:before { - content: "\f098"; -} -.fa-twitter:before { - content: "\f099"; -} -.fa-facebook-f:before, -.fa-facebook:before { - content: "\f09a"; -} -.fa-github:before { - content: "\f09b"; -} -.fa-unlock:before { - content: "\f09c"; -} -.fa-credit-card:before { - content: "\f09d"; -} -.fa-feed:before, -.fa-rss:before { - content: "\f09e"; -} -.fa-hdd-o:before { - content: "\f0a0"; -} -.fa-bullhorn:before { - content: "\f0a1"; -} -.fa-bell:before { - content: "\f0f3"; -} -.fa-certificate:before { - content: "\f0a3"; -} -.fa-hand-o-right:before { - content: "\f0a4"; -} -.fa-hand-o-left:before { - content: "\f0a5"; -} -.fa-hand-o-up:before { - content: "\f0a6"; -} -.fa-hand-o-down:before { - content: "\f0a7"; -} -.fa-arrow-circle-left:before { - content: "\f0a8"; -} -.fa-arrow-circle-right:before { - content: "\f0a9"; -} -.fa-arrow-circle-up:before { - content: "\f0aa"; -} -.fa-arrow-circle-down:before { - content: "\f0ab"; -} -.fa-globe:before { - content: "\f0ac"; -} -.fa-wrench:before { - content: "\f0ad"; -} -.fa-tasks:before { - content: "\f0ae"; -} -.fa-filter:before { - content: "\f0b0"; -} -.fa-briefcase:before { - content: "\f0b1"; -} -.fa-arrows-alt:before { - content: "\f0b2"; -} -.fa-group:before, -.fa-users:before { - content: "\f0c0"; -} -.fa-chain:before, -.fa-link:before { - content: "\f0c1"; -} -.fa-cloud:before { - content: "\f0c2"; -} -.fa-flask:before { - content: "\f0c3"; -} -.fa-cut:before, -.fa-scissors:before { - content: "\f0c4"; -} -.fa-copy:before, -.fa-files-o:before { - content: "\f0c5"; -} -.fa-paperclip:before { - content: "\f0c6"; -} -.fa-save:before, -.fa-floppy-o:before { - content: "\f0c7"; -} -.fa-square:before { - content: "\f0c8"; -} -.fa-navicon:before, -.fa-reorder:before, -.fa-bars:before { - content: "\f0c9"; -} -.fa-list-ul:before { - content: "\f0ca"; -} -.fa-list-ol:before { - content: "\f0cb"; -} -.fa-strikethrough:before { - content: "\f0cc"; -} -.fa-underline:before { - content: "\f0cd"; -} -.fa-table:before { - content: "\f0ce"; -} -.fa-magic:before { - content: "\f0d0"; -} -.fa-truck:before { - content: "\f0d1"; -} -.fa-pinterest:before { - content: "\f0d2"; -} -.fa-pinterest-square:before { - content: "\f0d3"; -} -.fa-google-plus-square:before { - content: "\f0d4"; -} -.fa-google-plus:before { - content: "\f0d5"; -} -.fa-money:before { - content: "\f0d6"; -} -.fa-caret-down:before { - content: "\f0d7"; -} -.fa-caret-up:before { - content: "\f0d8"; -} -.fa-caret-left:before { - content: "\f0d9"; -} -.fa-caret-right:before { - content: "\f0da"; -} -.fa-columns:before { - content: "\f0db"; -} -.fa-unsorted:before, -.fa-sort:before { - content: "\f0dc"; -} -.fa-sort-down:before, -.fa-sort-desc:before { - content: "\f0dd"; -} -.fa-sort-up:before, -.fa-sort-asc:before { - content: "\f0de"; -} -.fa-envelope:before { - content: "\f0e0"; -} -.fa-linkedin:before { - content: "\f0e1"; -} -.fa-rotate-left:before, -.fa-undo:before { - content: "\f0e2"; -} -.fa-legal:before, -.fa-gavel:before { - content: "\f0e3"; -} -.fa-dashboard:before, -.fa-tachometer:before { - content: "\f0e4"; -} -.fa-comment-o:before { - content: "\f0e5"; -} -.fa-comments-o:before { - content: "\f0e6"; -} -.fa-flash:before, -.fa-bolt:before { - content: "\f0e7"; -} -.fa-sitemap:before { - content: "\f0e8"; -} -.fa-umbrella:before { - content: "\f0e9"; -} -.fa-paste:before, -.fa-clipboard:before { - content: "\f0ea"; -} -.fa-lightbulb-o:before { - content: "\f0eb"; -} -.fa-exchange:before { - content: "\f0ec"; -} -.fa-cloud-download:before { - content: "\f0ed"; -} -.fa-cloud-upload:before { - content: "\f0ee"; -} -.fa-user-md:before { - content: "\f0f0"; -} -.fa-stethoscope:before { - content: "\f0f1"; -} -.fa-suitcase:before { - content: "\f0f2"; -} -.fa-bell-o:before { - content: "\f0a2"; -} -.fa-coffee:before { - content: "\f0f4"; -} -.fa-cutlery:before { - content: "\f0f5"; -} -.fa-file-text-o:before { - content: "\f0f6"; -} -.fa-building-o:before { - content: "\f0f7"; -} -.fa-hospital-o:before { - content: "\f0f8"; -} -.fa-ambulance:before { - content: "\f0f9"; -} -.fa-medkit:before { - content: "\f0fa"; -} -.fa-fighter-jet:before { - content: "\f0fb"; -} -.fa-beer:before { - content: "\f0fc"; -} -.fa-h-square:before { - content: "\f0fd"; -} -.fa-plus-square:before { - content: "\f0fe"; -} -.fa-angle-double-left:before { - content: "\f100"; -} -.fa-angle-double-right:before { - content: "\f101"; -} -.fa-angle-double-up:before { - content: "\f102"; -} -.fa-angle-double-down:before { - content: "\f103"; -} -.fa-angle-left:before { - content: "\f104"; -} -.fa-angle-right:before { - content: "\f105"; -} -.fa-angle-up:before { - content: "\f106"; -} -.fa-angle-down:before { - content: "\f107"; -} -.fa-desktop:before { - content: "\f108"; -} -.fa-laptop:before { - content: "\f109"; -} -.fa-tablet:before { - content: "\f10a"; -} -.fa-mobile-phone:before, -.fa-mobile:before { - content: "\f10b"; -} -.fa-circle-o:before { - content: "\f10c"; -} -.fa-quote-left:before { - content: "\f10d"; -} -.fa-quote-right:before { - content: "\f10e"; -} -.fa-spinner:before { - content: "\f110"; -} -.fa-circle:before { - content: "\f111"; -} -.fa-mail-reply:before, -.fa-reply:before { - content: "\f112"; -} -.fa-github-alt:before { - content: "\f113"; -} -.fa-folder-o:before { - content: "\f114"; -} -.fa-folder-open-o:before { - content: "\f115"; -} -.fa-smile-o:before { - content: "\f118"; -} -.fa-frown-o:before { - content: "\f119"; -} -.fa-meh-o:before { - content: "\f11a"; -} -.fa-gamepad:before { - content: "\f11b"; -} -.fa-keyboard-o:before { - content: "\f11c"; -} -.fa-flag-o:before { - content: "\f11d"; -} -.fa-flag-checkered:before { - content: "\f11e"; -} -.fa-terminal:before { - content: "\f120"; -} -.fa-code:before { - content: "\f121"; -} -.fa-mail-reply-all:before, -.fa-reply-all:before { - content: "\f122"; -} -.fa-star-half-empty:before, -.fa-star-half-full:before, -.fa-star-half-o:before { - content: "\f123"; -} -.fa-location-arrow:before { - content: "\f124"; -} -.fa-crop:before { - content: "\f125"; -} -.fa-code-fork:before { - content: "\f126"; -} -.fa-unlink:before, -.fa-chain-broken:before { - content: "\f127"; -} -.fa-question:before { - content: "\f128"; -} -.fa-info:before { - content: "\f129"; -} -.fa-exclamation:before { - content: "\f12a"; -} -.fa-superscript:before { - content: "\f12b"; -} -.fa-subscript:before { - content: "\f12c"; -} -.fa-eraser:before { - content: "\f12d"; -} -.fa-puzzle-piece:before { - content: "\f12e"; -} -.fa-microphone:before { - content: "\f130"; -} -.fa-microphone-slash:before { - content: "\f131"; -} -.fa-shield:before { - content: "\f132"; -} -.fa-calendar-o:before { - content: "\f133"; -} -.fa-fire-extinguisher:before { - content: "\f134"; -} -.fa-rocket:before { - content: "\f135"; -} -.fa-maxcdn:before { - content: "\f136"; -} -.fa-chevron-circle-left:before { - content: "\f137"; -} -.fa-chevron-circle-right:before { - content: "\f138"; -} -.fa-chevron-circle-up:before { - content: "\f139"; -} -.fa-chevron-circle-down:before { - content: "\f13a"; -} -.fa-html5:before { - content: "\f13b"; -} -.fa-css3:before { - content: "\f13c"; -} -.fa-anchor:before { - content: "\f13d"; -} -.fa-unlock-alt:before { - content: "\f13e"; -} -.fa-bullseye:before { - content: "\f140"; -} -.fa-ellipsis-h:before { - content: "\f141"; -} -.fa-ellipsis-v:before { - content: "\f142"; -} -.fa-rss-square:before { - content: "\f143"; -} -.fa-play-circle:before { - content: "\f144"; -} -.fa-ticket:before { - content: "\f145"; -} -.fa-minus-square:before { - content: "\f146"; -} -.fa-minus-square-o:before { - content: "\f147"; -} -.fa-level-up:before { - content: "\f148"; -} -.fa-level-down:before { - content: "\f149"; -} -.fa-check-square:before { - content: "\f14a"; -} -.fa-pencil-square:before { - content: "\f14b"; -} -.fa-external-link-square:before { - content: "\f14c"; -} -.fa-share-square:before { - content: "\f14d"; -} -.fa-compass:before { - content: "\f14e"; -} -.fa-toggle-down:before, -.fa-caret-square-o-down:before { - content: "\f150"; -} -.fa-toggle-up:before, -.fa-caret-square-o-up:before { - content: "\f151"; -} -.fa-toggle-right:before, -.fa-caret-square-o-right:before { - content: "\f152"; -} -.fa-euro:before, -.fa-eur:before { - content: "\f153"; -} -.fa-gbp:before { - content: "\f154"; -} -.fa-dollar:before, -.fa-usd:before { - content: "\f155"; -} -.fa-rupee:before, -.fa-inr:before { - content: "\f156"; -} -.fa-cny:before, -.fa-rmb:before, -.fa-yen:before, -.fa-jpy:before { - content: "\f157"; -} -.fa-ruble:before, -.fa-rouble:before, -.fa-rub:before { - content: "\f158"; -} -.fa-won:before, -.fa-krw:before { - content: "\f159"; -} -.fa-bitcoin:before, -.fa-btc:before { - content: "\f15a"; -} -.fa-file:before { - content: "\f15b"; -} -.fa-file-text:before { - content: "\f15c"; -} -.fa-sort-alpha-asc:before { - content: "\f15d"; -} -.fa-sort-alpha-desc:before { - content: "\f15e"; -} -.fa-sort-amount-asc:before { - content: "\f160"; -} -.fa-sort-amount-desc:before { - content: "\f161"; -} -.fa-sort-numeric-asc:before { - content: "\f162"; -} -.fa-sort-numeric-desc:before { - content: "\f163"; -} -.fa-thumbs-up:before { - content: "\f164"; -} -.fa-thumbs-down:before { - content: "\f165"; -} -.fa-youtube-square:before { - content: "\f166"; -} -.fa-youtube:before { - content: "\f167"; -} -.fa-xing:before { - content: "\f168"; -} -.fa-xing-square:before { - content: "\f169"; -} -.fa-youtube-play:before { - content: "\f16a"; -} -.fa-dropbox:before { - content: "\f16b"; -} -.fa-stack-overflow:before { - content: "\f16c"; -} -.fa-instagram:before { - content: "\f16d"; -} -.fa-flickr:before { - content: "\f16e"; -} -.fa-adn:before { - content: "\f170"; -} -.fa-bitbucket:before { - content: "\f171"; -} -.fa-bitbucket-square:before { - content: "\f172"; -} -.fa-tumblr:before { - content: "\f173"; -} -.fa-tumblr-square:before { - content: "\f174"; -} -.fa-long-arrow-down:before { - content: "\f175"; -} -.fa-long-arrow-up:before { - content: "\f176"; -} -.fa-long-arrow-left:before { - content: "\f177"; -} -.fa-long-arrow-right:before { - content: "\f178"; -} -.fa-apple:before { - content: "\f179"; -} -.fa-windows:before { - content: "\f17a"; -} -.fa-android:before { - content: "\f17b"; -} -.fa-linux:before { - content: "\f17c"; -} -.fa-dribbble:before { - content: "\f17d"; -} -.fa-skype:before { - content: "\f17e"; -} -.fa-foursquare:before { - content: "\f180"; -} -.fa-trello:before { - content: "\f181"; -} -.fa-female:before { - content: "\f182"; -} -.fa-male:before { - content: "\f183"; -} -.fa-gittip:before, -.fa-gratipay:before { - content: "\f184"; -} -.fa-sun-o:before { - content: "\f185"; -} -.fa-moon-o:before { - content: "\f186"; -} -.fa-archive:before { - content: "\f187"; -} -.fa-bug:before { - content: "\f188"; -} -.fa-vk:before { - content: "\f189"; -} -.fa-weibo:before { - content: "\f18a"; -} -.fa-renren:before { - content: "\f18b"; -} -.fa-pagelines:before { - content: "\f18c"; -} -.fa-stack-exchange:before { - content: "\f18d"; -} -.fa-arrow-circle-o-right:before { - content: "\f18e"; -} -.fa-arrow-circle-o-left:before { - content: "\f190"; -} -.fa-toggle-left:before, -.fa-caret-square-o-left:before { - content: "\f191"; -} -.fa-dot-circle-o:before { - content: "\f192"; -} -.fa-wheelchair:before { - content: "\f193"; -} -.fa-vimeo-square:before { - content: "\f194"; -} -.fa-turkish-lira:before, -.fa-try:before { - content: "\f195"; -} -.fa-plus-square-o:before { - content: "\f196"; -} -.fa-space-shuttle:before { - content: "\f197"; -} -.fa-slack:before { - content: "\f198"; -} -.fa-envelope-square:before { - content: "\f199"; -} -.fa-wordpress:before { - content: "\f19a"; -} -.fa-openid:before { - content: "\f19b"; -} -.fa-institution:before, -.fa-bank:before, -.fa-university:before { - content: "\f19c"; -} -.fa-mortar-board:before, -.fa-graduation-cap:before { - content: "\f19d"; -} -.fa-yahoo:before { - content: "\f19e"; -} -.fa-google:before { - content: "\f1a0"; -} -.fa-reddit:before { - content: "\f1a1"; -} -.fa-reddit-square:before { - content: "\f1a2"; -} -.fa-stumbleupon-circle:before { - content: "\f1a3"; -} -.fa-stumbleupon:before { - content: "\f1a4"; -} -.fa-delicious:before { - content: "\f1a5"; -} -.fa-digg:before { - content: "\f1a6"; -} -.fa-pied-piper-pp:before { - content: "\f1a7"; -} -.fa-pied-piper-alt:before { - content: "\f1a8"; -} -.fa-drupal:before { - content: "\f1a9"; -} -.fa-joomla:before { - content: "\f1aa"; -} -.fa-language:before { - content: "\f1ab"; -} -.fa-fax:before { - content: "\f1ac"; -} -.fa-building:before { - content: "\f1ad"; -} -.fa-child:before { - content: "\f1ae"; -} -.fa-paw:before { - content: "\f1b0"; -} -.fa-spoon:before { - content: "\f1b1"; -} -.fa-cube:before { - content: "\f1b2"; -} -.fa-cubes:before { - content: "\f1b3"; -} -.fa-behance:before { - content: "\f1b4"; -} -.fa-behance-square:before { - content: "\f1b5"; -} -.fa-steam:before { - content: "\f1b6"; -} -.fa-steam-square:before { - content: "\f1b7"; -} -.fa-recycle:before { - content: "\f1b8"; -} -.fa-automobile:before, -.fa-car:before { - content: "\f1b9"; -} -.fa-cab:before, -.fa-taxi:before { - content: "\f1ba"; -} -.fa-tree:before { - content: "\f1bb"; -} -.fa-spotify:before { - content: "\f1bc"; -} -.fa-deviantart:before { - content: "\f1bd"; -} -.fa-soundcloud:before { - content: "\f1be"; -} -.fa-database:before { - content: "\f1c0"; -} -.fa-file-pdf-o:before { - content: "\f1c1"; -} -.fa-file-word-o:before { - content: "\f1c2"; -} -.fa-file-excel-o:before { - content: "\f1c3"; -} -.fa-file-powerpoint-o:before { - content: "\f1c4"; -} -.fa-file-photo-o:before, -.fa-file-picture-o:before, -.fa-file-image-o:before { - content: "\f1c5"; -} -.fa-file-zip-o:before, -.fa-file-archive-o:before { - content: "\f1c6"; -} -.fa-file-sound-o:before, -.fa-file-audio-o:before { - content: "\f1c7"; -} -.fa-file-movie-o:before, -.fa-file-video-o:before { - content: "\f1c8"; -} -.fa-file-code-o:before { - content: "\f1c9"; -} -.fa-vine:before { - content: "\f1ca"; -} -.fa-codepen:before { - content: "\f1cb"; -} -.fa-jsfiddle:before { - content: "\f1cc"; -} -.fa-life-bouy:before, -.fa-life-buoy:before, -.fa-life-saver:before, -.fa-support:before, -.fa-life-ring:before { - content: "\f1cd"; -} -.fa-circle-o-notch:before { - content: "\f1ce"; -} -.fa-ra:before, -.fa-resistance:before, -.fa-rebel:before { - content: "\f1d0"; -} -.fa-ge:before, -.fa-empire:before { - content: "\f1d1"; -} -.fa-git-square:before { - content: "\f1d2"; -} -.fa-git:before { - content: "\f1d3"; -} -.fa-y-combinator-square:before, -.fa-yc-square:before, -.fa-hacker-news:before { - content: "\f1d4"; -} -.fa-tencent-weibo:before { - content: "\f1d5"; -} -.fa-qq:before { - content: "\f1d6"; -} -.fa-wechat:before, -.fa-weixin:before { - content: "\f1d7"; -} -.fa-send:before, -.fa-paper-plane:before { - content: "\f1d8"; -} -.fa-send-o:before, -.fa-paper-plane-o:before { - content: "\f1d9"; -} -.fa-history:before { - content: "\f1da"; -} -.fa-circle-thin:before { - content: "\f1db"; -} -.fa-header:before { - content: "\f1dc"; -} -.fa-paragraph:before { - content: "\f1dd"; -} -.fa-sliders:before { - content: "\f1de"; -} -.fa-share-alt:before { - content: "\f1e0"; -} -.fa-share-alt-square:before { - content: "\f1e1"; -} -.fa-bomb:before { - content: "\f1e2"; -} -.fa-soccer-ball-o:before, -.fa-futbol-o:before { - content: "\f1e3"; -} -.fa-tty:before { - content: "\f1e4"; -} -.fa-binoculars:before { - content: "\f1e5"; -} -.fa-plug:before { - content: "\f1e6"; -} -.fa-slideshare:before { - content: "\f1e7"; -} -.fa-twitch:before { - content: "\f1e8"; -} -.fa-yelp:before { - content: "\f1e9"; -} -.fa-newspaper-o:before { - content: "\f1ea"; -} -.fa-wifi:before { - content: "\f1eb"; -} -.fa-calculator:before { - content: "\f1ec"; -} -.fa-paypal:before { - content: "\f1ed"; -} -.fa-google-wallet:before { - content: "\f1ee"; -} -.fa-cc-visa:before { - content: "\f1f0"; -} -.fa-cc-mastercard:before { - content: "\f1f1"; -} -.fa-cc-discover:before { - content: "\f1f2"; -} -.fa-cc-amex:before { - content: "\f1f3"; -} -.fa-cc-paypal:before { - content: "\f1f4"; -} -.fa-cc-stripe:before { - content: "\f1f5"; -} -.fa-bell-slash:before { - content: "\f1f6"; -} -.fa-bell-slash-o:before { - content: "\f1f7"; -} -.fa-trash:before { - content: "\f1f8"; -} -.fa-copyright:before { - content: "\f1f9"; -} -.fa-at:before { - content: "\f1fa"; -} -.fa-eyedropper:before { - content: "\f1fb"; -} -.fa-paint-brush:before { - content: "\f1fc"; -} -.fa-birthday-cake:before { - content: "\f1fd"; -} -.fa-area-chart:before { - content: "\f1fe"; -} -.fa-pie-chart:before { - content: "\f200"; -} -.fa-line-chart:before { - content: "\f201"; -} -.fa-lastfm:before { - content: "\f202"; -} -.fa-lastfm-square:before { - content: "\f203"; -} -.fa-toggle-off:before { - content: "\f204"; -} -.fa-toggle-on:before { - content: "\f205"; -} -.fa-bicycle:before { - content: "\f206"; -} -.fa-bus:before { - content: "\f207"; -} -.fa-ioxhost:before { - content: "\f208"; -} -.fa-angellist:before { - content: "\f209"; -} -.fa-cc:before { - content: "\f20a"; -} -.fa-shekel:before, -.fa-sheqel:before, -.fa-ils:before { - content: "\f20b"; -} -.fa-meanpath:before { - content: "\f20c"; -} -.fa-buysellads:before { - content: "\f20d"; -} -.fa-connectdevelop:before { - content: "\f20e"; -} -.fa-dashcube:before { - content: "\f210"; -} -.fa-forumbee:before { - content: "\f211"; -} -.fa-leanpub:before { - content: "\f212"; -} -.fa-sellsy:before { - content: "\f213"; -} -.fa-shirtsinbulk:before { - content: "\f214"; -} -.fa-simplybuilt:before { - content: "\f215"; -} -.fa-skyatlas:before { - content: "\f216"; -} -.fa-cart-plus:before { - content: "\f217"; -} -.fa-cart-arrow-down:before { - content: "\f218"; -} -.fa-diamond:before { - content: "\f219"; -} -.fa-ship:before { - content: "\f21a"; -} -.fa-user-secret:before { - content: "\f21b"; -} -.fa-motorcycle:before { - content: "\f21c"; -} -.fa-street-view:before { - content: "\f21d"; -} -.fa-heartbeat:before { - content: "\f21e"; -} -.fa-venus:before { - content: "\f221"; -} -.fa-mars:before { - content: "\f222"; -} -.fa-mercury:before { - content: "\f223"; -} -.fa-intersex:before, -.fa-transgender:before { - content: "\f224"; -} -.fa-transgender-alt:before { - content: "\f225"; -} -.fa-venus-double:before { - content: "\f226"; -} -.fa-mars-double:before { - content: "\f227"; -} -.fa-venus-mars:before { - content: "\f228"; -} -.fa-mars-stroke:before { - content: "\f229"; -} -.fa-mars-stroke-v:before { - content: "\f22a"; -} -.fa-mars-stroke-h:before { - content: "\f22b"; -} -.fa-neuter:before { - content: "\f22c"; -} -.fa-genderless:before { - content: "\f22d"; -} -.fa-facebook-official:before { - content: "\f230"; -} -.fa-pinterest-p:before { - content: "\f231"; -} -.fa-whatsapp:before { - content: "\f232"; -} -.fa-server:before { - content: "\f233"; -} -.fa-user-plus:before { - content: "\f234"; -} -.fa-user-times:before { - content: "\f235"; -} -.fa-hotel:before, -.fa-bed:before { - content: "\f236"; -} -.fa-viacoin:before { - content: "\f237"; -} -.fa-train:before { - content: "\f238"; -} -.fa-subway:before { - content: "\f239"; -} -.fa-medium:before { - content: "\f23a"; -} -.fa-yc:before, -.fa-y-combinator:before { - content: "\f23b"; -} -.fa-optin-monster:before { - content: "\f23c"; -} -.fa-opencart:before { - content: "\f23d"; -} -.fa-expeditedssl:before { - content: "\f23e"; -} -.fa-battery-4:before, -.fa-battery:before, -.fa-battery-full:before { - content: "\f240"; -} -.fa-battery-3:before, -.fa-battery-three-quarters:before { - content: "\f241"; -} -.fa-battery-2:before, -.fa-battery-half:before { - content: "\f242"; -} -.fa-battery-1:before, -.fa-battery-quarter:before { - content: "\f243"; -} -.fa-battery-0:before, -.fa-battery-empty:before { - content: "\f244"; -} -.fa-mouse-pointer:before { - content: "\f245"; -} -.fa-i-cursor:before { - content: "\f246"; -} -.fa-object-group:before { - content: "\f247"; -} -.fa-object-ungroup:before { - content: "\f248"; -} -.fa-sticky-note:before { - content: "\f249"; -} -.fa-sticky-note-o:before { - content: "\f24a"; -} -.fa-cc-jcb:before { - content: "\f24b"; -} -.fa-cc-diners-club:before { - content: "\f24c"; -} -.fa-clone:before { - content: "\f24d"; -} -.fa-balance-scale:before { - content: "\f24e"; -} -.fa-hourglass-o:before { - content: "\f250"; -} -.fa-hourglass-1:before, -.fa-hourglass-start:before { - content: "\f251"; -} -.fa-hourglass-2:before, -.fa-hourglass-half:before { - content: "\f252"; -} -.fa-hourglass-3:before, -.fa-hourglass-end:before { - content: "\f253"; -} -.fa-hourglass:before { - content: "\f254"; -} -.fa-hand-grab-o:before, -.fa-hand-rock-o:before { - content: "\f255"; -} -.fa-hand-stop-o:before, -.fa-hand-paper-o:before { - content: "\f256"; -} -.fa-hand-scissors-o:before { - content: "\f257"; -} -.fa-hand-lizard-o:before { - content: "\f258"; -} -.fa-hand-spock-o:before { - content: "\f259"; -} -.fa-hand-pointer-o:before { - content: "\f25a"; -} -.fa-hand-peace-o:before { - content: "\f25b"; -} -.fa-trademark:before { - content: "\f25c"; -} -.fa-registered:before { - content: "\f25d"; -} -.fa-creative-commons:before { - content: "\f25e"; -} -.fa-gg:before { - content: "\f260"; -} -.fa-gg-circle:before { - content: "\f261"; -} -.fa-tripadvisor:before { - content: "\f262"; -} -.fa-odnoklassniki:before { - content: "\f263"; -} -.fa-odnoklassniki-square:before { - content: "\f264"; -} -.fa-get-pocket:before { - content: "\f265"; -} -.fa-wikipedia-w:before { - content: "\f266"; -} -.fa-safari:before { - content: "\f267"; -} -.fa-chrome:before { - content: "\f268"; -} -.fa-firefox:before { - content: "\f269"; -} -.fa-opera:before { - content: "\f26a"; -} -.fa-internet-explorer:before { - content: "\f26b"; -} -.fa-tv:before, -.fa-television:before { - content: "\f26c"; -} -.fa-contao:before { - content: "\f26d"; -} -.fa-500px:before { - content: "\f26e"; -} -.fa-amazon:before { - content: "\f270"; -} -.fa-calendar-plus-o:before { - content: "\f271"; -} -.fa-calendar-minus-o:before { - content: "\f272"; -} -.fa-calendar-times-o:before { - content: "\f273"; -} -.fa-calendar-check-o:before { - content: "\f274"; -} -.fa-industry:before { - content: "\f275"; -} -.fa-map-pin:before { - content: "\f276"; -} -.fa-map-signs:before { - content: "\f277"; -} -.fa-map-o:before { - content: "\f278"; -} -.fa-map:before { - content: "\f279"; -} -.fa-commenting:before { - content: "\f27a"; -} -.fa-commenting-o:before { - content: "\f27b"; -} -.fa-houzz:before { - content: "\f27c"; -} -.fa-vimeo:before { - content: "\f27d"; -} -.fa-black-tie:before { - content: "\f27e"; -} -.fa-fonticons:before { - content: "\f280"; -} -.fa-reddit-alien:before { - content: "\f281"; -} -.fa-edge:before { - content: "\f282"; -} -.fa-credit-card-alt:before { - content: "\f283"; -} -.fa-codiepie:before { - content: "\f284"; -} -.fa-modx:before { - content: "\f285"; -} -.fa-fort-awesome:before { - content: "\f286"; -} -.fa-usb:before { - content: "\f287"; -} -.fa-product-hunt:before { - content: "\f288"; -} -.fa-mixcloud:before { - content: "\f289"; -} -.fa-scribd:before { - content: "\f28a"; -} -.fa-pause-circle:before { - content: "\f28b"; -} -.fa-pause-circle-o:before { - content: "\f28c"; -} -.fa-stop-circle:before { - content: "\f28d"; -} -.fa-stop-circle-o:before { - content: "\f28e"; -} -.fa-shopping-bag:before { - content: "\f290"; -} -.fa-shopping-basket:before { - content: "\f291"; -} -.fa-hashtag:before { - content: "\f292"; -} -.fa-bluetooth:before { - content: "\f293"; -} -.fa-bluetooth-b:before { - content: "\f294"; -} -.fa-percent:before { - content: "\f295"; -} -.fa-gitlab:before { - content: "\f296"; -} -.fa-wpbeginner:before { - content: "\f297"; -} -.fa-wpforms:before { - content: "\f298"; -} -.fa-envira:before { - content: "\f299"; -} -.fa-universal-access:before { - content: "\f29a"; -} -.fa-wheelchair-alt:before { - content: "\f29b"; -} -.fa-question-circle-o:before { - content: "\f29c"; -} -.fa-blind:before { - content: "\f29d"; -} -.fa-audio-description:before { - content: "\f29e"; -} -.fa-volume-control-phone:before { - content: "\f2a0"; -} -.fa-braille:before { - content: "\f2a1"; -} -.fa-assistive-listening-systems:before { - content: "\f2a2"; -} -.fa-asl-interpreting:before, -.fa-american-sign-language-interpreting:before { - content: "\f2a3"; -} -.fa-deafness:before, -.fa-hard-of-hearing:before, -.fa-deaf:before { - content: "\f2a4"; -} -.fa-glide:before { - content: "\f2a5"; -} -.fa-glide-g:before { - content: "\f2a6"; -} -.fa-signing:before, -.fa-sign-language:before { - content: "\f2a7"; -} -.fa-low-vision:before { - content: "\f2a8"; -} -.fa-viadeo:before { - content: "\f2a9"; -} -.fa-viadeo-square:before { - content: "\f2aa"; -} -.fa-snapchat:before { - content: "\f2ab"; -} -.fa-snapchat-ghost:before { - content: "\f2ac"; -} -.fa-snapchat-square:before { - content: "\f2ad"; -} -.fa-pied-piper:before { - content: "\f2ae"; -} -.fa-first-order:before { - content: "\f2b0"; -} -.fa-yoast:before { - content: "\f2b1"; -} -.fa-themeisle:before { - content: "\f2b2"; -} -.fa-google-plus-circle:before, -.fa-google-plus-official:before { - content: "\f2b3"; -} -.fa-fa:before, -.fa-font-awesome:before { - content: "\f2b4"; -} -.fa-handshake-o:before { - content: "\f2b5"; -} -.fa-envelope-open:before { - content: "\f2b6"; -} -.fa-envelope-open-o:before { - content: "\f2b7"; -} -.fa-linode:before { - content: "\f2b8"; -} -.fa-address-book:before { - content: "\f2b9"; -} -.fa-address-book-o:before { - content: "\f2ba"; -} -.fa-vcard:before, -.fa-address-card:before { - content: "\f2bb"; -} -.fa-vcard-o:before, -.fa-address-card-o:before { - content: "\f2bc"; -} -.fa-user-circle:before { - content: "\f2bd"; -} -.fa-user-circle-o:before { - content: "\f2be"; -} -.fa-user-o:before { - content: "\f2c0"; -} -.fa-id-badge:before { - content: "\f2c1"; -} -.fa-drivers-license:before, -.fa-id-card:before { - content: "\f2c2"; -} -.fa-drivers-license-o:before, -.fa-id-card-o:before { - content: "\f2c3"; -} -.fa-quora:before { - content: "\f2c4"; -} -.fa-free-code-camp:before { - content: "\f2c5"; -} -.fa-telegram:before { - content: "\f2c6"; -} -.fa-thermometer-4:before, -.fa-thermometer:before, -.fa-thermometer-full:before { - content: "\f2c7"; -} -.fa-thermometer-3:before, -.fa-thermometer-three-quarters:before { - content: "\f2c8"; -} -.fa-thermometer-2:before, -.fa-thermometer-half:before { - content: "\f2c9"; -} -.fa-thermometer-1:before, -.fa-thermometer-quarter:before { - content: "\f2ca"; -} -.fa-thermometer-0:before, -.fa-thermometer-empty:before { - content: "\f2cb"; -} -.fa-shower:before { - content: "\f2cc"; -} -.fa-bathtub:before, -.fa-s15:before, -.fa-bath:before { - content: "\f2cd"; -} -.fa-podcast:before { - content: "\f2ce"; -} -.fa-window-maximize:before { - content: "\f2d0"; -} -.fa-window-minimize:before { - content: "\f2d1"; -} -.fa-window-restore:before { - content: "\f2d2"; -} -.fa-times-rectangle:before, -.fa-window-close:before { - content: "\f2d3"; -} -.fa-times-rectangle-o:before, -.fa-window-close-o:before { - content: "\f2d4"; -} -.fa-bandcamp:before { - content: "\f2d5"; -} -.fa-grav:before { - content: "\f2d6"; -} -.fa-etsy:before { - content: "\f2d7"; -} -.fa-imdb:before { - content: "\f2d8"; -} -.fa-ravelry:before { - content: "\f2d9"; -} -.fa-eercast:before { - content: "\f2da"; -} -.fa-microchip:before { - content: "\f2db"; -} -.fa-snowflake-o:before { - content: "\f2dc"; -} -.fa-superpowers:before { - content: "\f2dd"; -} -.fa-wpexplorer:before { - content: "\f2de"; -} -.fa-meetup:before { - content: "\f2e0"; -} -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - border: 0; -} -.sr-only-focusable:active, -.sr-only-focusable:focus { - position: static; - width: auto; - height: auto; - margin: 0; - overflow: visible; - clip: auto; -} diff --git a/frappe/public/css/fonts/fontawesome/LICENSE b/frappe/public/css/fonts/fontawesome/LICENSE new file mode 100644 index 0000000000..304228182c --- /dev/null +++ b/frappe/public/css/fonts/fontawesome/LICENSE @@ -0,0 +1,4 @@ +The Font Awesome font is licensed under the SIL OFL 1.1: +http://scripts.sil.org/OFL +Font Awesome CSS, LESS, and Sass files are licensed under the MIT License: +https://opensource.org/licenses/mit-license.html diff --git a/frappe/public/css/font-awesome.min.css b/frappe/public/css/fonts/fontawesome/font-awesome.min.css similarity index 100% rename from frappe/public/css/font-awesome.min.css rename to frappe/public/css/fonts/fontawesome/font-awesome.min.css diff --git a/frappe/public/css/tree_grid.css b/frappe/public/css/tree_grid.css index 8e23edfc0a..dc26510728 100644 --- a/frappe/public/css/tree_grid.css +++ b/frappe/public/css/tree_grid.css @@ -13,10 +13,3 @@ visibility: visible; } -.toggle.expand { - background: url("/assets/frappe/js/lib/slickgrid/images/expand.gif") no-repeat center center; -} - -.toggle.collapse { - background: url("/assets/frappe/js/lib/slickgrid/images/collapse.gif") no-repeat center center; -} diff --git a/frappe/public/js/frappe/form/controls/phone.js b/frappe/public/js/frappe/form/controls/phone.js index d67b449ac8..16ee56b841 100644 --- a/frappe/public/js/frappe/form/controls/phone.js +++ b/frappe/public/js/frappe/form/controls/phone.js @@ -1,14 +1,27 @@ - +import localforage from "localforage"; import PhonePicker from '../../phone_picker/phone_picker'; frappe.ui.form.ControlPhone = class ControlPhone extends frappe.ui.form.ControlData { - make_input() { + async make_input() { + await this.setup_country_codes(); super.make_input(); this.setup_country_code_picker(); this.input_events(); } + async setup_country_codes() { + const key = "country_code_info" + let data = await localforage.getItem(key); + if (data) { + this.country_codes = data; + } else { + const data = await frappe.xcall("frappe.geo.country_info.get_country_timezone_info"); + this.country_codes = data?.country_info; + localforage.setItem(key, this.country_codes); + } + } + input_events() { this.$input.keydown((e) => { const key_code = e.keyCode; @@ -22,10 +35,10 @@ frappe.ui.form.ControlPhone = class ControlPhone extends frappe.ui.form.ControlD // Replaces code when selected and removes previously selected. this.country_code_picker.on_change = (country) => { if (!country) { - return this.reset_inputx(); + return this.reset_input(); } - const country_code = frappe.boot.country_codes[country].code; - const country_isd = frappe.boot.country_codes[country].isd; + const country_code = this.country_codes[country].code; + const country_isd = this.country_codes[country].isd; this.set_flag(country_code); this.$icon = this.selected_icon.find('svg'); this.$flag = this.selected_icon.find('img'); @@ -69,7 +82,7 @@ frappe.ui.form.ControlPhone = class ControlPhone extends frappe.ui.form.ControlD let picker_wrapper = $('
'); this.country_code_picker = new PhonePicker({ parent: picker_wrapper, - countries: frappe.boot.country_codes + countries: this.country_codes }); this.$wrapper.popover({ @@ -119,6 +132,7 @@ frappe.ui.form.ControlPhone = class ControlPhone extends frappe.ui.form.ControlD } reset_input() { + if (!this.$input) return; this.$input.val(""); this.$wrapper.find('.country').text(""); if (this.selected_icon.find('svg').hasClass('hide')) { @@ -128,7 +142,10 @@ frappe.ui.form.ControlPhone = class ControlPhone extends frappe.ui.form.ControlD this.$input.css("padding-left", 30); } - set_formatted_input(value) { + async set_formatted_input(value) { + if (!this.country_codes) { + await this.setup_country_codes(); + } if (value && value.includes('-') && value.split('-').length == 2) { let isd = this.value.split("-")[0]; this.get_country_code_and_change_flag(isd); @@ -158,7 +175,7 @@ frappe.ui.form.ControlPhone = class ControlPhone extends frappe.ui.form.ControlD // country_code for India is 'in' get_country_code_and_change_flag(isd) { - let country_data = frappe.boot.country_codes; + let country_data = this.country_codes; let flag = this.selected_icon.find('img'); for (const country in country_data) { if (Object.values(country_data[country]).includes(isd)) { @@ -175,12 +192,12 @@ frappe.ui.form.ControlPhone = class ControlPhone extends frappe.ui.form.ControlD } get_country(country) { - const country_codes = frappe.boot.country_codes; + const country_codes = this.country_codes; return country_codes[country].isd; } get_country_flag(country) { - const country_codes = frappe.boot.country_codes; + const country_codes = this.country_codes; let code = country_codes[country].code; return frappe.utils.flag(code); } diff --git a/frappe/public/js/frappe/form/tab.js b/frappe/public/js/frappe/form/tab.js index 324d0c50c8..27f8c3ebf0 100644 --- a/frappe/public/js/frappe/form/tab.js +++ b/frappe/public/js/frappe/form/tab.js @@ -80,6 +80,7 @@ export default class Tab { set_active() { this.parent.find('.nav-link').tab('show'); this.wrapper.addClass('active'); + this.frm?.set_active_tab?.(this); } is_active() { diff --git a/frappe/public/js/frappe/list/list_settings.js b/frappe/public/js/frappe/list/list_settings.js index 6c48bd013a..f7c3bf9505 100644 --- a/frappe/public/js/frappe/list/list_settings.js +++ b/frappe/public/js/frappe/list/list_settings.js @@ -136,7 +136,7 @@ export default class ListSettings {
${fields}
-

+ Add / Remove Fields diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index 3a39a949c9..94a3c29b27 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -1456,7 +1456,11 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { on_update() {} update_url_with_filters() { - window.history.replaceState(null, null, this.get_url_with_filters()); + if (frappe.get_route_str() == this.page_name && !this.report_name) { + // only update URL if the route still matches current page. + // do not update if current list is a "saved report". + window.history.replaceState(null, null, this.get_url_with_filters()); + } } get_url_with_filters() { diff --git a/frappe/public/js/frappe/views/kanban/kanban_board.js b/frappe/public/js/frappe/views/kanban/kanban_board.bundle.js similarity index 83% rename from frappe/public/js/frappe/views/kanban/kanban_board.js rename to frappe/public/js/frappe/views/kanban/kanban_board.bundle.js index 64e90f5326..c1c78dbdb0 100644 --- a/frappe/public/js/frappe/views/kanban/kanban_board.js +++ b/frappe/public/js/frappe/views/kanban/kanban_board.bundle.js @@ -1,14 +1,17 @@ // TODO: Refactor for better UX +import Vuex from 'vuex'; + frappe.provide("frappe.views"); (function() { var method_prefix = 'frappe.desk.doctype.kanban_board.kanban_board.'; - var store = fluxify.createStore({ - id: 'store', - initialState: { + let columns_unwatcher = null; + + var store = new Vuex.Store({ + state: { doctype: '', board: {}, card_meta: {}, @@ -18,12 +21,16 @@ frappe.provide("frappe.views"); cur_list: {}, empty_state: true }, - actionCallbacks: { - init: function(updater, opts) { - updater.set({ + mutations: { + update_state(state, obj) { + Object.assign(state, obj); + } + }, + actions: { + init: function(context, opts) { + context.commit("update_state", { empty_state: true }); - var board = opts.board; var card_meta = opts.card_meta; opts.card_meta = card_meta; @@ -32,8 +39,7 @@ frappe.provide("frappe.views"); return prepare_card(card, opts); }); var columns = prepare_columns(board.columns); - - updater.set({ + context.commit("update_state", { doctype: opts.doctype, board: board, card_meta: card_meta, @@ -44,20 +50,20 @@ frappe.provide("frappe.views"); wrapper: opts.wrapper }); }, - update_cards: function(updater, cards) { - var state = this; + update_cards: function(context, cards) { + var state = context.state; var _cards = cards .map(card => prepare_card(card, state)) - .concat(this.cards) + .concat(state.cards) .uniqBy(card => card.name); - updater.set({ + context.commit("update_state", { cards: _cards }); }, - add_column: function(updater, col) { - if(frappe.model.can_create('Custom Field')) { - fluxify.doAction('update_column', col, 'add'); + add_column: function(context, col) { + if (frappe.model.can_create('Custom Field')) { + store.dispatch('update_column', {col, action: 'add'}); } else { frappe.msgprint({ title: __('Not permitted'), @@ -66,15 +72,15 @@ frappe.provide("frappe.views"); }); } }, - archive_column: function(updater, col) { - fluxify.doAction('update_column', col, 'archive'); + archive_column: function(context, col) { + store.dispatch('update_column', {col, action: 'archive'}); }, - restore_column: function(updater, col) { - fluxify.doAction('update_column', col, 'restore'); + restore_column: function(context, col) { + store.dispatch('update_column', {col, action: 'restore'}); }, - update_column: function(updater, col, action) { - var doctype = this.doctype; - var board = this.board; + update_column: function(context, {col, action}) { + var doctype = context.state.doctype; + var board = context.state.board; fetch_customization(doctype) .then(function(doc) { return modify_column_field_in_c11n(doc, board, col.title, action); @@ -84,23 +90,23 @@ frappe.provide("frappe.views"); return update_kanban_board(board.name, col.title, action); }).then(function(r) { var cols = r.message; - updater.set({ + context.commit("update_state", { columns: prepare_columns(cols) }); }, function(err) { console.error(err); // eslint-disable-line }); }, - add_card: function(updater, card_title, column_title) { - var doc = frappe.model.get_new_doc(this.doctype); - var field = this.card_meta.title_field; - var quick_entry = this.card_meta.quick_entry; - var state = this; + add_card: function(context, {card_title, column_title}) { + var state = context.state; + var doc = frappe.model.get_new_doc(state.doctype); + var field = state.card_meta.title_field; + var quick_entry = state.card_meta.quick_entry; var doc_fields = {}; doc_fields[field.fieldname] = card_title; - doc_fields[this.board.field_name] = column_title; - this.cur_list.filter_area.get().forEach(function(f) { + doc_fields[state.board.field_name] = column_title; + state.cur_list.filter_area.get().forEach(function(f) { if (f[2] !== "=") return; doc_fields[f[1]] = f[3]; }); @@ -114,7 +120,7 @@ frappe.provide("frappe.views"); const cards = [...state.cards, card]; // remember the name which we will override later const old_name = doc.name; - updater.set({ cards }); + context.commit("update_state", { cards }); if (field && !quick_entry) { return insert_doc(doc) @@ -125,49 +131,49 @@ frappe.provide("frappe.views"); const card = prepare_card(updated_doc, state); const new_cards = state.cards.slice(); new_cards[index] = card; - updater.set({ cards: new_cards }); + context.commit("update_state", { cards: new_cards }); const args = { new: 1, name: card.name, colname: updated_doc[state.board.field_name], }; - fluxify.doAction('update_order_for_single_card', args); + store.dispatch('update_order_for_single_card', args); }); } else { - frappe.new_doc(this.doctype, doc); + frappe.new_doc(state.doctype, doc); } }, - update_card: function(updater, card) { + update_card: function(context, card) { var index = -1; - this.cards.forEach(function(c, i) { + context.state.cards.forEach(function(c, i) { if (c.name === card.name) { index = i; } }); - var cards = this.cards.slice(); + var cards = context.state.cards.slice(); if (index !== -1) { cards.splice(index, 1, card); } - updater.set({ cards: cards }); + context.commit("update_state", { cards: cards }); }, - update_order_for_single_card: function(updater, card) { + update_order_for_single_card: function(context, card) { // cache original order - const _cards = this.cards.slice(); - const _columns = this.columns.slice(); + const _cards = context.state.cards.slice(); + const _columns = context.state.columns.slice(); let args = {}; let method_name = ""; if (card.new) { method_name = "add_card"; args = { - board_name: this.board.name, + board_name: context.state.board.name, docname: card.name, colname: card.colname, }; } else { method_name = "update_order_for_single_card"; args = { - board_name: this.board.name, + board_name: context.state.board.name, docname: card.name, from_colname: card.from_colname, to_colname: card.to_colname, @@ -184,7 +190,7 @@ frappe.provide("frappe.views"); let updated_cards = [{'name': card.name, 'column': card.to_colname || card.colname}]; let cards = update_cards_column(updated_cards); let columns = prepare_columns(board.columns); - updater.set({ + context.commit("update_state", { cards: cards, columns: columns }); @@ -192,20 +198,20 @@ frappe.provide("frappe.views"); } }).fail(function() { // revert original order - updater.set({ + context.commit("update_state", { cards: _cards, columns: _columns }); frappe.dom.unfreeze(); }); }, - update_order: function(updater) { + update_order: function(context) { // cache original order - const _cards = this.cards.slice(); - const _columns = this.columns.slice(); + const _cards = context.state.cards.slice(); + const _columns = context.state.columns.slice(); const order = {}; - this.wrapper.find('.kanban-column[data-column-value]') + context.state.wrapper.find('.kanban-column[data-column-value]') .each(function() { var col_name = $(this).data().columnValue; order[col_name] = []; @@ -218,7 +224,7 @@ frappe.provide("frappe.views"); frappe.call({ method: method_prefix + "update_order", args: { - board_name: this.board.name, + board_name: context.state.board.name, order: order }, callback: (r) => { @@ -226,46 +232,46 @@ frappe.provide("frappe.views"); var updated_cards = r.message[1]; var cards = update_cards_column(updated_cards); var columns = prepare_columns(board.columns); - updater.set({ + context.commit("update_state", { cards: cards, columns: columns }); } }).fail(function() { // revert original order - updater.set({ + context.commit("update_state", { cards: _cards, columns: _columns }); }); }, - update_column_order: function(updater, order) { + update_column_order: function(context, order) { return frappe.call({ method: method_prefix + "update_column_order", args: { - board_name: this.board.name, + board_name: context.state.board.name, order: order } }).then(function(r) { var board = r.message; var columns = prepare_columns(board.columns); - updater.set({ + context.commit("update_state", { columns: columns }); }); }, - set_indicator: function(updater, column, color) { + set_indicator: function(context, {column, color}) { return frappe.call({ method: method_prefix + "set_indicator", args: { - board_name: this.board.name, + board_name: context.state.board.name, column_name: column.title, indicator: color } }).then(function(r) { var board = r.message; var columns = prepare_columns(board.columns); - updater.set({ + context.commit("update_state", { columns: columns }); }); @@ -285,20 +291,29 @@ frappe.provide("frappe.views"); opts.cards = cards; if(self.wrapper.find('.kanban').length > 0 && self.cur_list.start !== 0) { - fluxify.doAction('update_cards', cards); + store.dispatch('update_cards', cards); } else { init(); } }; function init() { - fluxify.doAction('init', opts); - store.off('change:columns').on('change:columns', make_columns); + store.dispatch('init', opts); + columns_unwatcher && columns_unwatcher(); + store.watch((state, getters) => { + return state.columns; + }, make_columns); prepare(); - store.on('change:cur_list', setup_restore_columns); - store.on('change:columns', setup_restore_columns); - store.on('change:empty_state', show_empty_state); - fluxify.doAction('update_order'); + store.watch((state, getters) => { + return state.cur_list; + }, setup_restore_columns); + columns_unwatcher = store.watch((state, getters) => { + return state.columns; + }, setup_restore_columns); + store.watch((state, getters) => { + return state.empty_state; + }, show_empty_state); + store.dispatch('update_order'); } function prepare() { @@ -316,7 +331,7 @@ frappe.provide("frappe.views"); function make_columns() { self.$kanban_board.find(".kanban-column").not(".add-new-column").remove(); - var columns = store.getState().columns; + var columns = store.state.columns; columns.filter(is_active_column).map(function(col) { frappe.views.KanbanBoardColumn(col, self.$kanban_board); @@ -338,7 +353,7 @@ frappe.provide("frappe.views"); onEnd: function() { var order = sortable.toArray(); order = order.slice(1); - fluxify.doAction('update_column_order', order); + store.dispatch('update_column_order', order); } }); } @@ -365,7 +380,7 @@ frappe.provide("frappe.views"); var col = { title: title.trim() }; - fluxify.doAction('add_column', col); + store.dispatch('add_column', col); $compose_column_form.find('input').val(''); $compose_column.show(); $compose_column_form.hide(); @@ -421,8 +436,8 @@ frappe.provide("frappe.views"); } function setup_restore_columns() { - var cur_list = store.getState().cur_list; - var columns = store.getState().columns; + var cur_list = store.state.cur_list; + var columns = store.state.columns; var list_row_right = cur_list.$page .find(`[data-list-renderer='Kanban'] .list-row-right`) .css('margin-right', '15px'); @@ -456,12 +471,12 @@ frappe.provide("frappe.views"); title: column_title, status: 'Archived' }; - fluxify.doAction('restore_column', col); + store.dispatch('restore_column', col); }); } function show_empty_state() { - var empty_state = store.getState().empty_state; + var empty_state = store.state.empty_state; if(empty_state) { self.$kanban_board.find('.kanban-column').hide(); @@ -485,7 +500,9 @@ frappe.provide("frappe.views"); make_dom(); setup_sortable(); make_cards(); - store.on('change:cards', make_cards); + store.watch((state, getters) => { + return state.cards; + }, make_cards); bind_add_card(); bind_options(); } @@ -494,7 +511,7 @@ frappe.provide("frappe.views"); self.$kanban_column = $(frappe.render_template( 'kanban_column', { title: column.title, - doctype: store.getState().doctype, + doctype: store.state.doctype, indicator: frappe.scrub(column.indicator, '-') })).appendTo(wrapper); self.$kanban_cards = self.$kanban_column.find('.kanban-cards'); @@ -502,7 +519,7 @@ frappe.provide("frappe.views"); function make_cards() { self.$kanban_cards.empty(); - var cards = store.getState().cards; + var cards = store.state.cards; filtered_cards = get_cards_for_column(cards, column); var filtered_cards_names = filtered_cards.map(card => card.name); @@ -548,7 +565,7 @@ frappe.provide("frappe.views"); old_index: e.oldIndex, new_index: e.newIndex, }; - fluxify.doAction('update_order_for_single_card', args); + store.dispatch('update_order_for_single_card', args); }, onAdd: function() { }, @@ -579,10 +596,12 @@ frappe.provide("frappe.views"); var card_title = $textarea.val(); $new_card_area.hide(); $textarea.val(''); - fluxify.doAction('add_card', card_title, column.title) - .then(() => { - $btn_add.show(); - }); + store.dispatch('add_card', { + card_title, + column_title: column.title + }).then(() => { + $btn_add.show(); + }); } } }); @@ -602,10 +621,10 @@ frappe.provide("frappe.views"); var action = $btn.data().action; if (action === "archive") { - fluxify.doAction('archive_column', column); + store.dispatch('archive_column', column); } else if (action === "indicator") { var color = $btn.data().indicator; - fluxify.doAction('set_indicator', column, color); + store.dispatch('set_indicator', {column, color}); } }); get_column_indicators(function(indicators) { @@ -728,7 +747,7 @@ frappe.provide("frappe.views"); callback: function() { const users = self.assign_to_dialog.get_values().assign_to; card.assigned_list = [...new Set(card.assigned_list.concat(users))]; - fluxify.doAction('update_card', card); + store.dispatch('update_card', card); } }); self.assign_to_dialog = self.assign_to.dialog; @@ -850,21 +869,6 @@ frappe.provide("frappe.views"); }); } - function is_filters_modified(board, cur_list) { - return new Promise(function(resolve) { - setTimeout(function() { - try { - var list_filters = JSON.stringify(cur_list.filter_area.get()); - resolve(list_filters !== board.filters); - } catch(e) { - // sometimes the filter_list is not initiated - resolve(false); - } - - }, 2000); - }); - } - function is_active_column(col) { return col.status !== 'Archived'; } @@ -876,13 +880,13 @@ frappe.provide("frappe.views"); } function get_card(name) { - return store.getState().cards.find(function(c) { + return store.state.cards.find(function(c) { return c.name === name; }); } function update_cards_column(updated_cards) { - var cards = store.getState().cards; + var cards = store.state.cards; cards.forEach(function(c) { updated_cards.forEach(function(uc) { if(uc.name === c.name) { @@ -909,22 +913,4 @@ frappe.provide("frappe.views"); callback(indicators); }); } - - function isBound(el, event, fn) { - var events = $._data(el[0], 'events'); - if(!events) return false; - var handlers = events[event]; - var flag = false; - handlers.forEach(function(h) { - if(h.handler.name === fn.name) - flag = true; - }); - return flag; - } - - function remove_img_tags(html) { - const $temp = $(`

${html}
`); - $temp.find('img').remove(); - return $temp.html(); - } })(); diff --git a/frappe/public/js/frappe/views/kanban/kanban_view.js b/frappe/public/js/frappe/views/kanban/kanban_view.js index 129db13b07..f38cb906a2 100644 --- a/frappe/public/js/frappe/views/kanban/kanban_view.js +++ b/frappe/public/js/frappe/views/kanban/kanban_view.js @@ -209,10 +209,7 @@ frappe.views.KanbanView = class KanbanView extends frappe.views.ListView { } get required_libs() { - return [ - 'assets/frappe/js/lib/fluxify.min.js', - 'assets/frappe/js/frappe/views/kanban/kanban_board.js' - ]; + return 'kanban_board.bundle.js'; } }; diff --git a/frappe/public/js/frappe/views/reports/print_tree.html b/frappe/public/js/frappe/views/reports/print_tree.html index 973c1c0e21..7c0054de7b 100644 --- a/frappe/public/js/frappe/views/reports/print_tree.html +++ b/frappe/public/js/frappe/views/reports/print_tree.html @@ -9,7 +9,7 @@ {{ title }} + href="{{ base_url }}/assets/frappe/css/fonts/fontawesome/font-awesome.min.css">