From f5b8a651710a86549c98a22ee65ded726e3f0afa Mon Sep 17 00:00:00 2001
From: Bilelkihal <61744974+Bilelkihal@users.noreply.github.com>
Date: Tue, 7 Mar 2023 10:21:29 +0100
Subject: [PATCH 01/61] Update browse page design with the new design on figma
---
app/assets/images/arrow-down.svg | 3 +
app/assets/images/search.svg | 3 +
.../stylesheets/application.css.scss.erb | 1 +
app/assets/stylesheets/browse.scss | 315 ++++++++++++++++++
app/views/ontologies/browse.html.haml | 133 ++++++++
5 files changed, 455 insertions(+)
create mode 100644 app/assets/images/arrow-down.svg
create mode 100644 app/assets/images/search.svg
create mode 100644 app/assets/stylesheets/browse.scss
create mode 100644 app/views/ontologies/browse.html.haml
diff --git a/app/assets/images/arrow-down.svg b/app/assets/images/arrow-down.svg
new file mode 100644
index 0000000000..2757508f60
--- /dev/null
+++ b/app/assets/images/arrow-down.svg
@@ -0,0 +1,3 @@
+
diff --git a/app/assets/images/search.svg b/app/assets/images/search.svg
new file mode 100644
index 0000000000..5b6ace2ede
--- /dev/null
+++ b/app/assets/images/search.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/app/assets/stylesheets/application.css.scss.erb b/app/assets/stylesheets/application.css.scss.erb
index 1b70724a04..a63e75be5e 100644
--- a/app/assets/stylesheets/application.css.scss.erb
+++ b/app/assets/stylesheets/application.css.scss.erb
@@ -49,6 +49,7 @@
@import "login";
@import "components/index";
@import "account";
+@import "browse";
/* Bootstrap and Font Awesome */
@import "bootstrap";
diff --git a/app/assets/stylesheets/browse.scss b/app/assets/stylesheets/browse.scss
new file mode 100644
index 0000000000..9ba47a4f44
--- /dev/null
+++ b/app/assets/stylesheets/browse.scss
@@ -0,0 +1,315 @@
+
+.browse-container p{
+ margin: unset;
+}
+.browse-container h2{
+ margin: unset;
+ line-height: unset;
+}
+.browse-container hr{
+ margin: unset;
+}
+
+
+a{
+ text-decoration: none !important;
+}
+.browse-center{
+ display: flex;
+ justify-content: center;
+ margin: 50px 0;
+}
+.browse-title{
+ font-size: 18px;
+ font-weight: 700;
+}
+.browse-upload-ontology-button{
+ margin: 20px 0;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ font-size: 15px;
+ font-weight: 500;
+ color: var(--primary-color);
+ width: 226px;
+ padding: 15px 25px;
+ border: 1px solid var(--primary-color);
+ border-radius: 40px;
+}
+.browse-desc-text{
+ font-size: 16px;
+ color: #666666;
+ font-weight: 400;
+ margin: 5px 0 !important;
+}
+.browse-ontology-container{
+ border: 1px solid #DFDFDF;
+ border-radius: 5px;
+ padding: 15px 20px;
+ margin-bottom: 20px;
+ display: flex;
+ justify-content: space-between;
+}
+.browse-ontology-description{
+ width: 540px;
+}
+.browse-ontology-title{
+ color: var(--primary-color);
+ font-weight: 600;
+ font-size: 18px;
+}
+.browse-fair{
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ width: 400px;
+}
+
+.browse-sub-container{
+ display: flex;
+}
+.browse-first-row{
+ width: 300px;
+ margin-right: 20px;
+
+}
+
+.browse-second-row{
+ width: 846px;
+
+}
+
+#browse-title-line{
+ width: 55px;
+ margin-top: 3px;
+ border: 0.5px solid var(--primary-color);
+ border-radius: 5px;
+}
+.browse-fair-title{
+ font-size: 16px;
+ color: #666666;
+ font-weight: 400;
+}
+.browse-progress-bar{
+ width: 154px;
+ height: 10px;
+ border-radius: 20px;
+ background-color: #E7E7E7;
+
+}
+.browse-faire-progress{
+ width: 100%;
+ height: 10px;
+ background-color: var(--primary-color);
+ border-radius: 20px 0px 0px 20px;
+}
+.browse-fair-score{
+ font-size: 14px;
+ font-weight: 500;
+ color: #666666;
+}
+.browse-fair-details{
+ font-size: 14px;
+ font-weight: 400;
+ color: var(--primary-color);
+}
+.browse-uploaded{
+ font-size: 13px;
+ height: 37px;
+ width: 160px;
+ background-color: #E7E7E7;
+ border-radius: 5px;
+ display: flex;
+ padding: 2px 15px;
+ justify-content: space-between;
+ align-items: center;
+ color: #666666;
+ margin-top: 15px;
+}
+.browse-uploaded-date{
+ font-weight: 600;
+}
+.browse-ontology-cards{
+ display: grid;
+ grid-template-columns: 113px 113px;
+ grid-template-rows: 78px 78px;
+ grid-column-gap: 15px;
+ grid-row-gap: 15px;
+ direction: rtl;
+}
+.browse-onology-card{
+ border: 0.5px solid var(--primary-color);
+ border-radius: 5px;
+ height: 78px;
+ width: 113px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ color: var(--primary-color);
+}
+.browse-card-number{
+ font: 17px;
+ font-weight: 700;
+}
+.browse-card-text{
+ font-size: 15px;
+ font-weight: 500;
+}
+#browse-sub-title-line{
+ width:20px;
+ margin-top: 3px;
+ border: 0.5px solid var(--primary-color);
+ border-radius: 5px;
+ margin-bottom: 15px;
+}
+.browse-filters-title{
+ font-weight: 600;
+ font-size: 16px;
+}
+.browse-search-bar{
+ display: flex;
+
+}
+.browse-search-bar input{
+ border: none;
+ border-radius: 14px;
+ width: 100%;
+ box-shadow: rgba(100, 100, 111, 0.1) 0px 7px 29px 0px;
+ padding: 20px 20px;
+ margin-bottom: 20px;
+ font-size: 16px;
+ position: absolute;
+ width: 972px;
+ z-index: 1;
+
+}
+.browse-search-bar input:focus{
+ box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px;
+ outline: none;
+}
+
+.browse-search-bar select{
+ border: none;
+ background-color: white;
+ padding: 20px 20px;
+ font-size: 16px;
+ background-image: url("data:image/svg+xml;utf8,");
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ background-repeat: no-repeat;
+ background-position-x: 90%;
+ background-position-y: 50%;
+
+}
+.browse-search-bar select:focus{
+ outline: none;
+ box-shadow: rgba(100, 100, 111, 0.1) 0px 7px 29px 0px;
+}
+
+.browse-search-bar .browse-format-filter{
+ position: absolute;
+ margin-left: 526px;
+ z-index: 2;
+ width: 150px;
+}
+.browse-search-bar .browse-sort-by-filter{
+ position: absolute;
+ margin-left: 676px;
+ z-index: 2;
+ width: 296px;
+ border-radius: 0 14px 14px 0;
+}
+.browse-search-button{
+ margin-left: 85%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ border-radius: 14px;
+ padding: 20px;
+ background-color: var(--primary-color);
+ width: 160px;
+ height: 62px;
+ cursor: pointer;
+}
+.browse-search-button p{
+ font-size: 16px;
+ color: white;
+ margin-left: 15px;
+}
+.browse-filter{
+ border: 1px solid #DFDFDF;
+ border-radius: 5px;
+ padding: 14px 20px;
+ margin-bottom: 10px;
+
+
+}
+.browse-filter-checks-container{
+ display: flex;
+ flex-wrap: wrap;
+ margin-top: 10px;
+}
+.browse-filter-title-bar{
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ font-size: 14px;
+ font-weight: 400;
+ color: #666666;
+ cursor: pointer;
+}
+
+
+
+/* Toggle switch css */
+.switch {
+ position: relative;
+ display: inline-block;
+ width: 40px;
+ height: 20px;
+}
+.switch input {
+ opacity: 0;
+ width: 0;
+ height: 0;
+}
+.slider {
+ position: absolute;
+ cursor: pointer;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: #ccc;
+ -webkit-transition: .4s;
+ transition: .4s;
+ border-radius: 34px;
+}
+.slider:before {
+ position: absolute;
+ content: "";
+ height: 13px;
+ width: 13px;
+ left: 4px;
+ bottom: 4px;
+ background-color: white;
+ -webkit-transition: .4s;
+ transition: .4s;
+ border-radius: 50%;
+}
+input:checked + .slider {
+ background-color: var(--primary-color);
+}
+input:focus + .slider {
+ box-shadow: 0 0 1px var(--primary-color);
+}
+input:checked + .slider:before {
+ -webkit-transform: translateX(19px);
+ -ms-transform: translateX(19px);
+ transform: translateX(19px);
+}
+.account-page-subscribe-button{
+
+}
diff --git a/app/views/ontologies/browse.html.haml b/app/views/ontologies/browse.html.haml
new file mode 100644
index 0000000000..3208034da7
--- /dev/null
+++ b/app/views/ontologies/browse.html.haml
@@ -0,0 +1,133 @@
+.browse-center
+ .browse-container
+ %h2.browse-title Browse
+ %hr#browse-title-line/
+ - if session[:user].nil?
+ %a.browse-upload-ontology-button{:href => "/login?redirect=/ontologies/new"}
+ %svg{:fill => "none", :height => "14", :viewbox => "0 0 15 14", :width => "15", :xmlns => "http://www.w3.org/2000/svg"}
+ %path{:d => "M3.46242 4.17686L6.91776 0.23682C6.98312 0.162278 7.06302 0.102677 7.15225 0.0618794C7.24149 0.0210821 7.33808 0 7.43576 0C7.53344 0 7.63003 0.0210821 7.71927 0.0618794C7.80851 0.102677 7.8884 0.162278 7.95377 0.23682L11.4092 4.17686C11.533 4.31795 11.5971 4.50373 11.5874 4.69334C11.5777 4.88295 11.4951 5.06086 11.3577 5.18792C11.2204 5.31498 11.0395 5.38079 10.8548 5.37087C10.6702 5.36095 10.497 5.27612 10.3732 5.13503L8.13286 2.58055V10.4116C8.13286 10.6015 8.05942 10.7836 7.92869 10.9178C7.79796 11.0521 7.62065 11.1275 7.43576 11.1275C7.25088 11.1275 7.07357 11.0521 6.94284 10.9178C6.8121 10.7836 6.73866 10.6015 6.73866 10.4116V2.58058L4.49846 5.13512C4.43719 5.20498 4.36313 5.26177 4.28049 5.30223C4.19785 5.3427 4.10826 5.36605 4.01684 5.37096C3.92541 5.37587 3.83394 5.36223 3.74764 5.33084C3.66135 5.29944 3.58192 5.2509 3.51389 5.18798C3.44586 5.12506 3.39057 5.04899 3.35117 4.96413C3.31176 4.87926 3.28902 4.78726 3.28425 4.69337C3.27947 4.59948 3.29274 4.50554 3.32331 4.41692C3.35388 4.32829 3.40115 4.24672 3.46242 4.17686ZM14.1744 12.488H0.697103C0.51222 12.488 0.334909 12.5635 0.204177 12.6977C0.0734445 12.832 0 13.0141 0 13.2039C0 13.3938 0.0734445 13.5759 0.204177 13.7101C0.334909 13.8444 0.51222 13.9198 0.697103 13.9198H14.1744C14.3593 13.9198 14.5366 13.8444 14.6673 13.7101C14.7981 13.5759 14.8715 13.3938 14.8715 13.2039C14.8715 13.0141 14.7981 12.832 14.6673 12.6977C14.5366 12.5635 14.3593 12.488 14.1744 12.488Z", :fill => "#31B404"}
+ Submit new ontology
+ - else
+ %a.browse-upload-ontology-button{:href => "/ontologies/new"}
+ %svg{:fill => "none", :height => "14", :viewbox => "0 0 15 14", :width => "15", :xmlns => "http://www.w3.org/2000/svg"}
+ %path{:d => "M3.46242 4.17686L6.91776 0.23682C6.98312 0.162278 7.06302 0.102677 7.15225 0.0618794C7.24149 0.0210821 7.33808 0 7.43576 0C7.53344 0 7.63003 0.0210821 7.71927 0.0618794C7.80851 0.102677 7.8884 0.162278 7.95377 0.23682L11.4092 4.17686C11.533 4.31795 11.5971 4.50373 11.5874 4.69334C11.5777 4.88295 11.4951 5.06086 11.3577 5.18792C11.2204 5.31498 11.0395 5.38079 10.8548 5.37087C10.6702 5.36095 10.497 5.27612 10.3732 5.13503L8.13286 2.58055V10.4116C8.13286 10.6015 8.05942 10.7836 7.92869 10.9178C7.79796 11.0521 7.62065 11.1275 7.43576 11.1275C7.25088 11.1275 7.07357 11.0521 6.94284 10.9178C6.8121 10.7836 6.73866 10.6015 6.73866 10.4116V2.58058L4.49846 5.13512C4.43719 5.20498 4.36313 5.26177 4.28049 5.30223C4.19785 5.3427 4.10826 5.36605 4.01684 5.37096C3.92541 5.37587 3.83394 5.36223 3.74764 5.33084C3.66135 5.29944 3.58192 5.2509 3.51389 5.18798C3.44586 5.12506 3.39057 5.04899 3.35117 4.96413C3.31176 4.87926 3.28902 4.78726 3.28425 4.69337C3.27947 4.59948 3.29274 4.50554 3.32331 4.41692C3.35388 4.32829 3.40115 4.24672 3.46242 4.17686ZM14.1744 12.488H0.697103C0.51222 12.488 0.334909 12.5635 0.204177 12.6977C0.0734445 12.832 0 13.0141 0 13.2039C0 13.3938 0.0734445 13.5759 0.204177 13.7101C0.334909 13.8444 0.51222 13.9198 0.697103 13.9198H14.1744C14.3593 13.9198 14.5366 13.8444 14.6673 13.7101C14.7981 13.5759 14.8715 13.3938 14.8715 13.2039C14.8715 13.0141 14.7981 12.832 14.6673 12.6977C14.5366 12.5635 14.3593 12.488 14.1744 12.488Z", :fill => "#31B404"}
+ Submit new ontology
+ .browse-search-bar
+ %input{:name => "search", :placeholder => "Ex: Agrovoc ...", :type => "text"}/
+ %select#format.browse-format-filter{:name => "format"}
+ %option{:disabled => "disabled", :hidden => "", :selected => "selected", :value => ""} Format
+ %option{:value => "OBO"} OBO
+ %option{:value => "OWL"} OWL
+ %option{:value => "SKOS"} SKOS
+ %option{:value => "UMLS"} UMLS
+ %select#Sort_by.browse-sort-by-filter{:name => "Sort_by"}
+ %option{:disabled => "disabled", :hidden => "", :selected => "selected", :value => ""} Sort by
+ %option{:value => "Popular"} Popular
+ %option{:value => "Name"} Name
+ %option{:value => "class count"} class count
+ %option{:value => "Instances/Concepts count"} Instances/Concepts count
+ %option{:value => "Projects"} Projects
+ %option{:value => "Notes"} Notes
+ %option{:value => "Upload date"} Upload date
+ %option{:value => "Release date"} Release date
+ %option{:value => "FAIR"} FAIR
+ .browse-search-button
+ %img{:src => "#{asset_path("search.svg")}"}/
+ %p Search
+ %br/
+ .browse-sub-container
+ .browse-first-row
+ %p.browse-filters-title Filters
+ %hr#browse-sub-title-line/
+ .browse-filter
+ .browse-filter-title-bar{"data-target" => "#browse-category-filtre", "data-toggle" => "collapse"}
+ %p Category
+ %img{:src => "#{asset_path("arrow-down.svg")}"}/
+ #browse-category-filtre.collapse
+ .browse-filter-checks-container
+ - @categories.each do |category|
+ %div
+ = render ChipsComponent.new(name:category.id, value: category.name)
+ .browse-filter
+ .browse-filter-title-bar{"data-target" => "#browse-group-filtre", "data-toggle" => "collapse"}
+ %p Group
+ %img{:src => "#{asset_path("arrow-down.svg")}"}/
+ #browse-group-filtre.collapse
+ .browse-filter-checks-container
+ - @groups.each do |group|
+ %div
+ = render ChipsComponent.new(name:group.id, value: group.name)
+ .browse-filter
+ .browse-filter-title-bar{"data-target" => "#browse-format-filtre", "data-toggle" => "collapse"}
+ %p Format
+ %img{:src => "#{asset_path("arrow-down.svg")}"}/
+ #browse-format-filtre.collapse
+ .browse-filter-checks-container
+ - @formats.each do |format|
+ %div
+ = render ChipsComponent.new(name:format, value: format)
+ .browse-filter
+ .browse-filter-title-bar{"data-target" => "#browse-ontology-content-filtre", "data-toggle" => "collapse"}
+ %p Ontology content
+ %img{:src => "#{asset_path("arrow-down.svg")}"}/
+ .browse-filter
+ .browse-filter-title-bar{"data-target" => "#browse-natural-language-filtre", "data-toggle" => "collapse"}
+ %p Natural language
+ %img{:src => "#{asset_path("arrow-down.svg")}"}/
+ .browse-filter
+ .browse-filter-title-bar{"data-target" => "#browse-formality-levels-filtre", "data-toggle" => "collapse"}
+ %p Formality levels
+ %img{:src => "#{asset_path("arrow-down.svg")}"}/
+ .browse-filter
+ .browse-filter-title-bar{"data-target" => "#browse-is-of-type-filtre", "data-toggle" => "collapse"}
+ %p Is of type
+ %img{:src => "#{asset_path("arrow-down.svg")}"}/
+ .browse-second-row
+ %p.browse-desc-text{:style => "margin-bottom: 15px;"}
+ Showing 147 of 152
+ - @ontologies.each do |ontology|
+ .browse-ontology-container
+ .browse-ontology-description
+ %a.browse-ontology-title{:href => "/ontologies/#{ontology[:acronym]}"}
+ = ontology[:name]+" ("+ontology[:acronym]+")"
+ %p.browse-desc-text
+ = ontology[:description]
+ - unless ontology[:fairScore].nil? || ontology[:acronym] == 'AGROVOC'
+ .browse-fair
+ %p.browse-fair-title
+ FAIR score
+ .browse-progress-bar
+ .browse-faire-progress{:style => "width: #{ontology[:normalizedFairScore].to_s+"%"}"}
+ %p.browse-fair-score
+ = ontology[:fairScore]
+ %a.browse-fair-details{:href => "/ontologies/#{ontology[:acronym]}#fair-details"} FAIR details ...
+ .browse-uploaded
+ %p Uploaded:
+ %p.browse-uploaded-date
+ = ontology[:creationDate].to_s[0, 10]
+ .browse-ontology-cards
+ - if ontology[:format] != 'SKOS' && ontology[:class_count] > 0
+ %a.browse-onology-card{:href => "/ontologies/#{ontology[:acronym]}?p=classes"}
+ %p.browse-card-number
+ = ontology[:class_count_formatted]
+ %p.browse-card-text
+ classes
+ - if ontology[:individual_count] > 0
+ %a.browse-onology-card{:href => "/ontologies/#{ontology[:acronym]}?p=classes"}
+ %p.browse-card-number
+ = ontology[:individual_count_formatted]
+ %p.browse-card-text
+ = ontology[:format] == 'SKOS' ? "concepts" : "instances"
+ - if ontology[:project_count] > 0
+ %a.browse-onology-card{:href => "/ontologies/#{ontology[:acronym]}#projects_content"}
+ %p.browse-card-number
+ = ontology[:project_count]
+ %p.browse-card-text
+ projects
+ - if ontology[:note_count] > 0
+ %a.browse-onology-card{:href => "#"}
+ %p.browse-card-number
+ = ontology[:note_count]
+ %p.browse-card-text
+ notes
+
From ceeedb1b783d083bbc4987c060ce83c3d0f8fe14 Mon Sep 17 00:00:00 2001
From: Bilelkihal <61744974+Bilelkihal@users.noreply.github.com>
Date: Mon, 27 Mar 2023 16:10:38 +0200
Subject: [PATCH 02/61] Fix the problems in the design of the browse page and
make it responsive
---
app/assets/stylesheets/browse.scss | 227 +++++++++++++-----
app/views/ontologies/browse.html.erb | 324 --------------------------
app/views/ontologies/browse.html.haml | 198 ++++++----------
3 files changed, 242 insertions(+), 507 deletions(-)
delete mode 100644 app/views/ontologies/browse.html.erb
diff --git a/app/assets/stylesheets/browse.scss b/app/assets/stylesheets/browse.scss
index 9ba47a4f44..6fdd6188fa 100644
--- a/app/assets/stylesheets/browse.scss
+++ b/app/assets/stylesheets/browse.scss
@@ -10,19 +10,44 @@
margin: unset;
}
-
-a{
- text-decoration: none !important;
-}
.browse-center{
display: flex;
justify-content: center;
margin: 50px 0;
}
.browse-title{
- font-size: 18px;
+ font-size: 22px;
font-weight: 700;
}
+
+.browse-submit-new-ontology-and-you-are-admin-container{
+ display: flex;
+ margin-bottom: 20px;
+
+}
+.browse-you-are-admin{
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ border: 1px solid #DFDFDF;
+ border-radius: 5px;
+ padding: 20px;
+ margin-left: 180px
+}
+.browse-you-are-admin h3{
+ font-size: 20px;
+ font-weight: 600;
+
+}
+.browse-you-are-admin div{
+ display: flex;
+
+ align-items: center;
+
+}
+.browse-you-are-admin div p{
+ margin-left: 20px;
+}
.browse-upload-ontology-button{
margin: 20px 0;
display: flex;
@@ -67,6 +92,7 @@ a{
.browse-sub-container{
display: flex;
+ margin-top: 90px;
}
.browse-first-row{
width: 300px;
@@ -132,7 +158,7 @@ a{
.browse-ontology-cards{
display: grid;
grid-template-columns: 113px 113px;
- grid-template-rows: 78px 78px;
+ grid-template-rows: min-content;
grid-column-gap: 15px;
grid-row-gap: 15px;
direction: rtl;
@@ -167,6 +193,7 @@ a{
font-weight: 600;
font-size: 16px;
}
+
.browse-search-bar{
display: flex;
@@ -175,12 +202,12 @@ a{
border: none;
border-radius: 14px;
width: 100%;
- box-shadow: rgba(100, 100, 111, 0.1) 0px 7px 29px 0px;
+ box-shadow: rgba(100, 100, 111, 0.08) 0px 7px 29px 0px;
padding: 20px 20px;
margin-bottom: 20px;
font-size: 16px;
position: absolute;
- width: 972px;
+ width: 1165px;
z-index: 1;
}
@@ -209,13 +236,13 @@ a{
.browse-search-bar .browse-format-filter{
position: absolute;
- margin-left: 526px;
+ margin-left: 719px;
z-index: 2;
width: 150px;
}
.browse-search-bar .browse-sort-by-filter{
position: absolute;
- margin-left: 676px;
+ margin-left: 869px;
z-index: 2;
width: 296px;
border-radius: 0 14px 14px 0;
@@ -261,55 +288,145 @@ a{
cursor: pointer;
}
+.browse-ontology-title-bar{
+ display: flex;
+ align-items: center;
+}
+.browse-ontology-deprecated{
+ background-color: #EB433521;
+ color: #EB4335;
+ padding: 10px;
+ font-size: 14px;
+ border-radius: 8px;
+ margin-left: 15px;
+ margin-right: 15px;
+ font-weight: 500;
+}
+.browse-ontology-view{
+ background-color: var(--light-color);
+ color: var(--primary-color);
+ padding: 10px;
+ font-size: 14px;
+ border-radius: 8px;
+ margin-left: 15px;
+ margin-right: 15px;
+ font-weight: 500;
+}
+
+.browse-switch-filter{
+ display:flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 20px;
+}
+.browse-switch-filter p{
+ font-size: 16px;
+ color: #666666;
+}
+
-/* Toggle switch css */
-.switch {
- position: relative;
- display: inline-block;
- width: 40px;
- height: 20px;
-}
-.switch input {
- opacity: 0;
- width: 0;
- height: 0;
-}
-.slider {
- position: absolute;
- cursor: pointer;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background-color: #ccc;
- -webkit-transition: .4s;
- transition: .4s;
- border-radius: 34px;
-}
-.slider:before {
- position: absolute;
- content: "";
- height: 13px;
- width: 13px;
- left: 4px;
- bottom: 4px;
- background-color: white;
- -webkit-transition: .4s;
- transition: .4s;
- border-radius: 50%;
+.browse-ontology-title-bar{
+ display: flex;
+ align-items: center;
}
-input:checked + .slider {
- background-color: var(--primary-color);
-}
-input:focus + .slider {
- box-shadow: 0 0 1px var(--primary-color);
+.browse-ontology-deprecated{
+ background-color: #EB433521;
+ color: #EB4335;
+ padding: 10px;
+ font-size: 14px;
+ border-radius: 8px;
+ margin-left: 15px;
+ font-weight: 500;
+}
+.browse-ontology-view{
+ background-color: var(--light-color);
+ color: var(--primary-color);
+ padding: 10px;
+ font-size: 14px;
+ border-radius: 8px;
+ margin-left: 15px;
+ font-weight: 500;
+}
+
+
+
+
+@media only screen and (max-width: 1250px) {
+ .browse-first-row{
+ width: 24vw;
+ margin-right: 20px;
+ }
+
+ .browse-second-row{
+ width: 68vw;
+ }
+ .browse-search-bar input{
+ width: 94vw;
+ }
+ .browse-search-bar .browse-format-filter{
+ margin-left: 50%
+ }
+ .browse-search-bar .browse-sort-by-filter{
+ margin-left:65%
+ }
+
}
-input:checked + .slider:before {
- -webkit-transform: translateX(19px);
- -ms-transform: translateX(19px);
- transform: translateX(19px);
+@media only screen and (max-width: 1005px) {
+ .browse-sub-container{
+ display: block;
+ }
+ .browse-second-row{
+ width: 94vw;
+ }
+ .browse-first-row{
+ width: 94vw;
+ }
+ .browse-search-bar input{
+ position: static ;
+ margin-bottom: 1vw;
+ }
+ .browse-search-bar .browse-format-filter{
+ margin-left: 0;
+ position: static;
+ border-radius: 14px;
+ width:46.5vw;
+ box-shadow: rgba(100, 100, 111, 0.1) 0px 7px 29px 0px;
+ margin-right: 1vw;
+ }
+ .browse-search-bar .browse-sort-by-filter{
+ margin-left: 0;
+ position: static;
+ border-radius: 14px;
+ width:46vw;
+ box-shadow: rgba(100, 100, 111, 0.1) 0px 7px 29px 0px;
+ }
+
+ .browse-sub-container{
+ margin-top: 20px;
+ }
+ .browse-center{
+ margin-left: 20px;
+ }
}
-.account-page-subscribe-button{
+@media only screen and (max-width: 893px){
+ .browse-ontology-container{
+ display: block;
+ }
+ .browse-ontology-description{
+ width: 100%;
+ }
+ .browse-ontology-cards{
+ direction:ltr;
+ margin-top: 20px;
+ }
+ .browse-ontology-deprecated{
+ margin-right: 0;
+ }
+ .browse-ontology-view{
+ margin-right: 0;
+ }
+
}
+
diff --git a/app/views/ontologies/browse.html.erb b/app/views/ontologies/browse.html.erb
deleted file mode 100644
index c35fdd01cb..0000000000
--- a/app/views/ontologies/browse.html.erb
+++ /dev/null
@@ -1,324 +0,0 @@
-
-
-
-
-
-
Ontologies loading
-
please wait...
-
-
-
- Browse
-
- <%= t('ontologies.intro').html_safe %>
- <%= link_to(Rails.configuration.settings.links[:help_ontology_browse], id: 'ontology-browse-help',
- 'aria-label': 'View ontology browse help') do %>
-
- <% end %>
-
-
-
-
- Welcome admin
This coloring indicates admin-only features
-
-
-
-
Debug Info
- types: {{facets.types.active}}
- artifacts: {{facets.artifacts.active}}
- formats: {{facets.formats.active}}
- groups: {{facets.groups.active}}
- categories: {{facets.categories.active}}
- Selected ontologies: {{visible_ont_count}}
-
-
-
- <%if session[:user].nil?%>
-
- Submit New Ontology
-
- <%else%>
-
- Submit New Ontology
-
- <%end%>
-
-
-
-
Entry Type
-
-
-
-
-
-
-
-
-
-
-
Uploaded in the Last
-
-
-
-
-
-
-
Category
-
-
-
-
- ({{facet_counts["categories"][category.id] || 0}})
-
-
-
-
-
-
-
Group
-
-
-
-
- ({{facet_counts["groups"][group.id] || 0}})
-
-
-
-
-
-
-
-
-
Ontology Content
-
-
-
-
- ({{facet_counts["artifacts"][artifact] || 0}})
-
-
-
-
-
-
-
Natural Language
-
-
-
-
- ({{facet_counts["natural_languages"][language_uri] || 0}})
-
-
-
-
-
-
-
-
-
Is of Type
-
-
-
-
- ({{facet_counts["is_of_type"][is_of_type_uri] || 0}})
-
-
-
-
-
-
-
Missing Status
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Showing {{visible_ont_count}} of {{ontologies.length}}
-
-
-
-
-
-
-
-
-
-
VIEW
-
-
-
{{ontology.description | descriptionToText}}
-
- Uploaded: {{ontology.creationDate | date:'shortDate'}}
-
-
- Format: {{ontology.format}}
-
-
- View of: {{ontology.viewOfOnt.acronym}}
-
-
- Summary Only
-
-
- Groups: {{groupAcronyms(ontology.groups).join(', ')}}
-
-
- Categories: {{categoryNames(ontology.categories).join(', ')}}
-
-
-
- Admins: {{adminUsernames(ontology.administeredBy).join(', ')}}
-
-
- Pull URL
-
-
- Status: {{ontology.submissionStatusFormatted}}
-
-
-
-
-
No submissions available
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/views/ontologies/browse.html.haml b/app/views/ontologies/browse.html.haml
index 3208034da7..ddedb61a31 100644
--- a/app/views/ontologies/browse.html.haml
+++ b/app/views/ontologies/browse.html.haml
@@ -1,133 +1,75 @@
+
+
.browse-center
.browse-container
%h2.browse-title Browse
%hr#browse-title-line/
- - if session[:user].nil?
- %a.browse-upload-ontology-button{:href => "/login?redirect=/ontologies/new"}
- %svg{:fill => "none", :height => "14", :viewbox => "0 0 15 14", :width => "15", :xmlns => "http://www.w3.org/2000/svg"}
- %path{:d => "M3.46242 4.17686L6.91776 0.23682C6.98312 0.162278 7.06302 0.102677 7.15225 0.0618794C7.24149 0.0210821 7.33808 0 7.43576 0C7.53344 0 7.63003 0.0210821 7.71927 0.0618794C7.80851 0.102677 7.8884 0.162278 7.95377 0.23682L11.4092 4.17686C11.533 4.31795 11.5971 4.50373 11.5874 4.69334C11.5777 4.88295 11.4951 5.06086 11.3577 5.18792C11.2204 5.31498 11.0395 5.38079 10.8548 5.37087C10.6702 5.36095 10.497 5.27612 10.3732 5.13503L8.13286 2.58055V10.4116C8.13286 10.6015 8.05942 10.7836 7.92869 10.9178C7.79796 11.0521 7.62065 11.1275 7.43576 11.1275C7.25088 11.1275 7.07357 11.0521 6.94284 10.9178C6.8121 10.7836 6.73866 10.6015 6.73866 10.4116V2.58058L4.49846 5.13512C4.43719 5.20498 4.36313 5.26177 4.28049 5.30223C4.19785 5.3427 4.10826 5.36605 4.01684 5.37096C3.92541 5.37587 3.83394 5.36223 3.74764 5.33084C3.66135 5.29944 3.58192 5.2509 3.51389 5.18798C3.44586 5.12506 3.39057 5.04899 3.35117 4.96413C3.31176 4.87926 3.28902 4.78726 3.28425 4.69337C3.27947 4.59948 3.29274 4.50554 3.32331 4.41692C3.35388 4.32829 3.40115 4.24672 3.46242 4.17686ZM14.1744 12.488H0.697103C0.51222 12.488 0.334909 12.5635 0.204177 12.6977C0.0734445 12.832 0 13.0141 0 13.2039C0 13.3938 0.0734445 13.5759 0.204177 13.7101C0.334909 13.8444 0.51222 13.9198 0.697103 13.9198H14.1744C14.3593 13.9198 14.5366 13.8444 14.6673 13.7101C14.7981 13.5759 14.8715 13.3938 14.8715 13.2039C14.8715 13.0141 14.7981 12.832 14.6673 12.6977C14.5366 12.5635 14.3593 12.488 14.1744 12.488Z", :fill => "#31B404"}
- Submit new ontology
- - else
- %a.browse-upload-ontology-button{:href => "/ontologies/new"}
- %svg{:fill => "none", :height => "14", :viewbox => "0 0 15 14", :width => "15", :xmlns => "http://www.w3.org/2000/svg"}
- %path{:d => "M3.46242 4.17686L6.91776 0.23682C6.98312 0.162278 7.06302 0.102677 7.15225 0.0618794C7.24149 0.0210821 7.33808 0 7.43576 0C7.53344 0 7.63003 0.0210821 7.71927 0.0618794C7.80851 0.102677 7.8884 0.162278 7.95377 0.23682L11.4092 4.17686C11.533 4.31795 11.5971 4.50373 11.5874 4.69334C11.5777 4.88295 11.4951 5.06086 11.3577 5.18792C11.2204 5.31498 11.0395 5.38079 10.8548 5.37087C10.6702 5.36095 10.497 5.27612 10.3732 5.13503L8.13286 2.58055V10.4116C8.13286 10.6015 8.05942 10.7836 7.92869 10.9178C7.79796 11.0521 7.62065 11.1275 7.43576 11.1275C7.25088 11.1275 7.07357 11.0521 6.94284 10.9178C6.8121 10.7836 6.73866 10.6015 6.73866 10.4116V2.58058L4.49846 5.13512C4.43719 5.20498 4.36313 5.26177 4.28049 5.30223C4.19785 5.3427 4.10826 5.36605 4.01684 5.37096C3.92541 5.37587 3.83394 5.36223 3.74764 5.33084C3.66135 5.29944 3.58192 5.2509 3.51389 5.18798C3.44586 5.12506 3.39057 5.04899 3.35117 4.96413C3.31176 4.87926 3.28902 4.78726 3.28425 4.69337C3.27947 4.59948 3.29274 4.50554 3.32331 4.41692C3.35388 4.32829 3.40115 4.24672 3.46242 4.17686ZM14.1744 12.488H0.697103C0.51222 12.488 0.334909 12.5635 0.204177 12.6977C0.0734445 12.832 0 13.0141 0 13.2039C0 13.3938 0.0734445 13.5759 0.204177 13.7101C0.334909 13.8444 0.51222 13.9198 0.697103 13.9198H14.1744C14.3593 13.9198 14.5366 13.8444 14.6673 13.7101C14.7981 13.5759 14.8715 13.3938 14.8715 13.2039C14.8715 13.0141 14.7981 12.832 14.6673 12.6977C14.5366 12.5635 14.3593 12.488 14.1744 12.488Z", :fill => "#31B404"}
- Submit new ontology
- .browse-search-bar
- %input{:name => "search", :placeholder => "Ex: Agrovoc ...", :type => "text"}/
- %select#format.browse-format-filter{:name => "format"}
- %option{:disabled => "disabled", :hidden => "", :selected => "selected", :value => ""} Format
- %option{:value => "OBO"} OBO
- %option{:value => "OWL"} OWL
- %option{:value => "SKOS"} SKOS
- %option{:value => "UMLS"} UMLS
- %select#Sort_by.browse-sort-by-filter{:name => "Sort_by"}
- %option{:disabled => "disabled", :hidden => "", :selected => "selected", :value => ""} Sort by
- %option{:value => "Popular"} Popular
- %option{:value => "Name"} Name
- %option{:value => "class count"} class count
- %option{:value => "Instances/Concepts count"} Instances/Concepts count
- %option{:value => "Projects"} Projects
- %option{:value => "Notes"} Notes
- %option{:value => "Upload date"} Upload date
- %option{:value => "Release date"} Release date
- %option{:value => "FAIR"} FAIR
- .browse-search-button
- %img{:src => "#{asset_path("search.svg")}"}/
- %p Search
- %br/
- .browse-sub-container
- .browse-first-row
- %p.browse-filters-title Filters
- %hr#browse-sub-title-line/
- .browse-filter
- .browse-filter-title-bar{"data-target" => "#browse-category-filtre", "data-toggle" => "collapse"}
- %p Category
- %img{:src => "#{asset_path("arrow-down.svg")}"}/
- #browse-category-filtre.collapse
- .browse-filter-checks-container
- - @categories.each do |category|
- %div
- = render ChipsComponent.new(name:category.id, value: category.name)
- .browse-filter
- .browse-filter-title-bar{"data-target" => "#browse-group-filtre", "data-toggle" => "collapse"}
- %p Group
- %img{:src => "#{asset_path("arrow-down.svg")}"}/
- #browse-group-filtre.collapse
- .browse-filter-checks-container
- - @groups.each do |group|
- %div
- = render ChipsComponent.new(name:group.id, value: group.name)
- .browse-filter
- .browse-filter-title-bar{"data-target" => "#browse-format-filtre", "data-toggle" => "collapse"}
- %p Format
- %img{:src => "#{asset_path("arrow-down.svg")}"}/
- #browse-format-filtre.collapse
- .browse-filter-checks-container
- - @formats.each do |format|
- %div
- = render ChipsComponent.new(name:format, value: format)
- .browse-filter
- .browse-filter-title-bar{"data-target" => "#browse-ontology-content-filtre", "data-toggle" => "collapse"}
- %p Ontology content
- %img{:src => "#{asset_path("arrow-down.svg")}"}/
- .browse-filter
- .browse-filter-title-bar{"data-target" => "#browse-natural-language-filtre", "data-toggle" => "collapse"}
- %p Natural language
- %img{:src => "#{asset_path("arrow-down.svg")}"}/
- .browse-filter
- .browse-filter-title-bar{"data-target" => "#browse-formality-levels-filtre", "data-toggle" => "collapse"}
- %p Formality levels
- %img{:src => "#{asset_path("arrow-down.svg")}"}/
- .browse-filter
- .browse-filter-title-bar{"data-target" => "#browse-is-of-type-filtre", "data-toggle" => "collapse"}
- %p Is of type
- %img{:src => "#{asset_path("arrow-down.svg")}"}/
- .browse-second-row
- %p.browse-desc-text{:style => "margin-bottom: 15px;"}
- Showing 147 of 152
- - @ontologies.each do |ontology|
- .browse-ontology-container
- .browse-ontology-description
- %a.browse-ontology-title{:href => "/ontologies/#{ontology[:acronym]}"}
- = ontology[:name]+" ("+ontology[:acronym]+")"
- %p.browse-desc-text
- = ontology[:description]
- - unless ontology[:fairScore].nil? || ontology[:acronym] == 'AGROVOC'
- .browse-fair
- %p.browse-fair-title
- FAIR score
- .browse-progress-bar
- .browse-faire-progress{:style => "width: #{ontology[:normalizedFairScore].to_s+"%"}"}
- %p.browse-fair-score
- = ontology[:fairScore]
- %a.browse-fair-details{:href => "/ontologies/#{ontology[:acronym]}#fair-details"} FAIR details ...
- .browse-uploaded
- %p Uploaded:
- %p.browse-uploaded-date
- = ontology[:creationDate].to_s[0, 10]
- .browse-ontology-cards
- - if ontology[:format] != 'SKOS' && ontology[:class_count] > 0
- %a.browse-onology-card{:href => "/ontologies/#{ontology[:acronym]}?p=classes"}
- %p.browse-card-number
- = ontology[:class_count_formatted]
- %p.browse-card-text
- classes
- - if ontology[:individual_count] > 0
- %a.browse-onology-card{:href => "/ontologies/#{ontology[:acronym]}?p=classes"}
- %p.browse-card-number
- = ontology[:individual_count_formatted]
- %p.browse-card-text
- = ontology[:format] == 'SKOS' ? "concepts" : "instances"
- - if ontology[:project_count] > 0
- %a.browse-onology-card{:href => "/ontologies/#{ontology[:acronym]}#projects_content"}
- %p.browse-card-number
- = ontology[:project_count]
- %p.browse-card-text
- projects
- - if ontology[:note_count] > 0
- %a.browse-onology-card{:href => "#"}
- %p.browse-card-number
- = ontology[:note_count]
- %p.browse-card-text
- notes
+ .browse-submit-new-ontology-and-you-are-admin-container
+ - if session[:user].nil?
+ %a.browse-upload-ontology-button{:href => "/login?redirect=/ontologies/new"}
+ %svg{:fill => "none", :height => "14", :viewbox => "0 0 15 14", :width => "15", :xmlns => "http://www.w3.org/2000/svg"}
+ %path{:d => "M3.46242 4.17686L6.91776 0.23682C6.98312 0.162278 7.06302 0.102677 7.15225 0.0618794C7.24149 0.0210821 7.33808 0 7.43576 0C7.53344 0 7.63003 0.0210821 7.71927 0.0618794C7.80851 0.102677 7.8884 0.162278 7.95377 0.23682L11.4092 4.17686C11.533 4.31795 11.5971 4.50373 11.5874 4.69334C11.5777 4.88295 11.4951 5.06086 11.3577 5.18792C11.2204 5.31498 11.0395 5.38079 10.8548 5.37087C10.6702 5.36095 10.497 5.27612 10.3732 5.13503L8.13286 2.58055V10.4116C8.13286 10.6015 8.05942 10.7836 7.92869 10.9178C7.79796 11.0521 7.62065 11.1275 7.43576 11.1275C7.25088 11.1275 7.07357 11.0521 6.94284 10.9178C6.8121 10.7836 6.73866 10.6015 6.73866 10.4116V2.58058L4.49846 5.13512C4.43719 5.20498 4.36313 5.26177 4.28049 5.30223C4.19785 5.3427 4.10826 5.36605 4.01684 5.37096C3.92541 5.37587 3.83394 5.36223 3.74764 5.33084C3.66135 5.29944 3.58192 5.2509 3.51389 5.18798C3.44586 5.12506 3.39057 5.04899 3.35117 4.96413C3.31176 4.87926 3.28902 4.78726 3.28425 4.69337C3.27947 4.59948 3.29274 4.50554 3.32331 4.41692C3.35388 4.32829 3.40115 4.24672 3.46242 4.17686ZM14.1744 12.488H0.697103C0.51222 12.488 0.334909 12.5635 0.204177 12.6977C0.0734445 12.832 0 13.0141 0 13.2039C0 13.3938 0.0734445 13.5759 0.204177 13.7101C0.334909 13.8444 0.51222 13.9198 0.697103 13.9198H14.1744C14.3593 13.9198 14.5366 13.8444 14.6673 13.7101C14.7981 13.5759 14.8715 13.3938 14.8715 13.2039C14.8715 13.0141 14.7981 12.832 14.6673 12.6977C14.5366 12.5635 14.3593 12.488 14.1744 12.488Z", :fill => "#31B404"}
+ Submit new ontology
+ - else
+ %a.browse-upload-ontology-button{:href => "/ontologies/new"}
+ %svg{:fill => "none", :height => "14", :viewbox => "0 0 15 14", :width => "15", :xmlns => "http://www.w3.org/2000/svg"}
+ %path{:d => "M3.46242 4.17686L6.91776 0.23682C6.98312 0.162278 7.06302 0.102677 7.15225 0.0618794C7.24149 0.0210821 7.33808 0 7.43576 0C7.53344 0 7.63003 0.0210821 7.71927 0.0618794C7.80851 0.102677 7.8884 0.162278 7.95377 0.23682L11.4092 4.17686C11.533 4.31795 11.5971 4.50373 11.5874 4.69334C11.5777 4.88295 11.4951 5.06086 11.3577 5.18792C11.2204 5.31498 11.0395 5.38079 10.8548 5.37087C10.6702 5.36095 10.497 5.27612 10.3732 5.13503L8.13286 2.58055V10.4116C8.13286 10.6015 8.05942 10.7836 7.92869 10.9178C7.79796 11.0521 7.62065 11.1275 7.43576 11.1275C7.25088 11.1275 7.07357 11.0521 6.94284 10.9178C6.8121 10.7836 6.73866 10.6015 6.73866 10.4116V2.58058L4.49846 5.13512C4.43719 5.20498 4.36313 5.26177 4.28049 5.30223C4.19785 5.3427 4.10826 5.36605 4.01684 5.37096C3.92541 5.37587 3.83394 5.36223 3.74764 5.33084C3.66135 5.29944 3.58192 5.2509 3.51389 5.18798C3.44586 5.12506 3.39057 5.04899 3.35117 4.96413C3.31176 4.87926 3.28902 4.78726 3.28425 4.69337C3.27947 4.59948 3.29274 4.50554 3.32331 4.41692C3.35388 4.32829 3.40115 4.24672 3.46242 4.17686ZM14.1744 12.488H0.697103C0.51222 12.488 0.334909 12.5635 0.204177 12.6977C0.0734445 12.832 0 13.0141 0 13.2039C0 13.3938 0.0734445 13.5759 0.204177 13.7101C0.334909 13.8444 0.51222 13.9198 0.697103 13.9198H14.1744C14.3593 13.9198 14.5366 13.8444 14.6673 13.7101C14.7981 13.5759 14.8715 13.3938 14.8715 13.2039C14.8715 13.0141 14.7981 12.832 14.6673 12.6977C14.5366 12.5635 14.3593 12.488 14.1744 12.488Z", :fill => "#31B404"}
+ Submit new ontology
+ - if session[:user]&.admin?
+ .browse-you-are-admin
+ %h3 Welcome admin
+ %div
+ %svg{:fill => "none", :height => "11", :viewbox => "0 0 11 11", :width => "11", :xmlns => "http://www.w3.org/2000/svg"}
+ %circle{:cx => "5.5", :cy => "5.5", :fill => "#145FF4", :r => "5.5"}
+ %p This coloring indicates admin-only features
+
+ %div{data: { controller: "turbo-frame history browse-filters" , "turbo-frame-url-value": "/ontologies_filter", action: "change->browse-filters#dispatchFilterEvent changed->history#updateURL changed->turbo-frame#updateFrame"}}
+ .browse-search-bar
+ .browse-search-container
+ %input{:name => "search", :placeholder => "Ex: Agrovoc ...", :type => "text", :value => @search, data: {action: "input->browse-filters#dispatchInputEvent"}}
+ .browse-search-filters
+ %select#format.browse-format-filter{:name => "format"}
+ - format= [["All formats",""], "OBO", "OWL", "SKOS", "UMLS"]
+ = options_for_select(format, @selected_format)
+ %select#Sort_by.browse-sort-by-filter{:name => "Sort_by"}
+ %option{:disabled => "disabled", :hidden => "", :selected => "selected", :value => ""} Sort by
+ %option{:value => "Popular"} Popular
+ %option{:value => "Name"} Name
+ %option{:value => "class count"} class count
+ %option{:value => "Instances/Concepts count"} Instances/Concepts count
+ %option{:value => "Projects"} Projects
+ %option{:value => "Notes"} Notes
+ %option{:value => "Upload date"} Upload date
+ %option{:value => "Release date"} Release date
+ %option{:value => "FAIR"} FAIR
+
+ .browse-sub-container
+ .browse-first-row{data:{controller: "browse-filters", action: "change->browse-filters#dispatchFilterEvent changed->history#updateURL changed->turbo-frame#updateFrame"}}
+ %p.browse-filters-title Filters
+ %hr#browse-sub-title-line
+ = render SwitchInputComponent.new(id:'filter-views', name:'views', checked: @show_views) do
+ Show ontology views
+ = render SwitchInputComponent.new(id:'filter-retired', name:'retired',checked: @show_retired) do
+ Show retired ontologies
+
+ - @filters.each do |key, values|
+ - if session[:user]&.admin? || key != :missingStatus
+ .browse-filter{data:{controller: "show-filter-count browse-filters", action: "change->show-filter-count#updateCount change->browse-filters#dispatchFilterEvent"}, id: "#{key}_filter_container", style: "#{"border-color: #145FF4;" if key == :missingStatus}"}
+ .browse-filter-title-bar{"data-target" => "#browse-#{key}-filter", "data-toggle" => "collapse"}
+ %p
+ #{key.to_s.underscore.humanize.capitalize}
+ %span.badge.badge-secondary{"data-show-filter-count-target":"countSpan", style: "#{values[2] && values[2].positive? ? '' : 'display: none;'}"}
+ = values[2]
+ %img{:src => "#{asset_path("arrow-down.svg")}"}/
+ .collapse{id: "browse-#{key}-filter", class: "#{values[2].positive? ? 'show': ''}"}
+ .browse-filter-checks-container
+ - values.first.each do |object|
+ %div
+ = render ChipsComponent.new(name: object["acronym"], value: object["name"] || object["id"], checked: values[1]&.include?(object["id"]))
+
+
+ .browse-second-row
+ = render TurboFrameComponent.new(id:"search-results" , src: "/ontologies_filter?#{request.original_url.split('?').last}",
+ data:{"turbo-frame-target":"frame", "turbo-frame-url-value": "/ontologies_filter"})
+
From b2566f7d146856587c0a94c308fbef8801d313d2 Mon Sep 17 00:00:00 2001
From: Bilelkihal <61744974+Bilelkihal@users.noreply.github.com>
Date: Mon, 27 Mar 2023 16:11:22 +0200
Subject: [PATCH 03/61] Add the light color variable for differnet themes
---
app/assets/stylesheets/theme-variables.scss.erb | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/app/assets/stylesheets/theme-variables.scss.erb b/app/assets/stylesheets/theme-variables.scss.erb
index 4a7c79a68d..e0209de2cc 100644
--- a/app/assets/stylesheets/theme-variables.scss.erb
+++ b/app/assets/stylesheets/theme-variables.scss.erb
@@ -6,24 +6,28 @@
--primary-color: #31B404;
--hover-color: #40C811;
--secondary-color: #ffc107;
+ --light-color: #F5FCF3;
}
<% when "stageportal" %>
:root{
--primary-color: #76A7CC;
--hover-color: #6B96B7;
--secondary-color: #ffc107;
+ --light-color: #F1F6FA;
}
<% when "bioportal" %>
:root{
--primary-color: #76A7CC;
--hover-color: #6B96B7;
--secondary-color: #ffc107;
+ --light-color: #F1F6FA;
}
<% when "ontoportal" %>
:root{
--primary-color: #6E98A2;
--hover-color: #7BABB6;
--secondary-color: #ffc107;
+ --light-color: #F0F5F6;
}
<%# Here to add a new theme ... %>
<% end %>
From 325d9b588035b9365e2c11ae49f0f749f4223af3 Mon Sep 17 00:00:00 2001
From: Bilelkihal <61744974+Bilelkihal@users.noreply.github.com>
Date: Mon, 27 Mar 2023 16:15:43 +0200
Subject: [PATCH 04/61] Extract the ontologies section in the browse page to be
the src of the turbo frame
---
app/views/ontologies/_ontologies.html.haml | 61 ++++++++++++++++++++++
config/routes.rb | 1 +
2 files changed, 62 insertions(+)
create mode 100644 app/views/ontologies/_ontologies.html.haml
diff --git a/app/views/ontologies/_ontologies.html.haml b/app/views/ontologies/_ontologies.html.haml
new file mode 100644
index 0000000000..1c6ef1735c
--- /dev/null
+++ b/app/views/ontologies/_ontologies.html.haml
@@ -0,0 +1,61 @@
+
+
+= turbo_frame_tag "search-results" do
+ %p.browse-desc-text{:style => "margin-bottom: 15px;"}
+ = "Showing "+ @ontologies.length.to_s
+ -# Here we put our turbo frame "ontologies_frame"
+ - @ontologies.each do |ontology|
+ .browse-ontology-container
+ .browse-ontology-description
+ .browse-ontology-title-bar
+ %a.browse-ontology-title{:href => "/ontologies/#{ontology[:acronym]}", data: {'turbo-frame': '_top'}}
+ = ontology[:name]+" ("+ontology[:acronym]+")"
+ - if ontology[:deprecated]
+ .browse-ontology-deprecated
+ deprecated
+ - if ontology[:viewOfOnt]
+ .browse-ontology-view
+ view
+
+ %p.browse-desc-text
+ = ontology[:description]
+
+
+ - unless ontology[:fairScore].nil? || ontology[:acronym] == 'AGROVOC'
+ .browse-fair
+ %p.browse-fair-title
+ FAIR score
+ .browse-progress-bar
+ .browse-faire-progress{:style => "width: #{ontology[:normalizedFairScore].to_s+"%"}"}
+ %p.browse-fair-score
+ = ontology[:fairScore]
+ %a.browse-fair-details{:href => "/ontologies/#{ontology[:acronym]}#fair-details"} FAIR details ...
+ .browse-uploaded
+ %p Uploaded:
+ %p.browse-uploaded-date
+ = ontology[:creationDate].to_s[0, 10]
+ .browse-ontology-cards
+ - if ontology[:format] != 'SKOS' && ontology[:class_count] > 0
+ %a.browse-onology-card{:href => "/ontologies/#{ontology[:acronym]}?p=classes"}
+ %p.browse-card-number
+ = ontology[:class_count_formatted]
+ %p.browse-card-text
+ classes
+ - if ontology[:individual_count] > 0
+ %a.browse-onology-card{:href => "/ontologies/#{ontology[:acronym]}?p=classes"}
+ %p.browse-card-number
+ = ontology[:individual_count_formatted]
+ %p.browse-card-text
+ = ontology[:format] == 'SKOS' ? "concepts" : "instances"
+ - if ontology[:project_count] > 0
+ %a.browse-onology-card{:href => "/ontologies/#{ontology[:acronym]}#projects_content"}
+ %p.browse-card-number
+ = ontology[:project_count]
+ %p.browse-card-text
+ projects
+ - if ontology[:note_count] > 0
+ %a.browse-onology-card{:href => "#"}
+ %p.browse-card-number
+ = ontology[:note_count]
+ %p.browse-card-text
+ notes
\ No newline at end of file
diff --git a/config/routes.rb b/config/routes.rb
index 057297ac71..5e82961bac 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -113,6 +113,7 @@
get '/ontologies/:acronym/classes/:purl_conceptid', to: 'ontologies#show', constraints: { purl_conceptid: /[^\/]+/ }
get '/ontologies/:acronym/: f', to: 'ontologies#show', constraints: { purl_conceptid: /[^\/]+/ }
match '/ontologies/:acronym/submissions/:id/edit_metadata' => 'submissions#edit_metadata', via: [:get, :post]
+ get '/ontologies_filter', to: 'ontologies#ontologies_filter'
# Analytics
get '/analytics/:action' => 'analytics#(?-mix:search_result_clicked|user_intention_surveys)'
From 8c2dcaf5c13f9921f57d77c6a82f417d538adb78 Mon Sep 17 00:00:00 2001
From: Bilelkihal <61744974+Bilelkihal@users.noreply.github.com>
Date: Mon, 27 Mar 2023 16:18:55 +0200
Subject: [PATCH 05/61] Change the color of the turbo progress bar
---
app/assets/stylesheets/bioportal.scss | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/app/assets/stylesheets/bioportal.scss b/app/assets/stylesheets/bioportal.scss
index 1003395d06..cae7371c95 100644
--- a/app/assets/stylesheets/bioportal.scss
+++ b/app/assets/stylesheets/bioportal.scss
@@ -1,3 +1,8 @@
+.turbo-progress-bar {
+ height: 5px;
+ background-color: var(--light-color);
+}
+
a{
text-decoration: none !important;
}
From e10b7e7f397ccfbd4d8acd93bc40f29936ff41da Mon Sep 17 00:00:00 2001
From: Bilelkihal <61744974+Bilelkihal@users.noreply.github.com>
Date: Mon, 27 Mar 2023 16:19:33 +0200
Subject: [PATCH 06/61] Update the design of the switch component
---
app/assets/stylesheets/components/index.scss | 3 +-
app/assets/stylesheets/components/switch.scss | 64 +++++++++++++++++++
app/components/switch_input_component.rb | 2 +-
.../switch_input_component.html.haml | 11 ++--
4 files changed, 74 insertions(+), 6 deletions(-)
create mode 100644 app/assets/stylesheets/components/switch.scss
diff --git a/app/assets/stylesheets/components/index.scss b/app/assets/stylesheets/components/index.scss
index c61d4d8dcc..ebeeebd4fe 100644
--- a/app/assets/stylesheets/components/index.scss
+++ b/app/assets/stylesheets/components/index.scss
@@ -1,2 +1,3 @@
@import 'chips';
-@import 'card_message'
\ No newline at end of file
+@import 'card_message';
+@import "switch";
\ No newline at end of file
diff --git a/app/assets/stylesheets/components/switch.scss b/app/assets/stylesheets/components/switch.scss
new file mode 100644
index 0000000000..5a70e0fe92
--- /dev/null
+++ b/app/assets/stylesheets/components/switch.scss
@@ -0,0 +1,64 @@
+
+.switch-filter{
+ display:flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 20px;
+}
+.switch-filter p{
+ font-size: 16px;
+ color: #666666;
+ margin-bottom: 0;
+ margin-right: 10px;
+}
+
+
+
+/* Toggle switch css */
+.switch {
+ position: relative;
+ display: inline-block;
+ width: 40px;
+ height: 20px;
+ margin-bottom: 0;
+}
+.switch input {
+ opacity: 0;
+ width: 0;
+ height: 0;
+}
+.slider {
+ position: absolute;
+ cursor: pointer;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: #ccc;
+ -webkit-transition: .4s;
+ transition: .4s;
+ border-radius: 34px;
+}
+.slider:before {
+ position: absolute;
+ content: "";
+ height: 13px;
+ width: 13px;
+ left: 4px;
+ bottom: 4px;
+ background-color: white;
+ -webkit-transition: .4s;
+ transition: .4s;
+ border-radius: 50%;
+}
+input:checked + .slider {
+ background-color: var(--primary-color);
+}
+input:focus + .slider {
+ box-shadow: 0 0 1px var(--primary-color);
+}
+input:checked + .slider:before {
+ -webkit-transform: translateX(19px);
+ -ms-transform: translateX(19px);
+ transform: translateX(19px);
+}
\ No newline at end of file
diff --git a/app/components/switch_input_component.rb b/app/components/switch_input_component.rb
index e8ba664312..3b7f677817 100644
--- a/app/components/switch_input_component.rb
+++ b/app/components/switch_input_component.rb
@@ -3,7 +3,7 @@
class SwitchInputComponent < ViewComponent::Base
- def initialize(id:, name: , label: nil, value: '', checked: false, boolean_switch: false)
+ def initialize(id:, name: , label: '', value: '', checked: false, boolean_switch: false)
super
@id = id
@name = name
diff --git a/app/components/switch_input_component/switch_input_component.html.haml b/app/components/switch_input_component/switch_input_component.html.haml
index 1f74a7c247..4fe4ccbac9 100644
--- a/app/components/switch_input_component/switch_input_component.html.haml
+++ b/app/components/switch_input_component/switch_input_component.html.haml
@@ -1,6 +1,9 @@
-%div.custom-control.custom-switch
+%div.switch-filter
- if @boolean_switch
= hidden_field_tag @name, @value
- = check_box_tag check_box_name, @value, @checked, class: 'custom-control-input', id: check_box_id, onChange: "#{boolean_switch_action}"
- %label.custom-control-label{for: check_box_id}
- = content || @label
\ No newline at end of file
+ - if content || !@label.empty?
+ %p
+ = content || @label
+ %label.switch{for: check_box_id}
+ = check_box_tag check_box_name, @value, @checked, class: '', id: check_box_id, onChange: "#{boolean_switch_action}"
+ %span.slider
\ No newline at end of file
From c57eb9faf3dcc71b0b60d3c3da72cc439c1616e1 Mon Sep 17 00:00:00 2001
From: Bilelkihal <61744974+Bilelkihal@users.noreply.github.com>
Date: Mon, 27 Mar 2023 16:20:28 +0200
Subject: [PATCH 07/61] Add the checked property to the chips component
---
app/components/chips_component.rb | 7 ++++++-
app/components/chips_component/chips_component.html.haml | 2 +-
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/app/components/chips_component.rb b/app/components/chips_component.rb
index ced043f2ed..316bcaf911 100644
--- a/app/components/chips_component.rb
+++ b/app/components/chips_component.rb
@@ -1,6 +1,11 @@
class ChipsComponent < ViewComponent::Base
- def initialize(name:, value:)
+ def initialize(name:, value:, checked: false)
@name = name
@value = value
+ @checked = checked
+ end
+
+ def checked?
+ @checked
end
end
\ No newline at end of file
diff --git a/app/components/chips_component/chips_component.html.haml b/app/components/chips_component/chips_component.html.haml
index 3e260e4318..5f8ed7f689 100644
--- a/app/components/chips_component/chips_component.html.haml
+++ b/app/components/chips_component/chips_component.html.haml
@@ -1,7 +1,7 @@
.chips-container
%div
%label{:for => "chips-#{@name}-check"}
- %input{:id => "chips-#{@name}-check", :name => @name, :type => "checkbox", :value => @value}/
+ %input{:id => "chips-#{@name}-check", :name => @name, :type => "checkbox", :value => @value, checked: checked?}
%span
%svg.chips-check-icon{:fill => "none", :height => "8", :viewbox => "0 0 10 8", :width => "10", :xmlns => "http://www.w3.org/2000/svg"}
%path{:d => "M9.76764 0.232287C9.45824 -0.0775267 8.95582 -0.0773313 8.646 0.232287L3.59787 5.28062L1.35419 3.03696C1.04438 2.72714 0.542174 2.72714 0.23236 3.03696C-0.0774534 3.34677 -0.0774534 3.84897 0.23236 4.15879L3.03684 6.96326C3.19165 7.11807 3.39464 7.19567 3.59765 7.19567C3.80067 7.19567 4.00386 7.11827 4.15867 6.96326L9.76764 1.3541C10.0775 1.0445 10.0775 0.542081 9.76764 0.232287Z"}
From 719e45604eb6a303c5ca0fae9b34753a34f19ade Mon Sep 17 00:00:00 2001
From: Bilelkihal <61744974+Bilelkihal@users.noreply.github.com>
Date: Mon, 27 Mar 2023 16:23:13 +0200
Subject: [PATCH 08/61] Add the filtering logic of the browse page
---
Gemfile.lock | 9 +-
app/controllers/ontologies_controller.rb | 375 ++++++++++++++----
.../controllers/browse_filters_controller.js | 65 +++
app/javascript/controllers/index.js | 6 +
.../controllers/turbo_frame_controller.js | 11 +-
app/javascript/mixins/useHistory.js | 43 +-
package.json | 1 +
yarn.lock | 5 +
8 files changed, 407 insertions(+), 108 deletions(-)
create mode 100644 app/javascript/controllers/browse_filters_controller.js
diff --git a/Gemfile.lock b/Gemfile.lock
index 2c9f387e88..10925e47fb 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -210,8 +210,9 @@ GEM
mime-types-data (~> 3.2015)
mime-types-data (3.2023.0218.1)
mini_mime (1.1.2)
- minitest (5.18.0)
- msgpack (1.6.1)
+ mini_portile2 (2.8.1)
+ minitest (5.17.0)
+ msgpack (1.6.0)
multi_json (1.15.0)
multipart-post (2.3.0)
mysql2 (0.5.3)
@@ -235,6 +236,9 @@ GEM
netrc (0.11.0)
newrelic_rpm (9.0.0)
nio4r (2.5.8)
+ nokogiri (1.14.2)
+ mini_portile2 (~> 2.8.0)
+ racc (~> 1.4)
nokogiri (1.14.2-x86_64-linux)
racc (~> 1.4)
oj (3.14.2)
@@ -418,6 +422,7 @@ GEM
zeitwerk (2.6.7)
PLATFORMS
+ ruby
x86_64-linux
DEPENDENCIES
diff --git a/app/controllers/ontologies_controller.rb b/app/controllers/ontologies_controller.rb
index f37a0e2a36..e8b6d3bff0 100644
--- a/app/controllers/ontologies_controller.rb
+++ b/app/controllers/ontologies_controller.rb
@@ -14,51 +14,44 @@ class OntologiesController < ApplicationController
helper :concepts
helper :fair_score
- layout :determine_layout
+ layout 'ontology'
- before_action :authorize_and_redirect, :only=>[:edit,:update,:create,:new]
+ before_action :authorize_and_redirect, :only => [:edit, :update, :create, :new]
before_action :submission_metadata, only: [:show]
- KNOWN_PAGES = Set.new(["terms", "classes", "mappings", "notes", "widgets", "summary", "properties" ,"instances", "schemes", "collections"])
+ KNOWN_PAGES = Set.new(["terms", "classes", "mappings", "notes", "widgets", "summary", "properties", "instances", "schemes", "collections"])
EXTERNAL_MAPPINGS_GRAPH = "http://data.bioontology.org/metadata/ExternalMappings"
INTERPORTAL_MAPPINGS_GRAPH = "http://data.bioontology.org/metadata/InterportalMappings"
-
# GET /ontologies
def index
- @app_name = 'FacetedBrowsing'
- @app_dir = '/browse'
- @base_path = @app_dir
- ontologies = LinkedData::Client::Models::Ontology.all(include: LinkedData::Client::Models::Ontology.include_params + ',viewOf', include_views: true, display_context: false)
- ontologies_hash = Hash[ontologies.map {|o| [o.id, o] }]
+ @categories = LinkedData::Client::Models::Category.all(display_links: false, display_context: false)
+ @groups = LinkedData::Client::Models::Group.all(display_links: false, display_context: false)
+ @filters = ontology_filters_init(@categories, @groups)
+ render 'browse'
+ end
+
+ def ontologies_filter
+ ontologies = LinkedData::Client::Models::Ontology.all(
+ include: LinkedData::Client::Models::Ontology.include_params + ',viewOf', include_views: true, display_context: false)
+ ontologies_hash = Hash[ontologies.map { |o| [o.id, o] }]
@admin = session[:user] ? session[:user].admin? : false
- @development = Rails.env.development?
- # We could get naturalLanguages, isOfType and formalityLevels from the API, but for performance we are storing it in config/bioportal_config_production.rb
- #@metadata = submission_metadata
- # The attributes used when retrieving the submission. We are not retrieving all attributes to be faster
- browse_attributes = 'ontology,acronym,submissionStatus,description,pullLocation,creationDate,released,name,naturalLanguage,hasOntologyLanguage,hasFormalityLevel,isOfType,contact'
- submissions = LinkedData::Client::Models::OntologySubmission.all(include_views: true, display_links: false,display_context: false, include: browse_attributes)
- submissions_map = Hash[submissions.map {|sub| [sub.ontology.acronym, sub] }]
+ browse_attributes = 'ontology,acronym,submissionStatus,description,pullLocation,creationDate,released,name,
+ naturalLanguage,hasOntologyLanguage,hasFormalityLevel,isOfType,contact,deprecated,status'
+ submissions = LinkedData::Client::Models::OntologySubmission.all(include_views: true, display_links: false,
+ display_context: false, include: browse_attributes)
+ submissions_map = Hash[submissions.map { |sub| [sub.ontology.acronym, sub] }]
@categories = LinkedData::Client::Models::Category.all(display_links: false, display_context: false)
- @categories_hash = Hash[@categories.map {|c| [c.id, c] }]
-
@groups = LinkedData::Client::Models::Group.all(display_links: false, display_context: false)
- @groups_hash = Hash[@groups.map {|g| [g.id, g] }]
analytics = LinkedData::Client::Analytics.last_month
- @analytics = Hash[analytics.onts.map {|o| [o[:ont].to_s, o[:views]]}]
+ @analytics = Hash[analytics.onts.map { |o| [o[:ont].to_s, o[:views]] }]
- reviews = {}
- LinkedData::Client::Models::Review.all(display_links: false, display_context: false).each do |r|
- reviews[r.reviewedOntology] ||= []
- reviews[r.reviewedOntology] << r
- end
metrics_hash = get_metrics_hash
- @formats = Set.new
#get fairscores of all ontologies
@fair_scores = fairness_service_enabled? ? get_fair_score('all') : nil;
@@ -76,30 +69,29 @@ def index
o[:class_count_formatted] = number_with_delimiter(o[:class_count], delimiter: ',')
o[:individual_count_formatted] = number_with_delimiter(o[:individual_count], delimiter: ',')
- o[:id] = ont.id
- o[:type] = ont.viewOf.nil? ? 'ontology' : 'ontology_view'
- o[:show] = ont.viewOf.nil? ? true : false # show ontologies only by default
- o[:reviews] = reviews[ont.id] || []
- o[:groups] = ont.group || []
- o[:categories] = ont.hasDomain || []
- o[:note_count] = ont.notes.length
- o[:review_count] = ont.reviews.length
- o[:project_count] = ont.projects.length
- o[:private] = ont.private?
- o[:popularity] = @analytics[ont.acronym] || 0
+ o[:id] = ont.id
+ o[:type] = ont.viewOf.nil? ? 'ontology' : 'ontology_view'
+ o[:show] = ont.viewOf.nil? ? true : false # show ontologies only by default
+ o[:groups] = ont.group || []
+ o[:categories] = ont.hasDomain || []
+ o[:note_count] = ont.notes.length
+ o[:review_count] = ont.reviews.length
+ o[:project_count] = ont.projects.length
+ o[:private] = ont.private?
+ o[:popularity] = @analytics[ont.acronym] || 0
o[:submissionStatus] = []
- o[:administeredBy] = ont.administeredBy
- o[:name] = ont.name
- o[:acronym] = ont.acronym
- o[:projects] = ont.projects
- o[:notes] = ont.notes
+ o[:administeredBy] = ont.administeredBy
+ o[:name] = ont.name
+ o[:acronym] = ont.acronym
+ o[:projects] = ont.projects
+ o[:notes] = ont.notes
if !@fair_scores.nil? && !@fair_scores[ont.acronym].nil?
- o[:fairScore] = @fair_scores[ont.acronym]['score']
- o[:normalizedFairScore] = @fair_scores[ont.acronym]['normalizedScore']
+ o[:fairScore] = @fair_scores[ont.acronym]['score']
+ o[:normalizedFairScore] = @fair_scores[ont.acronym]['normalizedScore']
else
- o[:fairScore] = nil
- o[:normalizedFairScore] = 0
+ o[:fairScore] = nil
+ o[:normalizedFairScore] = 0
end
if o[:type].eql?('ontology_view')
@@ -119,19 +111,20 @@ def index
sub = submissions_map[ont.acronym]
if sub
- o[:submissionStatus] = sub.submissionStatus
- o[:submission] = true
- o[:pullLocation] = sub.pullLocation
- o[:description] = sub.description
- o[:creationDate] = sub.creationDate
- o[:released] = sub.released
- o[:naturalLanguage] = sub.naturalLanguage
- o[:hasFormalityLevel] = sub.hasFormalityLevel
- o[:isOfType] = sub.isOfType
+ o[:submissionStatus] = sub.submissionStatus
+ o[:deprecated] = sub.deprecated
+ o[:status] = sub.status
+ o[:submission] = true
+ o[:pullLocation] = sub.pullLocation
+ o[:description] = sub.description
+ o[:creationDate] = sub.creationDate
+ o[:released] = sub.released
+ o[:naturalLanguage] = sub.naturalLanguage
+ o[:hasFormalityLevel] = sub.hasFormalityLevel
+ o[:isOfType] = sub.isOfType
o[:submissionStatusFormatted] = submission_status2string(sub).gsub(/\(|\)/, '')
o[:format] = sub.hasOntologyLanguage
- @formats << sub.hasOntologyLanguage
else
# Used to sort ontologies without submissions to the end when sorting on upload date
o[:creationDate] = DateTime.parse('19900601')
@@ -139,19 +132,20 @@ def index
@ontologies << o
end
+
+ @ontologies.sort! { |a, b| b[:popularity] <=> a[:popularity] }
- @ontologies.sort! {|a,b| b[:popularity] <=> a[:popularity]}
-
- render 'browse'
+ @ontologies = apply_ontology_filters(@ontologies, @categories, @groups)
+ render partial: "ontologies"
end
-
+
def classes
@submission = get_ontology_submission_ready(@ontology)
get_class(params)
if @submission.hasOntologyLanguage == 'SKOS'
- @schemes = get_schemes(@ontology)
+ @schemes = get_schemes(@ontology)
@collections = get_collections(@ontology, add_colors: true)
else
@instance_details, type = get_instance_and_type(params[:instanceid])
@@ -161,7 +155,6 @@ def classes
@instances_concept_id = get_concept_id(params, @concept, @root)
end
-
if ['application/ld+json', 'application/json'].include?(request.accept)
render plain: @concept.to_jsonld, content_type: request.accept and return
end
@@ -218,8 +211,8 @@ def edit
redirect_to_home unless session[:user] && @ontology.administeredBy.include?(session[:user].id) || session[:user].admin?
@categories = LinkedData::Client::Models::Category.all
@groups = LinkedData::Client::Models::Group.all
- @user_select_list = LinkedData::Client::Models::User.all.map {|u| [u.username, u.id]}
- @user_select_list.sort! {|a,b| a[1].downcase <=> b[1].downcase}
+ @user_select_list = LinkedData::Client::Models::User.all.map { |u| [u.username, u.id] }
+ @user_select_list.sort! { |a, b| a[1].downcase <=> b[1].downcase }
end
def mappings
@@ -234,11 +227,12 @@ def mappings
def new
@ontology = LinkedData::Client::Models::Ontology.new
- @ontologies = LinkedData::Client::Models::Ontology.all(include: 'acronym', include_views: true,display_links: false, display_context: false)
+ @ontologies = LinkedData::Client::Models::Ontology.all(include: 'acronym', include_views: true,
+ display_links: false, display_context: false)
@categories = LinkedData::Client::Models::Category.all
@groups = LinkedData::Client::Models::Group.all
- @user_select_list = LinkedData::Client::Models::User.all.map {|u| [u.username, u.id]}
- @user_select_list.sort! {|a,b| a[1].downcase <=> b[1].downcase}
+ @user_select_list = LinkedData::Client::Models::User.all.map { |u| [u.username, u.id] }
+ @user_select_list.sort! { |a, b| a[1].downcase <=> b[1].downcase }
end
def notes
@@ -256,9 +250,9 @@ def notes
def instances
if request.xhr?
- render partial: 'instances/instances', locals: { id: 'instances-data-table'}, layout: false
+ render partial: 'instances/instances', locals: { id: 'instances-data-table' }, layout: false
else
- render partial: 'instances/instances', locals: { id: 'instances-data-table'}, layout: 'ontology_viewer'
+ render partial: 'instances/instances', locals: { id: 'instances-data-table' }, layout: 'ontology_viewer'
end
end
@@ -392,7 +386,7 @@ def summary
@metrics = @ontology.explore.metrics rescue []
#@reviews = @ontology.explore.reviews.sort {|a,b| b.created <=> a.created} || []
- @projects = @ontology.explore.projects.sort {|a,b| a.name.downcase <=> b.name.downcase } || []
+ @projects = @ontology.explore.projects.sort { |a, b| a.name.downcase <=> b.name.downcase } || []
@analytics = LinkedData::Client::HTTP.get(@ontology.links['analytics'])
#Call to fairness assessment service
@@ -400,7 +394,7 @@ def summary
@fair_scores_data = create_fair_scores_data(tmp.values.first) unless tmp.nil?
@views = get_views(@ontology)
- @view_decorators = @views.map{ |view| ViewDecorator.new(view, view_context) }
+ @view_decorators = @views.map { |view| ViewDecorator.new(view, view_context) }
if request.xhr?
render partial: 'ontologies/sections/metadata', layout: false
@@ -421,8 +415,8 @@ def update
error_response = @ontology.update
if response_error?(error_response)
@categories = LinkedData::Client::Models::Category.all
- @user_select_list = LinkedData::Client::Models::User.all.map {|u| [u.username, u.id]}
- @user_select_list.sort! {|a,b| a[1].downcase <=> b[1].downcase}
+ @user_select_list = LinkedData::Client::Models::User.all.map { |u| [u.username, u.id] }
+ @user_select_list.sort! { |a, b| a[1].downcase <=> b[1].downcase }
@errors = response_errors(error_response)
@errors = { acronym: 'Acronym already exists, please use another' } if error_response.status == 409
flash[:error] = @errors
@@ -452,16 +446,92 @@ def widgets
end
end
-
private
+ def views_filter(filtered_ontologies, check)
+ filtered_ontologies.select { |o| (o[:viewOfOnt] && check) || o[:viewOfOnt].nil? }
+ end
+
+ def retired_filter(filtered_ontologies, check)
+ filtered_ontologies.select { |o| (o[:status].eql?('retired') && check) || !o[:status].eql?('retired') }
+ end
+
+ def apply_ontology_filters(ontologies, categories, groups)
+ @filters = ontology_filters_init(categories, groups)
+ filtered_ontologies = ontologies
+
+ filtered_ontologies = views_filter(filtered_ontologies, @show_views)
+ filtered_ontologies = retired_filter(filtered_ontologies, @show_retired)
+ filtered_ontologies = search(filtered_ontologies, params[:search]) unless (params[:search].nil? || params[:search].empty?)
+ filtered_ontologies = format_filter(filtered_ontologies, params[:format]) unless params[:format].nil?
+ filtered_ontologies = sort_by_filter(filtered_ontologies, params[:sort_by]) unless params[:sort_by].nil?
+ filtered_ontologies = filter_ontologies_by(filtered_ontologies, :categories)
+ filtered_ontologies = filter_ontologies_by(filtered_ontologies, :groups)
+ filtered_ontologies = filter_ontologies_by(filtered_ontologies, :naturalLanguage)
+ filtered_ontologies = filter_ontologies_by(filtered_ontologies, :hasFormalityLevel)
+ filtered_ontologies = filter_ontologies_by(filtered_ontologies, :isOfType)
+ filtered_ontologies = filter_ontologies_by(filtered_ontologies, :missingStatus)
+
+ filtered_ontologies
+ end
+
+ def ontology_filters_init(categories, groups)
+ @languages = submission_metadata.select { |x| x["@id"]["naturalLanguage"] }.first["enforcedValues"].map do |id, name|
+ { "id" => id, "name" => name, "acronym" => id.split('/').last }
+ end
+
+ @formalityLevel = submission_metadata.select { |x| x["@id"]["hasFormalityLevel"] }.first["enforcedValues"].map do |id, name|
+ { "id" => id, "name" => name, "acronym" => name.camelize(:lower) }
+ end
+
+ @isOfType = submission_metadata.select { |x| x["@id"]["isOfType"] }.first["enforcedValues"].map do |id, name|
+ { "id" => id, "name" => name, "acronym" => name.camelize(:lower) }
+ end
+
+ @missingStatus = [
+ {"id" => "RDF", "name" => "RDF", "acronym" => "RDF"},
+ {"id" => "ABSOLETE", "name" => "ABSOLETE", "acronym" => "ABSOLETE"},
+ {"id" => "METRICS", "name" => "METRICS", "acronym" => "METRICS"},
+ {"id" => "RDF_LABELS", "name" => "RDF LABELS", "acronym" => "RDFLABELS"},
+ {"id" => "UPLOADED", "name" => "UPLOADED", "acronym" => "UPLOADED"},
+ {"id" => "INDEXED_PROPERTIES", "name" => "INDEXED PROPERTIES", "acronym" => "INDEXED_PROPERTIES"},
+ {"id" => "ANNOTATOR", "name" => "ANNOTATOR", "acronym" => "ANNOTATOR"},
+ {"id" => "DIFF", "name" => "DIFF", "acronym" => "DIFF"}
+ ]
+
+ #@missingStatus = submission_metadata.select { |x| x["@id"]["submissionStatus"] }.first["enforcedValues"].map do |id, name|
+ # { "id" => id, "name" => name, "acronym" => name.camelize(:lower) }
+ #end
+
+ @show_views = params[:show_views]&.eql?('true')
+ @show_retired = params[:show_retired]&.eql?('true')
+ @selected_format = params[:format]
+ @search = params[:search]
+
+ {
+ categories: object_filter(categories, :categories),
+ groups: object_filter(groups, :groups),
+ naturalLanguage: object_filter(@languages, :naturalLanguage),
+ hasFormalityLevel: object_filter(@formalityLevel, :hasFormalityLevel),
+ isOfType: object_filter(@isOfType, :isOfType),
+ missingStatus: object_filter(@missingStatus, :missingStatus)
+ }
+ end
+
+ def filter_ontologies_by(filtered_ontologies, object_name)
+ objects, checks, _ = @filters[object_name]
+
+ return filtered_ontologies if checks.empty?
+
+ filtered_ontologies(filtered_ontologies, checks, object_name)
+ end
def ontology_params
- p = params.require(:ontology).permit(:name, :acronym, { administeredBy:[] }, :viewingRestriction, { acl:[] },
- { hasDomain:[] }, :isView, :viewOf, :subscribe_notifications, {group:[]})
+ p = params.require(:ontology).permit(:name, :acronym, { administeredBy: [] }, :viewingRestriction, { acl: [] },
+ { hasDomain: [] }, :isView, :viewOf, :subscribe_notifications, { group: [] })
p[:administeredBy].reject!(&:blank?)
p[:acl].reject!(&:blank?)
@@ -470,19 +540,152 @@ def ontology_params
p.to_h
end
- def determine_layout
- case action_name
- when 'index'
- 'angular'
- else
- super
- end
- end
def get_views(ontology)
views = ontology.explore.views || []
- views.select!{ |view| view.access?(session[:user]) }
- views.sort{ |a,b| a.acronym.downcase <=> b.acronym.downcase }
+ views.select! { |view| view.access?(session[:user]) }
+ views.sort { |a, b| a.acronym.downcase <=> b.acronym.downcase }
end
+ def search(ontologies_table, search_input)
+ # I need to fix the problem of capitale&small letters
+ filtered_table = []
+ ontologies_table.each do |ontology|
+ if ontology[:name].downcase.include? search_input.downcase
+ filtered_table << ontology unless filtered_table.include? ontology
+ end
+ end
+ filtered_table
+ end
+
+ def format_filter(ontologies_table, format)
+ filtered_table = []
+ ontologies_table.each do |ontology|
+
+ if ontology[:format]&.downcase == format.downcase
+ filtered_table << ontology
+ end
+ end
+
+ filtered_table
+ end
+
+ def sort_by_filter(ontologies_table, sort_by)
+
+ filtered_table = ontologies_table
+ case sort_by
+ when "Name"
+ for i in 0..ontologies_table.length-1 do
+ for j in i..ontologies_table.length-1 do
+ if ontologies_table[i][:name]>ontologies_table[j][:name]
+ tmp = ontologies_table[i]
+ ontologies_table[i] = ontologies_table[j]
+ ontologies_table[j] = tmp
+ end
+ end
+ end
+ when "class count"
+ for i in 0..ontologies_table.length-1 do
+ for j in i..ontologies_table.length-1 do
+ if ontologies_table[i][:class_count] x.name)
+ filter = this.element.id.split("_")[0]
+ }
+
+ this.#dispatchEvent(filter, checks)
+ }
+
+
+ #dispatchEvent(filter, checks){
+ let data = {
+ [filter]: checks,
+ }
+ const customEvent = new CustomEvent('changed', {
+ detail: {
+ data: data
+ }, bubbles: true
+ });
+
+ this.element.dispatchEvent(customEvent);
+ }
+ #getSelectedChecks() {
+ return Array.from(this.element.querySelectorAll('input:checked'))
+ }
+
+}
diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js
index cd0bcb2235..e1d8816ad1 100644
--- a/app/javascript/controllers/index.js
+++ b/app/javascript/controllers/index.js
@@ -4,6 +4,9 @@
import { application } from "./application"
+import BrowseFiltersController from "./browse_filters_controller"
+application.register("browse-filters", BrowseFiltersController)
+
import ChosenController from "./chosen_controller"
application.register("chosen", ChosenController)
@@ -37,6 +40,9 @@ application.register("metadata-downloader", MetadataDownloaderController)
import OntoportalAutocompleteController from "./ontoportal_autocomplete_controller"
application.register("ontoportal-autocomplete", OntoportalAutocompleteController)
+import ShowFilterCountController from "./show_filter_count_controller"
+application.register("show-filter-count", ShowFilterCountController)
+
import ShowModalController from "./show_modal_controller"
application.register("show-modal", ShowModalController)
diff --git a/app/javascript/controllers/turbo_frame_controller.js b/app/javascript/controllers/turbo_frame_controller.js
index 41c67dd5d9..11c1f22467 100644
--- a/app/javascript/controllers/turbo_frame_controller.js
+++ b/app/javascript/controllers/turbo_frame_controller.js
@@ -14,13 +14,10 @@ export default class extends Controller {
}
updateFrame(event) {
- const { data } = event.detail
- const values = Object.values(data)
-
- // remove null and empty values
- values.filter((value) => value !== "" || value !== undefined)
-
- if (values.length === 0) {
+
+ const newData = event.detail.data
+ const values = Object.entries(newData)[0][1]
+ if (values.filter(x => x.length !== 0).length === 0 && this.placeHolderValue) {
this.frame.innerHTML = this.placeHolderValue
} else {
this.frame.innerHTML = ""
diff --git a/app/javascript/mixins/useHistory.js b/app/javascript/mixins/useHistory.js
index f84ff41332..874f82310f 100644
--- a/app/javascript/mixins/useHistory.js
+++ b/app/javascript/mixins/useHistory.js
@@ -22,16 +22,20 @@ export class HistoryService {
}
getUpdatedURL(currentUrl, newData) {
- const base = document.location.origin
- const url = new URL(currentUrl, base)
- this.#updateURLFromState(url.searchParams, this.getState().data)
- this.#addNewDataToUrl(url, newData)
- return url.pathname + url.search
- }
+ const url = new URL(currentUrl, document.location.origin)
+ const urlParams = url.searchParams
+ this.#updateURLFromState(urlParams, this.getState())
- #addNewDataToUrl(url, newData) {
- const wantedData = this.#filterUnwantedData(newData, this.unWantedData);
+
+ this.#filterUnwantedData(newData).forEach(([updatedParam, newValue]) => {
+ newValue = Array.isArray(newValue) ? newValue : [newValue]
+ if (newValue !== null && Array.from(newValue).length > 0) {
+ urlParams.set(updatedParam, newValue.join(','))
+ }else{
+ urlParams.delete(updatedParam)
+ }
+ })
wantedData.forEach(([updatedParam, newValue]) => {
if (newValue === null) {
@@ -43,10 +47,13 @@ export class HistoryService {
});
}
- #filterUnwantedData(data, unWantedData) {
- return Object.entries(data).filter(([key]) => !unWantedData.some(uw => key.toLowerCase().includes(uw.toLowerCase())))
+ #filterUnwantedData(newData) {
+ const unWantedData = ['turbo', 'controller', 'target', 'value']
+ return Object.entries(newData).filter(([key]) => unWantedData.filter(x => key.toLowerCase().includes(x)).length === 0)
}
+ #initStateFromUrl(currentUrl) {
+
#initStateFromUrl(currentUrl) {
const url = new URL(currentUrl, document.location.origin)
const urlParams = url.searchParams
@@ -58,9 +65,19 @@ export class HistoryService {
}
#updateURLFromState(urlParams, state) {
- Object.entries(state).forEach(([key, val]) => {
- if (key !== 'p'){
- urlParams.set(key, val)
+ let oldValue = null
+ urlParams.forEach((newVal, key) => {
+ oldValue = state[key]
+
+ if (oldValue !== undefined && oldValue !== newVal) {
+ if (newVal.length !== 0){
+ urlParams.set(key, newVal)
+ }else{
+ urlParams.remove(key)
+ }
+
+ } else if (oldValue !== undefined) {
+ state[key] = newVal
}
})
}
diff --git a/package.json b/package.json
index d112843e7c..067f33b970 100644
--- a/package.json
+++ b/package.json
@@ -4,6 +4,7 @@
"dependencies": {
"@hotwired/stimulus": "^3.0.1",
"@hotwired/turbo-rails": "^7.1.1",
+ "debounce": "^1.2.1",
"esbuild": "^0.14.41",
"flatpickr": "^4.6.13",
"split.js": "^1.6.5",
diff --git a/yarn.lock b/yarn.lock
index b3e75bb55e..4034c999fc 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -30,6 +30,11 @@
resolved "https://registry.yarnpkg.com/@rails/actioncable/-/actioncable-7.0.4.tgz#70a3ca56809f7aaabb80af2f9c01ae51e1a8ed41"
integrity sha512-tz4oM+Zn9CYsvtyicsa/AwzKZKL+ITHWkhiu7x+xF77clh2b4Rm+s6xnOgY/sGDWoFWZmtKsE95hxBPkgQQNnQ==
+debounce@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5"
+ integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==
+
esbuild-android-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz#505f41832884313bbaffb27704b8bcaa2d8616be"
From 75705a63541f29d051e3d26193ac9a1ba8315f65 Mon Sep 17 00:00:00 2001
From: Bilelkihal <61744974+Bilelkihal@users.noreply.github.com>
Date: Mon, 27 Mar 2023 16:23:34 +0200
Subject: [PATCH 09/61] Add the checked filters count in the browse page
---
.../controllers/show_filter_count_controller.js | 13 +++++++++++++
1 file changed, 13 insertions(+)
create mode 100644 app/javascript/controllers/show_filter_count_controller.js
diff --git a/app/javascript/controllers/show_filter_count_controller.js b/app/javascript/controllers/show_filter_count_controller.js
new file mode 100644
index 0000000000..a7f6fb101c
--- /dev/null
+++ b/app/javascript/controllers/show_filter_count_controller.js
@@ -0,0 +1,13 @@
+import { Controller } from "@hotwired/stimulus"
+
+// Connects to data-controller="show-filter-count"
+
+export default class extends Controller {
+ static targets = ["countSpan"]
+ updateCount(){
+ const checkInputs = this.element.querySelectorAll('input:checked')
+ const count = checkInputs.length
+ this.countSpanTarget.style.display = count === 0 ? "none" : "inline-block"
+ this.countSpanTarget.innerHTML = count === 0 ? "" : count
+ }
+}
From c5c98fa888a5aab9075483447087c3988c049520 Mon Sep 17 00:00:00 2001
From: Bilelkihal <61744974+Bilelkihal@users.noreply.github.com>
Date: Mon, 27 Mar 2023 16:23:45 +0200
Subject: [PATCH 10/61] Remove the angular from the browse page
---
app/views/layouts/angular.html.erb | 72 ------------------------------
1 file changed, 72 deletions(-)
delete mode 100644 app/views/layouts/angular.html.erb
diff --git a/app/views/layouts/angular.html.erb b/app/views/layouts/angular.html.erb
deleted file mode 100644
index 61b977ad44..0000000000
--- a/app/views/layouts/angular.html.erb
+++ /dev/null
@@ -1,72 +0,0 @@
-<% raise ArgumentError, "@app_name variable must be set to use the Angular layout" unless @app_name%>
-<% raise ArgumentError, "@base_path variable must be set to use the Angular layout" unless @base_path%>
-<% @base_path = "/#{@base_path}/".gsub(/\/{2,}/, "/") %>
-<% appliance_mode = Rails.env.appliance? %>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <% unless appliance_mode %>
-
- <% end %>
- <%= csrf_meta_tag %>
- <%if @title.nil?%><%=$ORG_SITE%><%else%><%="#{@title} | #{$ORG_SITE}"%><%end%>
-
-
- <%= stylesheet_link_tag "https://use.fontawesome.com/releases/v5.2.0/css/all.css", integrity: "sha384-hWVjflwFxL6sNzntih27bfxkr27PmbbK/iSvJ+a4+0owXq79v+lsFkW54bOGbiDQ", crossorigin: "anonymous" %>
- <%= stylesheet_link_tag "application" %>
-
-
- <%=javascript_include_tag "vendor"%>
- <%=javascript_include_tag "//cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0/handlebars.min.js"%>
- <%=javascript_include_tag "//cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.1/Chart.min.js"%>
- <%=javascript_include_tag "//ajax.aspnetcdn.com/ajax/jquery.validate/1.11.1/jquery.validate.min.js"%>
-
- <%=render partial: 'layouts/topnav'%>
-
- <%=render partial: 'layouts/notices'%>
-
-
-
-
- <%=yield%>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <%= render partial: 'layouts/footer' %>
-
-
-
\ No newline at end of file
From b4a201fb8c5fbcb121748f51f95b22e5c890a319 Mon Sep 17 00:00:00 2001
From: Bilelkihal <61744974+Bilelkihal@users.noreply.github.com>
Date: Mon, 27 Mar 2023 18:43:15 +0200
Subject: [PATCH 11/61] In browse page, limit the ontology description to 3
lines and add a button show more to see the entire description
---
app/assets/stylesheets/browse.scss | 12 +++++++
app/views/ontologies/_ontologies.html.haml | 39 +++++++++++++++++++---
2 files changed, 47 insertions(+), 4 deletions(-)
diff --git a/app/assets/stylesheets/browse.scss b/app/assets/stylesheets/browse.scss
index 6fdd6188fa..1146964a37 100644
--- a/app/assets/stylesheets/browse.scss
+++ b/app/assets/stylesheets/browse.scss
@@ -66,6 +66,16 @@
color: #666666;
font-weight: 400;
margin: 5px 0 !important;
+ overflow: hidden;
+ display: -webkit-box;
+ -webkit-line-clamp: 3; /* Change this value to control the number of lines shown */
+ -webkit-box-orient: vertical;
+}
+.browse-show-more-button {
+ cursor: pointer;
+ font-size: 14px;
+ color: gray;
+ margin-bottom: 10px;
}
.browse-ontology-container{
border: 1px solid #DFDFDF;
@@ -352,6 +362,8 @@
+
+
@media only screen and (max-width: 1250px) {
.browse-first-row{
width: 24vw;
diff --git a/app/views/ontologies/_ontologies.html.haml b/app/views/ontologies/_ontologies.html.haml
index 1c6ef1735c..d5125176ef 100644
--- a/app/views/ontologies/_ontologies.html.haml
+++ b/app/views/ontologies/_ontologies.html.haml
@@ -16,9 +16,11 @@
- if ontology[:viewOfOnt]
.browse-ontology-view
view
-
- %p.browse-desc-text
- = ontology[:description]
+ .browse-desc-container
+ %p.browse-desc-text
+ = ontology[:description]
+ .browse-show-more-button
+ Show more ...
- unless ontology[:fairScore].nil? || ontology[:acronym] == 'AGROVOC'
@@ -58,4 +60,33 @@
%p.browse-card-number
= ontology[:note_count]
%p.browse-card-text
- notes
\ No newline at end of file
+ notes
+ :javascript
+ const textContainers = document.querySelectorAll('.browse-desc-container');
+ textContainers.forEach(container => {
+ const text = container.querySelector('.browse-desc-text');
+ const button = container.querySelector('.browse-show-more-button ');
+
+ // Get the height of the text element
+ text.style.display = 'block';
+ const textHeight = text.clientHeight;
+ text.style.display = '-webkit-box';
+
+ // Check if text has less than 3 lines so we don't display show more
+ if (textHeight <= text.clientHeight) {
+ button.style.display = 'none';
+ }
+
+ button.addEventListener('click', function() {
+ if (text.getAttribute('data-state') === 'truncated') {
+ text.style.display = "-webkit-box";
+ text.setAttribute('data-state', 'expanded');
+ button.textContent = 'Show more ...';
+ } else {
+ text.style.maxHeight = text.scrollHeight + 'px';
+ text.style.display = "unset";
+ button.textContent = 'Show Less ...';
+ text.setAttribute('data-state', 'truncated');
+ }
+ });
+ });
\ No newline at end of file
From 676d53976158999f2e19a544f3539d8d2a82d7fd Mon Sep 17 00:00:00 2001
From: Syphax Bouazzouni
Date: Tue, 28 Mar 2023 01:15:42 +0200
Subject: [PATCH 12/61] remove duplicate action
changed->turbo-frame#updateFrame in the browser
---
app/views/ontologies/browse.html.haml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/views/ontologies/browse.html.haml b/app/views/ontologies/browse.html.haml
index ddedb61a31..844c61f04e 100644
--- a/app/views/ontologies/browse.html.haml
+++ b/app/views/ontologies/browse.html.haml
@@ -44,7 +44,7 @@
%option{:value => "FAIR"} FAIR
.browse-sub-container
- .browse-first-row{data:{controller: "browse-filters", action: "change->browse-filters#dispatchFilterEvent changed->history#updateURL changed->turbo-frame#updateFrame"}}
+ .browse-first-row{data:{controller: "browse-filters", action: "change->browse-filters#dispatchFilterEvent changed->history#updateURL"}}
%p.browse-filters-title Filters
%hr#browse-sub-title-line
= render SwitchInputComponent.new(id:'filter-views', name:'views', checked: @show_views) do
From 482f8f1eda776d33fbebce215b5b40c092f8b21a Mon Sep 17 00:00:00 2001
From: Syphax Bouazzouni
Date: Tue, 28 Mar 2023 01:18:14 +0200
Subject: [PATCH 13/61] add text truncate stimulus controller
---
app/javascript/controllers/application.js | 3 ++-
app/javascript/controllers/index.js | 3 +++
.../controllers/text_truncate_controller.js | 21 +++++++++++++++++++
package.json | 3 ++-
4 files changed, 28 insertions(+), 2 deletions(-)
create mode 100644 app/javascript/controllers/text_truncate_controller.js
diff --git a/app/javascript/controllers/application.js b/app/javascript/controllers/application.js
index 258ab85b4c..1855468060 100644
--- a/app/javascript/controllers/application.js
+++ b/app/javascript/controllers/application.js
@@ -12,7 +12,8 @@ import Flatpickr from "stimulus-flatpickr"
application.register("flatpickr", Flatpickr);
import NestedForm from 'stimulus-rails-nested-form'
application.register('nested-form', NestedForm)
-
+import ReadMore from 'stimulus-read-more'
+application.register('read-more', ReadMore)
export { application }
diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js
index e1d8816ad1..12c4cba5d7 100644
--- a/app/javascript/controllers/index.js
+++ b/app/javascript/controllers/index.js
@@ -52,6 +52,9 @@ application.register("simple-tree", SimpleTreeController)
import SkosCollectionColorsController from "./skos_collection_colors_controller"
application.register("skos-collection-colors", SkosCollectionColorsController)
+import TextTruncateController from "./text_truncate_controller"
+application.register("text-truncate", TextTruncateController)
+
import Ontology_viewer_tabs_controller from "./ontology_viewer_tabs_controller"
application.register("ontology-viewer-tabs", Ontology_viewer_tabs_controller)
diff --git a/app/javascript/controllers/text_truncate_controller.js b/app/javascript/controllers/text_truncate_controller.js
new file mode 100644
index 0000000000..b3c235e835
--- /dev/null
+++ b/app/javascript/controllers/text_truncate_controller.js
@@ -0,0 +1,21 @@
+import ReadMore from "stimulus-read-more";
+
+// Connects to data-controller="text-truncate"
+export default class extends ReadMore {
+ static targets = ['button']
+
+ connect() {
+ super.connect()
+ if (!this.#isTextClamped()) {
+ this.#hideButton()
+ }
+ }
+
+ #isTextClamped() {
+ return this.contentTarget.scrollHeight > this.contentTarget.clientHeight
+ }
+
+ #hideButton() {
+ this.buttonTarget.style.display = 'none'
+ }
+}
diff --git a/package.json b/package.json
index 067f33b970..2359c8ecae 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,8 @@
"flatpickr": "^4.6.13",
"split.js": "^1.6.5",
"stimulus-flatpickr": "^3.0.0-0",
- "stimulus-rails-nested-form": "^4.0.0"
+ "stimulus-rails-nested-form": "^4.0.0",
+ "stimulus-read-more": "^4.1.0",
},
"scripts": {
"build": "esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds"
From d48110854cb64a41c6e5425fc03d7aae41f60c03 Mon Sep 17 00:00:00 2001
From: Syphax Bouazzouni
Date: Tue, 28 Mar 2023 01:21:52 +0200
Subject: [PATCH 14/61] replace in the browser descriptions js with a
controller to show more
---
app/assets/stylesheets/browse.scss | 25 +++++++------
app/views/ontologies/_ontologies.html.haml | 41 +++-------------------
2 files changed, 20 insertions(+), 46 deletions(-)
diff --git a/app/assets/stylesheets/browse.scss b/app/assets/stylesheets/browse.scss
index 1146964a37..012c1cafd7 100644
--- a/app/assets/stylesheets/browse.scss
+++ b/app/assets/stylesheets/browse.scss
@@ -25,6 +25,20 @@
margin-bottom: 20px;
}
+
+.browse-desc-text{
+ font-size: 16px;
+ color: #666666;
+ font-weight: 400;
+ margin: 5px 0 !important;
+ overflow: hidden;
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: var(--read-more-line-clamp, 2);
+}
+
+
+
.browse-you-are-admin{
display: flex;
flex-direction: column;
@@ -61,16 +75,7 @@
border: 1px solid var(--primary-color);
border-radius: 40px;
}
-.browse-desc-text{
- font-size: 16px;
- color: #666666;
- font-weight: 400;
- margin: 5px 0 !important;
- overflow: hidden;
- display: -webkit-box;
- -webkit-line-clamp: 3; /* Change this value to control the number of lines shown */
- -webkit-box-orient: vertical;
-}
+
.browse-show-more-button {
cursor: pointer;
font-size: 14px;
diff --git a/app/views/ontologies/_ontologies.html.haml b/app/views/ontologies/_ontologies.html.haml
index d5125176ef..5b4d8ecdc2 100644
--- a/app/views/ontologies/_ontologies.html.haml
+++ b/app/views/ontologies/_ontologies.html.haml
@@ -1,5 +1,3 @@
-
-
= turbo_frame_tag "search-results" do
%p.browse-desc-text{:style => "margin-bottom: 15px;"}
= "Showing "+ @ontologies.length.to_s
@@ -16,11 +14,11 @@
- if ontology[:viewOfOnt]
.browse-ontology-view
view
- .browse-desc-container
- %p.browse-desc-text
+ .browse-desc-container{data:{controller:"text-truncate", 'text-truncate-more-text-value': '+ Show more ...' , 'text-truncate-less-text-value': '- Show less ...'}}
+ %p.browse-desc-text{'data-text-truncate-target': 'content'}
= ontology[:description]
- .browse-show-more-button
- Show more ...
+ .browse-show-more-button{data:{'text-truncate-target': 'button', 'action':"click->text-truncate#toggle"}}
+ + Show more ...
- unless ontology[:fairScore].nil? || ontology[:acronym] == 'AGROVOC'
@@ -60,33 +58,4 @@
%p.browse-card-number
= ontology[:note_count]
%p.browse-card-text
- notes
- :javascript
- const textContainers = document.querySelectorAll('.browse-desc-container');
- textContainers.forEach(container => {
- const text = container.querySelector('.browse-desc-text');
- const button = container.querySelector('.browse-show-more-button ');
-
- // Get the height of the text element
- text.style.display = 'block';
- const textHeight = text.clientHeight;
- text.style.display = '-webkit-box';
-
- // Check if text has less than 3 lines so we don't display show more
- if (textHeight <= text.clientHeight) {
- button.style.display = 'none';
- }
-
- button.addEventListener('click', function() {
- if (text.getAttribute('data-state') === 'truncated') {
- text.style.display = "-webkit-box";
- text.setAttribute('data-state', 'expanded');
- button.textContent = 'Show more ...';
- } else {
- text.style.maxHeight = text.scrollHeight + 'px';
- text.style.display = "unset";
- button.textContent = 'Show Less ...';
- text.setAttribute('data-state', 'truncated');
- }
- });
- });
\ No newline at end of file
+ notes
\ No newline at end of file
From ac4e2e7c94d7f232fd8486855652a7eb67f5370e Mon Sep 17 00:00:00 2001
From: Syphax Bouazzouni
Date: Tue, 28 Mar 2023 01:23:28 +0200
Subject: [PATCH 15/61] add timeago stimulus controller to show dates in a
human readable way
---
app/javascript/controllers/application.js | 2 ++
package.json | 1 +
2 files changed, 3 insertions(+)
diff --git a/app/javascript/controllers/application.js b/app/javascript/controllers/application.js
index 1855468060..f0624491d2 100644
--- a/app/javascript/controllers/application.js
+++ b/app/javascript/controllers/application.js
@@ -14,6 +14,8 @@ import NestedForm from 'stimulus-rails-nested-form'
application.register('nested-form', NestedForm)
import ReadMore from 'stimulus-read-more'
application.register('read-more', ReadMore)
+import Timeago from 'stimulus-timeago'
+application.register('timeago', Timeago)
export { application }
diff --git a/package.json b/package.json
index 2359c8ecae..ee3ae0d700 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,7 @@
"stimulus-flatpickr": "^3.0.0-0",
"stimulus-rails-nested-form": "^4.0.0",
"stimulus-read-more": "^4.1.0",
+ "stimulus-timeago": "^4.1.0"
},
"scripts": {
"build": "esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds"
From 8605597a55272e217da70d631f69e61d85006008 Mon Sep 17 00:00:00 2001
From: Syphax Bouazzouni
Date: Tue, 28 Mar 2023 01:24:56 +0200
Subject: [PATCH 16/61] add ontology contacts and format to the ontology card
---
app/assets/stylesheets/browse.scss | 27 +++++++++++-----------
app/controllers/ontologies_controller.rb | 4 +---
app/views/ontologies/_ontologies.html.haml | 19 +++++++++++----
3 files changed, 30 insertions(+), 20 deletions(-)
diff --git a/app/assets/stylesheets/browse.scss b/app/assets/stylesheets/browse.scss
index 012c1cafd7..f87319cbba 100644
--- a/app/assets/stylesheets/browse.scss
+++ b/app/assets/stylesheets/browse.scss
@@ -23,7 +23,7 @@
.browse-submit-new-ontology-and-you-are-admin-container{
display: flex;
margin-bottom: 20px;
-
+
}
.browse-desc-text{
@@ -51,13 +51,13 @@
.browse-you-are-admin h3{
font-size: 20px;
font-weight: 600;
-
+
}
.browse-you-are-admin div{
display: flex;
-
+
align-items: center;
-
+
}
.browse-you-are-admin div p{
margin-left: 20px;
@@ -112,12 +112,12 @@
.browse-first-row{
width: 300px;
margin-right: 20px;
-
+
}
.browse-second-row{
width: 846px;
-
+
}
#browse-title-line{
@@ -157,7 +157,7 @@
.browse-uploaded{
font-size: 13px;
height: 37px;
- width: 160px;
+ max-width: 400px;
background-color: #E7E7E7;
border-radius: 5px;
display: flex;
@@ -169,6 +169,7 @@
}
.browse-uploaded-date{
font-weight: 600;
+ white-space: nowrap;
}
.browse-ontology-cards{
display: grid;
@@ -267,7 +268,7 @@
display: flex;
align-items: center;
justify-content: center;
-
+
border-radius: 14px;
padding: 20px;
background-color: var(--primary-color);
@@ -285,7 +286,7 @@
border-radius: 5px;
padding: 14px 20px;
margin-bottom: 10px;
-
+
}
.browse-filter-checks-container{
@@ -374,9 +375,9 @@
width: 24vw;
margin-right: 20px;
}
-
+
.browse-second-row{
- width: 68vw;
+ width: 68vw;
}
.browse-search-bar input{
width: 94vw;
@@ -418,7 +419,7 @@
width:46vw;
box-shadow: rgba(100, 100, 111, 0.1) 0px 7px 29px 0px;
}
-
+
.browse-sub-container{
margin-top: 20px;
}
@@ -444,6 +445,6 @@
.browse-ontology-view{
margin-right: 0;
}
-
+
}
diff --git a/app/controllers/ontologies_controller.rb b/app/controllers/ontologies_controller.rb
index e8b6d3bff0..b1a8f9f50e 100644
--- a/app/controllers/ontologies_controller.rb
+++ b/app/controllers/ontologies_controller.rb
@@ -125,9 +125,7 @@ def ontologies_filter
o[:submissionStatusFormatted] = submission_status2string(sub).gsub(/\(|\)/, '')
o[:format] = sub.hasOntologyLanguage
- else
- # Used to sort ontologies without submissions to the end when sorting on upload date
- o[:creationDate] = DateTime.parse('19900601')
+ o[:contact] = sub.contact.map{|c| c.name}.first
end
@ontologies << o
diff --git a/app/views/ontologies/_ontologies.html.haml b/app/views/ontologies/_ontologies.html.haml
index 5b4d8ecdc2..ac698cd442 100644
--- a/app/views/ontologies/_ontologies.html.haml
+++ b/app/views/ontologies/_ontologies.html.haml
@@ -30,10 +30,21 @@
%p.browse-fair-score
= ontology[:fairScore]
%a.browse-fair-details{:href => "/ontologies/#{ontology[:acronym]}#fair-details"} FAIR details ...
- .browse-uploaded
- %p Uploaded:
- %p.browse-uploaded-date
- = ontology[:creationDate].to_s[0, 10]
+ .d-flex.align-items-baseline
+ - if ontology[:creationDate]
+ .browse-uploaded
+ %p.mr-1 Uploaded
+ %p.browse-uploaded-date{data:{controller: 'timeago', 'timeago-datetime-value': ontology[:creationDate], 'timeago-add-suffix-value': 'true'}}
+ - if ontology[:contact]
+ %p.mx-1 by
+ %p.browse-uploaded-date.text-truncate
+ = ontology[:contact].humanize
+
+
+ - if ontology[:format]
+ .browse-ontology-view
+ = ontology[:format]
+
.browse-ontology-cards
- if ontology[:format] != 'SKOS' && ontology[:class_count] > 0
%a.browse-onology-card{:href => "/ontologies/#{ontology[:acronym]}?p=classes"}
From a9b54b9df2a1ed4f938584125d187dd2363176ba Mon Sep 17 00:00:00 2001
From: Syphax Bouazzouni
Date: Tue, 28 Mar 2023 01:27:02 +0200
Subject: [PATCH 17/61] remove old angular files
---
public/browse/.bowerrc | 3 -
public/browse/.gitignore | 7 -
public/browse/.jshintrc | 13 -
public/browse/app.css | 184 -
public/browse/app.js | 417 -
public/browse/bower.json | 18 -
public/browse/checklist-model.js | 99 -
public/browse/e2e-tests/protractor.conf.js | 19 -
public/browse/e2e-tests/scenarios.js | 42 -
public/browse/font/webhostinghub-glyphs.eot | Bin 503876 -> 0 bytes
public/browse/font/webhostinghub-glyphs.svg | 7835 -----
public/browse/font/webhostinghub-glyphs.ttf | Bin 503664 -> 0 bytes
public/browse/font/whhg.css | 2085 --
public/browse/karma.conf.js | 33 -
public/browse/lib/angular-animate/.bower.json | 19 -
public/browse/lib/angular-animate/README.md | 77 -
.../lib/angular-animate/angular-animate.js | 2136 --
.../angular-animate/angular-animate.min.js | 33 -
.../angular-animate.min.js.map | 8 -
public/browse/lib/angular-animate/bower.json | 9 -
.../browse/lib/angular-animate/package.json | 26 -
.../browse/lib/angular-bindonce/.bower.json | 37 -
.../browse/lib/angular-bindonce/CHANGELOG.md | 43 -
public/browse/lib/angular-bindonce/README.md | 154 -
.../browse/lib/angular-bindonce/bindonce.js | 325 -
.../lib/angular-bindonce/bindonce.min.js | 1 -
public/browse/lib/angular-bindonce/bower.json | 28 -
.../browse/lib/angular-bindonce/package.json | 28 -
public/browse/lib/angular-loader/.bower.json | 19 -
public/browse/lib/angular-loader/README.md | 65 -
.../lib/angular-loader/angular-loader.js | 405 -
.../lib/angular-loader/angular-loader.min.js | 9 -
.../angular-loader/angular-loader.min.js.map | 8 -
public/browse/lib/angular-loader/bower.json | 9 -
public/browse/lib/angular-loader/package.json | 26 -
public/browse/lib/angular-mocks/.bower.json | 19 -
public/browse/lib/angular-mocks/README.md | 57 -
.../browse/lib/angular-mocks/angular-mocks.js | 2380 --
public/browse/lib/angular-mocks/bower.json | 9 -
public/browse/lib/angular-mocks/package.json | 27 -
public/browse/lib/angular-route/.bower.json | 19 -
public/browse/lib/angular-route/README.md | 77 -
.../browse/lib/angular-route/angular-route.js | 996 -
.../lib/angular-route/angular-route.min.js | 15 -
.../angular-route/angular-route.min.js.map | 8 -
public/browse/lib/angular-route/bower.json | 9 -
public/browse/lib/angular-route/package.json | 26 -
public/browse/lib/angular/.bower.json | 17 -
public/browse/lib/angular/README.md | 67 -
public/browse/lib/angular/angular-csp.css | 13 -
public/browse/lib/angular/angular.js | 25917 ----------------
public/browse/lib/angular/angular.min.js | 250 -
public/browse/lib/angular/angular.min.js.gzip | Bin 45728 -> 0 bytes
public/browse/lib/angular/angular.min.js.map | 8 -
public/browse/lib/angular/bower.json | 8 -
public/browse/lib/angular/package.json | 25 -
.../browse/lib/html5-boilerplate/.bower.json | 14 -
.../lib/html5-boilerplate/.gitattributes | 1 -
.../browse/lib/html5-boilerplate/.gitignore | 2 -
public/browse/lib/html5-boilerplate/.htaccess | 551 -
public/browse/lib/html5-boilerplate/404.html | 157 -
.../browse/lib/html5-boilerplate/CHANGELOG.md | 133 -
.../lib/html5-boilerplate/CONTRIBUTING.md | 154 -
.../browse/lib/html5-boilerplate/LICENSE.md | 19 -
public/browse/lib/html5-boilerplate/README.md | 61 -
.../apple-touch-icon-precomposed.png | Bin 1226 -> 0 bytes
.../lib/html5-boilerplate/crossdomain.xml | 15 -
.../browse/lib/html5-boilerplate/css/main.css | 304 -
.../lib/html5-boilerplate/css/normalize.css | 527 -
.../browse/lib/html5-boilerplate/doc/TOC.md | 37 -
.../lib/html5-boilerplate/doc/crossdomain.md | 17 -
.../browse/lib/html5-boilerplate/doc/css.md | 136 -
.../lib/html5-boilerplate/doc/extend.md | 586 -
.../browse/lib/html5-boilerplate/doc/faq.md | 76 -
.../browse/lib/html5-boilerplate/doc/html.md | 161 -
public/browse/lib/html5-boilerplate/doc/js.md | 31 -
.../browse/lib/html5-boilerplate/doc/misc.md | 26 -
.../browse/lib/html5-boilerplate/doc/usage.md | 113 -
.../browse/lib/html5-boilerplate/favicon.ico | Bin 766 -> 0 bytes
.../browse/lib/html5-boilerplate/humans.txt | 15 -
.../lib/html5-boilerplate/img/.gitignore | 0
.../browse/lib/html5-boilerplate/index.html | 42 -
.../browse/lib/html5-boilerplate/js/main.js | 1 -
.../lib/html5-boilerplate/js/plugins.js | 24 -
.../js/vendor/jquery-1.10.2.min.js | 6 -
.../js/vendor/modernizr-2.6.2.min.js | 4 -
.../browse/lib/html5-boilerplate/robots.txt | 3 -
public/browse/lib/lunr.js/.bower.json | 21 -
public/browse/lib/lunr.js/.gitignore | 1 -
public/browse/lib/lunr.js/.travis.yml | 6 -
public/browse/lib/lunr.js/CHANGELOG.mdown | 94 -
public/browse/lib/lunr.js/CNAME | 1 -
public/browse/lib/lunr.js/CONTRIBUTING.mdown | 20 -
public/browse/lib/lunr.js/LICENSE | 19 -
public/browse/lib/lunr.js/Makefile | 68 -
public/browse/lib/lunr.js/README.mdown | 63 -
public/browse/lib/lunr.js/VERSION | 1 -
public/browse/lib/lunr.js/bower.json | 11 -
public/browse/lib/lunr.js/component.json | 9 -
public/browse/lib/lunr.js/example/app.js | 89 -
.../lib/lunr.js/example/example_data.json | 3016 --
.../lib/lunr.js/example/example_index.json | 1 -
public/browse/lib/lunr.js/example/index.html | 81 -
.../lib/lunr.js/example/index_builder.js | 34 -
public/browse/lib/lunr.js/example/jquery.js | 4 -
public/browse/lib/lunr.js/example/mustache.js | 570 -
public/browse/lib/lunr.js/example/require.js | 36 -
.../example/templates/question_list.mustache | 10 -
.../example/templates/question_view.mustache | 5 -
public/browse/lib/lunr.js/example/text.js | 386 -
public/browse/lib/lunr.js/index.html | 305 -
.../browse/lib/lunr.js/lib/document_store.js | 96 -
.../browse/lib/lunr.js/lib/event_emitter.js | 82 -
public/browse/lib/lunr.js/lib/index.js | 421 -
public/browse/lib/lunr.js/lib/lunr.js | 50 -
public/browse/lib/lunr.js/lib/pipeline.js | 217 -
public/browse/lib/lunr.js/lib/sorted_set.js | 238 -
public/browse/lib/lunr.js/lib/stemmer.js | 190 -
.../lib/lunr.js/lib/stop_word_filter.js | 147 -
public/browse/lib/lunr.js/lib/token_store.js | 193 -
public/browse/lib/lunr.js/lib/tokenizer.js | 35 -
public/browse/lib/lunr.js/lib/trimmer.js | 26 -
public/browse/lib/lunr.js/lib/utils.js | 24 -
public/browse/lib/lunr.js/lib/vector.js | 125 -
public/browse/lib/lunr.js/lunr.js | 1882 --
public/browse/lib/lunr.js/lunr.min.js | 7 -
public/browse/lib/lunr.js/notes | 47 -
public/browse/lib/lunr.js/package.json | 24 -
public/browse/lib/lunr.js/server.js | 45 -
public/browse/lib/lunr.js/styles.css | 133 -
.../lib/lunr.js/test/env/augment.min.js | 8 -
public/browse/lib/lunr.js/test/env/jquery.js | 16 -
public/browse/lib/lunr.js/test/env/qunit.css | 235 -
public/browse/lib/lunr.js/test/env/qunit.js | 1977 --
public/browse/lib/lunr.js/test/env/runner.js | 125 -
.../lib/lunr.js/test/event_emitter_test.js | 75 -
.../lunr.js/test/fixtures/stemming_vocab.json | 1 -
public/browse/lib/lunr.js/test/index.html | 52 -
public/browse/lib/lunr.js/test/index_test.js | 322 -
public/browse/lib/lunr.js/test/lunr_test.js | 37 -
.../browse/lib/lunr.js/test/pipeline_test.js | 185 -
public/browse/lib/lunr.js/test/runner.sh | 9 -
public/browse/lib/lunr.js/test/search_test.js | 77 -
.../lib/lunr.js/test/serialisation_test.js | 46 -
.../lib/lunr.js/test/sorted_set_test.js | 118 -
.../browse/lib/lunr.js/test/stemmer_test.js | 14 -
.../lib/lunr.js/test/stop_word_filter_test.js | 22 -
.../lib/lunr.js/test/store_node_test.js | 17 -
public/browse/lib/lunr.js/test/store_test.js | 60 -
public/browse/lib/lunr.js/test/test_helper.js | 23 -
.../lib/lunr.js/test/token_store_test.js | 177 -
.../browse/lib/lunr.js/test/tokenizer_test.js | 65 -
.../browse/lib/lunr.js/test/trimmer_test.js | 27 -
public/browse/lib/lunr.js/test/vector_test.js | 39 -
public/browse/package.json | 35 -
155 files changed, 59978 deletions(-)
delete mode 100755 public/browse/.bowerrc
delete mode 100755 public/browse/.gitignore
delete mode 100755 public/browse/.jshintrc
delete mode 100755 public/browse/app.css
delete mode 100755 public/browse/app.js
delete mode 100755 public/browse/bower.json
delete mode 100644 public/browse/checklist-model.js
delete mode 100755 public/browse/e2e-tests/protractor.conf.js
delete mode 100755 public/browse/e2e-tests/scenarios.js
delete mode 100755 public/browse/font/webhostinghub-glyphs.eot
delete mode 100755 public/browse/font/webhostinghub-glyphs.svg
delete mode 100755 public/browse/font/webhostinghub-glyphs.ttf
delete mode 100755 public/browse/font/whhg.css
delete mode 100755 public/browse/karma.conf.js
delete mode 100644 public/browse/lib/angular-animate/.bower.json
delete mode 100644 public/browse/lib/angular-animate/README.md
delete mode 100644 public/browse/lib/angular-animate/angular-animate.js
delete mode 100644 public/browse/lib/angular-animate/angular-animate.min.js
delete mode 100644 public/browse/lib/angular-animate/angular-animate.min.js.map
delete mode 100644 public/browse/lib/angular-animate/bower.json
delete mode 100644 public/browse/lib/angular-animate/package.json
delete mode 100644 public/browse/lib/angular-bindonce/.bower.json
delete mode 100644 public/browse/lib/angular-bindonce/CHANGELOG.md
delete mode 100644 public/browse/lib/angular-bindonce/README.md
delete mode 100644 public/browse/lib/angular-bindonce/bindonce.js
delete mode 100644 public/browse/lib/angular-bindonce/bindonce.min.js
delete mode 100644 public/browse/lib/angular-bindonce/bower.json
delete mode 100644 public/browse/lib/angular-bindonce/package.json
delete mode 100644 public/browse/lib/angular-loader/.bower.json
delete mode 100644 public/browse/lib/angular-loader/README.md
delete mode 100644 public/browse/lib/angular-loader/angular-loader.js
delete mode 100644 public/browse/lib/angular-loader/angular-loader.min.js
delete mode 100644 public/browse/lib/angular-loader/angular-loader.min.js.map
delete mode 100644 public/browse/lib/angular-loader/bower.json
delete mode 100644 public/browse/lib/angular-loader/package.json
delete mode 100644 public/browse/lib/angular-mocks/.bower.json
delete mode 100644 public/browse/lib/angular-mocks/README.md
delete mode 100644 public/browse/lib/angular-mocks/angular-mocks.js
delete mode 100644 public/browse/lib/angular-mocks/bower.json
delete mode 100644 public/browse/lib/angular-mocks/package.json
delete mode 100644 public/browse/lib/angular-route/.bower.json
delete mode 100644 public/browse/lib/angular-route/README.md
delete mode 100644 public/browse/lib/angular-route/angular-route.js
delete mode 100644 public/browse/lib/angular-route/angular-route.min.js
delete mode 100644 public/browse/lib/angular-route/angular-route.min.js.map
delete mode 100644 public/browse/lib/angular-route/bower.json
delete mode 100644 public/browse/lib/angular-route/package.json
delete mode 100644 public/browse/lib/angular/.bower.json
delete mode 100644 public/browse/lib/angular/README.md
delete mode 100644 public/browse/lib/angular/angular-csp.css
delete mode 100644 public/browse/lib/angular/angular.js
delete mode 100644 public/browse/lib/angular/angular.min.js
delete mode 100644 public/browse/lib/angular/angular.min.js.gzip
delete mode 100644 public/browse/lib/angular/angular.min.js.map
delete mode 100644 public/browse/lib/angular/bower.json
delete mode 100644 public/browse/lib/angular/package.json
delete mode 100644 public/browse/lib/html5-boilerplate/.bower.json
delete mode 100644 public/browse/lib/html5-boilerplate/.gitattributes
delete mode 100644 public/browse/lib/html5-boilerplate/.gitignore
delete mode 100644 public/browse/lib/html5-boilerplate/.htaccess
delete mode 100644 public/browse/lib/html5-boilerplate/404.html
delete mode 100644 public/browse/lib/html5-boilerplate/CHANGELOG.md
delete mode 100644 public/browse/lib/html5-boilerplate/CONTRIBUTING.md
delete mode 100644 public/browse/lib/html5-boilerplate/LICENSE.md
delete mode 100644 public/browse/lib/html5-boilerplate/README.md
delete mode 100644 public/browse/lib/html5-boilerplate/apple-touch-icon-precomposed.png
delete mode 100644 public/browse/lib/html5-boilerplate/crossdomain.xml
delete mode 100644 public/browse/lib/html5-boilerplate/css/main.css
delete mode 100644 public/browse/lib/html5-boilerplate/css/normalize.css
delete mode 100644 public/browse/lib/html5-boilerplate/doc/TOC.md
delete mode 100644 public/browse/lib/html5-boilerplate/doc/crossdomain.md
delete mode 100644 public/browse/lib/html5-boilerplate/doc/css.md
delete mode 100644 public/browse/lib/html5-boilerplate/doc/extend.md
delete mode 100644 public/browse/lib/html5-boilerplate/doc/faq.md
delete mode 100644 public/browse/lib/html5-boilerplate/doc/html.md
delete mode 100644 public/browse/lib/html5-boilerplate/doc/js.md
delete mode 100644 public/browse/lib/html5-boilerplate/doc/misc.md
delete mode 100644 public/browse/lib/html5-boilerplate/doc/usage.md
delete mode 100644 public/browse/lib/html5-boilerplate/favicon.ico
delete mode 100644 public/browse/lib/html5-boilerplate/humans.txt
delete mode 100644 public/browse/lib/html5-boilerplate/img/.gitignore
delete mode 100644 public/browse/lib/html5-boilerplate/index.html
delete mode 100644 public/browse/lib/html5-boilerplate/js/main.js
delete mode 100644 public/browse/lib/html5-boilerplate/js/plugins.js
delete mode 100644 public/browse/lib/html5-boilerplate/js/vendor/jquery-1.10.2.min.js
delete mode 100644 public/browse/lib/html5-boilerplate/js/vendor/modernizr-2.6.2.min.js
delete mode 100644 public/browse/lib/html5-boilerplate/robots.txt
delete mode 100644 public/browse/lib/lunr.js/.bower.json
delete mode 100644 public/browse/lib/lunr.js/.gitignore
delete mode 100644 public/browse/lib/lunr.js/.travis.yml
delete mode 100644 public/browse/lib/lunr.js/CHANGELOG.mdown
delete mode 100644 public/browse/lib/lunr.js/CNAME
delete mode 100644 public/browse/lib/lunr.js/CONTRIBUTING.mdown
delete mode 100644 public/browse/lib/lunr.js/LICENSE
delete mode 100644 public/browse/lib/lunr.js/Makefile
delete mode 100644 public/browse/lib/lunr.js/README.mdown
delete mode 100644 public/browse/lib/lunr.js/VERSION
delete mode 100644 public/browse/lib/lunr.js/bower.json
delete mode 100644 public/browse/lib/lunr.js/component.json
delete mode 100644 public/browse/lib/lunr.js/example/app.js
delete mode 100644 public/browse/lib/lunr.js/example/example_data.json
delete mode 100644 public/browse/lib/lunr.js/example/example_index.json
delete mode 100644 public/browse/lib/lunr.js/example/index.html
delete mode 100644 public/browse/lib/lunr.js/example/index_builder.js
delete mode 100644 public/browse/lib/lunr.js/example/jquery.js
delete mode 100644 public/browse/lib/lunr.js/example/mustache.js
delete mode 100644 public/browse/lib/lunr.js/example/require.js
delete mode 100644 public/browse/lib/lunr.js/example/templates/question_list.mustache
delete mode 100644 public/browse/lib/lunr.js/example/templates/question_view.mustache
delete mode 100644 public/browse/lib/lunr.js/example/text.js
delete mode 100644 public/browse/lib/lunr.js/index.html
delete mode 100644 public/browse/lib/lunr.js/lib/document_store.js
delete mode 100644 public/browse/lib/lunr.js/lib/event_emitter.js
delete mode 100644 public/browse/lib/lunr.js/lib/index.js
delete mode 100644 public/browse/lib/lunr.js/lib/lunr.js
delete mode 100644 public/browse/lib/lunr.js/lib/pipeline.js
delete mode 100644 public/browse/lib/lunr.js/lib/sorted_set.js
delete mode 100644 public/browse/lib/lunr.js/lib/stemmer.js
delete mode 100644 public/browse/lib/lunr.js/lib/stop_word_filter.js
delete mode 100644 public/browse/lib/lunr.js/lib/token_store.js
delete mode 100644 public/browse/lib/lunr.js/lib/tokenizer.js
delete mode 100644 public/browse/lib/lunr.js/lib/trimmer.js
delete mode 100644 public/browse/lib/lunr.js/lib/utils.js
delete mode 100644 public/browse/lib/lunr.js/lib/vector.js
delete mode 100644 public/browse/lib/lunr.js/lunr.js
delete mode 100644 public/browse/lib/lunr.js/lunr.min.js
delete mode 100644 public/browse/lib/lunr.js/notes
delete mode 100644 public/browse/lib/lunr.js/package.json
delete mode 100644 public/browse/lib/lunr.js/server.js
delete mode 100644 public/browse/lib/lunr.js/styles.css
delete mode 100644 public/browse/lib/lunr.js/test/env/augment.min.js
delete mode 100644 public/browse/lib/lunr.js/test/env/jquery.js
delete mode 100644 public/browse/lib/lunr.js/test/env/qunit.css
delete mode 100644 public/browse/lib/lunr.js/test/env/qunit.js
delete mode 100644 public/browse/lib/lunr.js/test/env/runner.js
delete mode 100644 public/browse/lib/lunr.js/test/event_emitter_test.js
delete mode 100644 public/browse/lib/lunr.js/test/fixtures/stemming_vocab.json
delete mode 100644 public/browse/lib/lunr.js/test/index.html
delete mode 100644 public/browse/lib/lunr.js/test/index_test.js
delete mode 100644 public/browse/lib/lunr.js/test/lunr_test.js
delete mode 100644 public/browse/lib/lunr.js/test/pipeline_test.js
delete mode 100755 public/browse/lib/lunr.js/test/runner.sh
delete mode 100644 public/browse/lib/lunr.js/test/search_test.js
delete mode 100644 public/browse/lib/lunr.js/test/serialisation_test.js
delete mode 100644 public/browse/lib/lunr.js/test/sorted_set_test.js
delete mode 100644 public/browse/lib/lunr.js/test/stemmer_test.js
delete mode 100644 public/browse/lib/lunr.js/test/stop_word_filter_test.js
delete mode 100644 public/browse/lib/lunr.js/test/store_node_test.js
delete mode 100644 public/browse/lib/lunr.js/test/store_test.js
delete mode 100644 public/browse/lib/lunr.js/test/test_helper.js
delete mode 100644 public/browse/lib/lunr.js/test/token_store_test.js
delete mode 100644 public/browse/lib/lunr.js/test/tokenizer_test.js
delete mode 100644 public/browse/lib/lunr.js/test/trimmer_test.js
delete mode 100644 public/browse/lib/lunr.js/test/vector_test.js
delete mode 100755 public/browse/package.json
diff --git a/public/browse/.bowerrc b/public/browse/.bowerrc
deleted file mode 100755
index 7dcff4cd00..0000000000
--- a/public/browse/.bowerrc
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "directory" : "lib"
-}
\ No newline at end of file
diff --git a/public/browse/.gitignore b/public/browse/.gitignore
deleted file mode 100755
index b702acc68f..0000000000
--- a/public/browse/.gitignore
+++ /dev/null
@@ -1,7 +0,0 @@
-logs/*
-!.gitkeep
-node_modules/
-bower_components/
-tmp
-.DS_Store
-.idea
\ No newline at end of file
diff --git a/public/browse/.jshintrc b/public/browse/.jshintrc
deleted file mode 100755
index 6f00218e37..0000000000
--- a/public/browse/.jshintrc
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "globalstrict": true,
- "globals": {
- "angular": false,
- "describe": false,
- "it": false,
- "expect": false,
- "beforeEach": false,
- "afterEach": false,
- "module": false,
- "inject": false
- }
-}
\ No newline at end of file
diff --git a/public/browse/app.css b/public/browse/app.css
deleted file mode 100755
index 4cc86735e5..0000000000
--- a/public/browse/app.css
+++ /dev/null
@@ -1,184 +0,0 @@
-.grid-container {
- max-width: 1400px;
-}
-[ng-cloak].splash {
- display: block !important;
-}
-.splash {
- display: none;
- width: 25%;
- height: 4em;
- padding: 2em;
- text-align: center;
- margin: 2em auto;
- background-color: #ECECEC;
- border-radius: 3px;
-}
-[ng-cloak] {
- display: none;
-}
-.admin {
- background-color: #EFFFEF !important;
-}
-.welcome_admin {
- width: 33%;
- padding: 7px 10px 5px;
- border-radius: 3px;
- text-align: center;
- top: -5em;
- position: relative;
- float: right;
- left: -33%;
-}
-.search {
- float: left;
- width: 150px;
- padding: 2px 4px 1px;
- margin-top: -5px;
- width: 200px;
-}
-.smaller {
- color: gray;
- font-size: .8em;
- padding-bottom: 2px;
-}
-#facets {
- margin-top: 1em;
-}
-.new_ontology_button {
- display: inline-block;
- width: 100%;
- padding-left: 0px !important;
- padding-right: 0px !important;
- margin-top: 1em;
-}
-.facet {
- margin: 1em 0;
- border: thin lightGray solid;
- padding: 5px;
- border-radius: 3px;
-}
-.facet_disabled {
- color: gray;
-}
-.checkbox_list {
- max-height: 200px;
- overflow: auto;
- padding-left: 1px;
-}
-.checkbox_list > span {
- overflow-x: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- width: 200px;
- display: block;
-}
-#sorting {
-}
-#sorting_float {
- float: right;
- margin-bottom: .5em;
-}
-@keyframes highlight {
- 0% {
- background: yellow;
- }
- 100% {
- background: none;
- }
-}
-@-webkit-keyframes highlight {
- 0% {
- background: yellow;
- }
- 100% {
- background: none;
- }
-}
-.trigger_highlight-add, .trigger_highlight-remove {
- -webkit-animation: highlight 1s;
- animation: highlight 1s;
-}
-#ontologies {
-}
-.clear {
- clear: both;
-}
-.ontology {
- border: thin gray solid;
- border-radius: 5px;
- padding: .5em 1em .5em;
- margin: 1em 0;
-}
-.ontology_view_badge {
- float: left;
- padding: 5px;
- text-align: center;
- margin-right: 5px;
- border-radius: 3px;
- color: white;
- background-color: #234979;
- font-size: 1em;
- font-weight: bold;
-}
-.ontology_view_badge a {
- color: white;
- text-decoration: none;
-}
-.locked_ont {
- font-size: .65em;
- padding-left: .2em;
- color: gray;
- cursor: help;
- /* Make the lock appear in the upper left corner */
- /*
- background-color: #234979;
- padding: 2.5px 4px;
- font-size: 1em;
- color: white;
- position: relative;
- left: -10px;
- top: -8px;
- border-radius: 5px 0px;
- float: left;
- */
-}
-.ont-info {
- margin: 5px 5px 0 0;
- display: inline-block;
- padding: 5px 10px 3px;
- border-radius: 3px;
- background-color: #ECECEC;
- font-size: 11px;
-}
-.badge_grid {
- padding-bottom: 10px;
- float: right !important;
-}
-.ontology_badge {
- border-radius: 5px;
- text-align: center;
- max-width: 120px;
- height: 45px;
- max-height: 45px;
- border: solid #234979 thin;
-}
-.ontology_badge:hover {
- cursor: pointer;
-}
-.badge_title {
- border-radius: 4px 4px 0 0;
- font-size: 12px;
- color: white;
- background-color: #234979;
-}
-.badge_count {
- border-radius: 0 0 4px 4px;
- height: 22px;
- font-size: 1.1em;
- font-weight: bold;
- color: #234979;
-}
-.ontology_badge:hover .badge_count {
- background-color: rgba(220, 235, 255, 0.79);
-}
\ No newline at end of file
diff --git a/public/browse/app.js b/public/browse/app.js
deleted file mode 100755
index fe18ca0d9b..0000000000
--- a/public/browse/app.js
+++ /dev/null
@@ -1,417 +0,0 @@
-'use strict';
-
-// Declare app level module which depends on views, and components
-angular.module('FacetedBrowsing', [
- 'ngRoute',
- 'FacetedBrowsing.OntologyList'
-]).
-config( ['$locationProvider', function ($locationProvider) {
- $locationProvider.html5Mode(true);
-}])
-;
-
-var app = angular.module('FacetedBrowsing.OntologyList', ['checklist-model', 'ngAnimate', 'pasvaz.bindonce'])
-
-.controller('OntologyList', ['$scope', '$animate', '$timeout', function($scope, $animate, $timeout) {
- // Default values
- $scope.visible_ont_count = 0;
- $scope.ontology_sort_order = "-popularity";
- $scope.previous_sort_order = "-popularity";
- $scope.show_highlight = false;
-
- // Data transfer from Rails
- $scope.debug = jQuery(document).data().bp.development;
- $scope.admin = jQuery(document).data().bp.admin;
- $scope.ontologies = jQuery(document).data().bp.ontologies;
- $scope.formats = jQuery(document).data().bp.formats.sort();
- $scope.categories = jQuery(document).data().bp.categories.sort(function(a, b){
- if (a.name < b.name) return -1;
- if (a.name > b.name) return 1;
- return 0;
- });
- $scope.categories_hash = jQuery(document).data().bp.categories_hash;
- $scope.groups = jQuery(document).data().bp.groups.sort(function(a, b){
- if (a.acronym < b.acronym) return -1;
- if (a.acronym > b.acronym) return 1;
- return 0;
- });
- $scope.groups_hash = jQuery(document).data().bp.groups_hash;
-
- $scope.formality_levels = jQuery(document).data().bp.formality_levels;
- $scope.natural_languages = jQuery(document).data().bp.natural_languages;
- $scope.is_of_type = jQuery(document).data().bp.is_of_type;
-
- // Search setup
- $scope.searchText = null;
- $scope.ontIndex = lunr(function() {
- this.field('acronym', 100);
- this.field('name', 50);
- this.field('description');
- this.ref('id');
- });
- $scope.ontIndex.pipeline.reset();
-
- // Default setup for facets
- $scope.facets = {
- types: {
- active: ["ontology"],
- ont_property: "type",
- filter: function(ontology) {
- if ($scope.facets.types.active.length == 0)
- return true;
- if ($scope.facets.types.active.indexOf(ontology.type) === -1)
- return false;
- return true;
- },
- },
- formats: {
- active: [],
- ont_property: "format",
- filter: function(ontology) {
- if ($scope.facets.formats.active.length == 0)
- return true;
- if ($scope.facets.formats.active.indexOf(ontology.format) === -1)
- return false;
- return true;
- },
- },
- groups: {
- active: [],
- ont_property: "groups",
- filter: function(ontology) {
- if ($scope.facets.groups.active.length == 0)
- return true;
- if (intersection($scope.facets.groups.active, ontology.groups).length === 0)
- return false;
- return true;
- },
- },
- categories: {
- active: [],
- ont_property: "categories",
- filter: function(ontology) {
- if ($scope.facets.categories.active.length == 0)
- return true;
- if (intersection($scope.facets.categories.active, ontology.categories).length === 0)
- return false;
- return true;
- },
- },
- artifacts: {
- active: [],
- ont_property: "artifacts",
- filter: function(ontology) {
- if ($scope.facets.artifacts.active.length == 0)
- return true;
- if (intersection($scope.facets.artifacts.active, ontology.artifacts).length === 0)
- return false;
- return true;
- },
- },
- missing_status: {
- active: "",
- ont_property: "submissionStatus",
- values: ["None", "RDF", "OBSOLETE", "METRICS", "RDF_LABELS", "UPLOADED", "INDEXED", "ANNOTATOR", "DIFF"],
- filter: function(ontology) {
- if ($scope.facets.missing_status.active == "")
- return true;
- if (ontology.submissionStatus.indexOf($scope.facets.missing_status.active) !== -1)
- return false;
- return true;
- }
- },
- upload_date: {
- active: "",
- ont_property: "creationDate",
- values: {day: 1, week: 7, month: 30, three_months: 90, six_months: 180, year: 365, all: "all"},
- day_text: ["day", "week", "month", "three_months", "six_months", "year", "all"],
- filter: function(ontology) {
- var active = $scope.facets.upload_date.active;
- if (active == "")
- return true;
- if (!ontology.submission)
- return false;
- var ontDate = new Date(ontology.creationDate);
- var compareDate = new Date();
- compareDate.setDate(compareDate.getDate() - active);
- if (ontDate >= compareDate)
- return true;
- return false;
- }
- },
- formality_levels: {
- active: [],
- ont_property: "hasFormalityLevel",
- filter: function(ontology) {
- if ($scope.facets.formality_levels.active.length == 0)
- return true;
- if ($scope.facets.formality_levels.active.indexOf(ontology.hasFormalityLevel) === -1)
- return false;
- return true;
- }
- },
- natural_languages: {
- active: [],
- ont_property: "naturalLanguage",
- filter: function(ontology) {
- if ($scope.facets.natural_languages.active.length == 0)
- return true;
- if ( !ontology.naturalLanguage )
- return false;
- for (var i = 0; i < ontology.naturalLanguage.length; i++) {
- if ($scope.facets.natural_languages.active.indexOf(ontology.naturalLanguage[i]) !== -1)
- return true;
- }
- return false;
- }
- },
- is_of_type: {
- active: [],
- ont_property: "isOfType",
- filter: function(ontology) {
- if ($scope.facets.is_of_type.active.length == 0)
- return true;
- if ($scope.facets.is_of_type.active.indexOf(ontology.isOfType) === -1)
- return false;
- return true;
- }
- }
- }
-
- // Instantiate object counts
- // This doesn't happen on the facet itself because
- // there is a $watch directive and updating counts
- // on the facets causes an infinite loop.
- $scope.facet_counts = {};
- Object.keys($scope.facets).forEach(function(facet) {$scope.facet_counts[facet] = {}});
-
- // Default values for facets that aren't definied on the ontologies
- $scope.types = {
- ontology: {sort: 1, id: "ontology"},
- ontology_view: {sort: 2, id: "ontology_view"}
- };
- $scope.artifacts = ["notes", "reviews", "projects", "summary_only"];
-
- $scope.groupAcronyms = function(groups) {
- var groupNames = [];
- angular.forEach(groups, function(group) {
- groupNames.push($scope.groups_hash[group].acronym);
- });
- return groupNames;
- };
-
- $scope.categoryNames = function(categories) {
- var catNames = [];
- angular.forEach(categories, function(category) {
- catNames.push($scope.categories_hash[category].name)
- })
- return catNames;
- }
-
- $scope.adminUsernames = function(admins) {
- return admins.map(function(a){return a.split('/').slice(-1)[0]});
- }
-
- $scope.ontologySortOrder = function(newOrder) {
- $scope.ontology_sort_order = newOrder;
- }
-
- // This watches the facets and updates the list depending on which facets are selected
- // All facets are basically ANDed together and return true if no options under the facet are selected.
- $scope.$watch('facets', function() {
- filterOntologies();
- }, true);
-
- $scope.$watch('searchText', function() {
- filterOntologies();
- });
-
- var filterOntologies = function() {
- var key, i, ontology, facet, facet_count, show, other_facets, count = 0;
- $scope.show_highlight = false;
- $scope.show_highlight = true;
-
- // Reset facet counts
- Object.keys($scope.facet_counts).forEach(function(key) {
- $scope.facet_counts[key] = {};
- });
-
- // First, filter by search. Do this first because the facets
- // will apply on top of the search results (EX: for hiding views)
- filterSearch();
-
- // Filter ontologies based on facet + count for facets
- for (i = 0; i < $scope.ontologies.length; i++) {
- ontology = $scope.ontologies[i];
-
- if (searchActive() && ontology.show === false) continue;
-
- // Filter out ontologies based on their facet filter functions
- ontology.show = Object.keys($scope.facets).map(function(key) {
- return $scope.facets[key].filter(ontology);
- }).every(Boolean);
-
- // Check each facet entry to calculate counts
- // Counts are calculated by looking at whether or not ontologies match OTHER facets
- // IE, counts show what will be available for a given facet entry if that entry
- // were to be selected relative to what is already selected in other facets.
- Object.keys($scope.facets).forEach(function(key) {
- facet = $scope.facets[key];
- other_facets = Object.keys($scope.facets).filter(function(f){return key != f});
- show = other_facets.map(function(other_facet){return $scope.facets[other_facet].filter(ontology)}).every(Boolean);
- if (show) {
- facet_count = $scope.facet_counts[key];
- if (angular.isArray(ontology[facet.ont_property])) {
- ontology[facet.ont_property].forEach(function(val) {
- facet_count[val] = (facet_count[val] || 0) + 1;
- });
- } else {
- facet_count[ontology[facet.ont_property]] = (facet_count[ontology[facet.ont_property]] || 0) + 1;
- }
- }
- });
- }
-
- $scope.visible_ont_count = $scope.ontologies.filter(function(ont) {return ont.show}).length;
-
- // Highlight the count
- count = $("#visible_ont_count");
- if (count.hasClass("trigger_highlight")) {
- $animate.removeClass(count, "trigger_highlight");
- } else {
- $animate.addClass(count, "trigger_highlight");
- }
- }
-
- var filterSearch = function() {
- var i, results, ontology, found = {};
- if (!searchActive()) {
- $scope.ontologySortOrder($scope.previous_sort_order);
- return;
- }
- if ($scope.ontology_sort_order !== "-search_rank") {
- $scope.previous_sort_order = $scope.ontology_sort_order;
- }
- $scope.ontologySortOrder("-search_rank");
- results = $scope.ontIndex.search($scope.searchText);
-
- angular.forEach(results, function(r){found[r.ref] = r});
- for (i = 0; i < $scope.ontologies.length; i++) {
- ontology = $scope.ontologies[i];
- ontology.show = false;
- ontology.search_rank = 0;
- if (found[ontology.id]) {
- ontology.show = true;
- ontology.search_rank = found[ontology.id].score;
- }
- }
- }
-
- var searchActive = function() {
- return !($scope.searchText === null || $scope.searchText === "");
- }
-
- var countAllInFacet = function(facet) {
- var active_facets = Object.keys($scope.facets).filter(function(facet) {return $scope.facets[facet].active.length > 0});
- if (active_facets.length == 0 || (active_facets.length == 1 && active_facets[0] == facet)) {
- return true;
- }
- return false;
- }
-
- var intersection = function(x, y) {
- if (typeof x === 'undefined' || typeof y === 'undefined') {return [];}
- var ret = [];
- for (var i = 0; i < x.length; i++) {
- for (var z = 0; z < y.length; z++) {
- if (x[i] == y[z]) {
- ret.push(i);
- break;
- }
- }
- }
- return ret;
- }
-
-
- $scope.init = function() {
- $scope.ontologies = jQuery(document).data().bp.fullOntologies;
- if (BP_queryString().filter) {
- angular.forEach($scope.groups, function(group) {
- if (group.acronym == BP_queryString().filter)
- $scope.facets.groups.active.push(group.id);
- });
- }
- filterOntologies();
- angular.forEach($scope.ontologies, function(ont) {
- $scope.ontIndex.add({
- id: ont.id,
- acronym: ont.acronym,
- name: ont.name,
- description: ont.description
- })
- });
- }
- $timeout($scope.init);
-
-}])
-
-.filter('idToTitle', function() {
- return function(input) {
- if (input) {
- var splitInput = input.replace(/_/g, " ").split(" ");
- var newInput = [];
- var word;
- for (word in splitInput) {
- word = splitInput[word];
- if (word[0].toUpperCase() == word[0]) {
- newInput.push(word);
- } else {
- newInput.push(word[0].toUpperCase() + word.slice(1));
- }
-
- }
- return newInput.join(" ");
- }
- };
-})
-
-.filter('humanShortNum', function() {
- return function(input) {
- if (input) {
- var num = parseInt(input);
- if (num < 10000) {return num;}
- if (num > 10000 && num < 1000000) {
- return String(+(Math.round(num / 1000 + "e+1") + "e-1")) + "k"
- }
- if (num > 1000000) {
- return String(+(Math.round(num / 100000 + "e+1") + "e-1")) + "M"
- }
- return newInput.join(" ");
- }
- };
-})
-
-.filter("toArray", function() {
- return function(obj) {
- var result = [];
- angular.forEach(obj, function(val, key) {
- result.push(val);
- });
- return result;
- };
-})
-
-.filter('htmlToText', function() {
- return function(text) {
- return String(text).replace(/<[^>]+>/gm, '');
- }
-})
-
-.filter('descriptionToText', function() {
- return function(text) {
- text = String(text).replace(/<[^>]+>/gm, '');
- return text.split(/\.\W/)[0];
- }
-})
-;
\ No newline at end of file
diff --git a/public/browse/bower.json b/public/browse/bower.json
deleted file mode 100755
index ed30585ecb..0000000000
--- a/public/browse/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "angular-seed",
- "description": "A starter project for AngularJS",
- "version": "0.0.0",
- "homepage": "https://github.com/angular/angular-seed",
- "license": "MIT",
- "private": true,
- "dependencies": {
- "angular": "1.3.x",
- "angular-route": "1.3.x",
- "angular-loader": "1.3.x",
- "angular-mocks": "~1.3.x",
- "angular-animate": "1.3.x",
- "html5-boilerplate": "~4.3.0",
- "lunr.js": "~0.5.6",
- "angular-bindonce": "~0.3.3"
- }
-}
diff --git a/public/browse/checklist-model.js b/public/browse/checklist-model.js
deleted file mode 100644
index 228f0c5a03..0000000000
--- a/public/browse/checklist-model.js
+++ /dev/null
@@ -1,99 +0,0 @@
-/**
- * Checklist-model
- * AngularJS directive for list of checkboxes
- */
-
-angular.module('checklist-model', [])
-.directive('checklistModel', ['$parse', '$compile', function($parse, $compile) {
- // contains
- function contains(arr, item) {
- if (angular.isArray(arr)) {
- for (var i = 0; i < arr.length; i++) {
- if (angular.equals(arr[i], item)) {
- return true;
- }
- }
- }
- return false;
- }
-
- // add
- function add(arr, item) {
- arr = angular.isArray(arr) ? arr : [];
- for (var i = 0; i < arr.length; i++) {
- if (angular.equals(arr[i], item)) {
- return arr;
- }
- }
- arr.push(item);
- return arr;
- }
-
- // remove
- function remove(arr, item) {
- if (angular.isArray(arr)) {
- for (var i = 0; i < arr.length; i++) {
- if (angular.equals(arr[i], item)) {
- arr.splice(i, 1);
- break;
- }
- }
- }
- return arr;
- }
-
- // http://stackoverflow.com/a/19228302/1458162
- function postLinkFn(scope, elem, attrs) {
- // compile with `ng-model` pointing to `checked`
- $compile(elem)(scope);
-
- // getter / setter for original model
- var getter = $parse(attrs.checklistModel);
- var setter = getter.assign;
-
- // value added to list
- var value = $parse(attrs.checklistValue)(scope.$parent);
-
- // watch UI checked change
- scope.$watch('checked', function(newValue, oldValue) {
- if (newValue === oldValue) {
- return;
- }
- var current = getter(scope.$parent);
- if (newValue === true) {
- setter(scope.$parent, add(current, value));
- } else {
- setter(scope.$parent, remove(current, value));
- }
- });
-
- // watch original model change
- scope.$parent.$watch(attrs.checklistModel, function(newArr, oldArr) {
- scope.checked = contains(newArr, value);
- }, true);
- }
-
- return {
- restrict: 'A',
- priority: 1000,
- terminal: true,
- scope: true,
- compile: function(tElement, tAttrs) {
- if (tElement[0].tagName !== 'INPUT' || tElement[0].getAttribute('type') !== 'checkbox') {
- throw 'checklist-model should be applied to `input[type="checkbox"]`.';
- }
-
- if (!tAttrs.checklistValue) {
- throw 'You should provide `checklist-value`.';
- }
-
- // exclude recursion
- tElement.removeAttr('checklist-model');
-
- // local scope var storing individual checkbox model
- tElement.attr('ng-model', 'checked');
-
- return postLinkFn;
- }
- };
-}]);
diff --git a/public/browse/e2e-tests/protractor.conf.js b/public/browse/e2e-tests/protractor.conf.js
deleted file mode 100755
index b45a117aa5..0000000000
--- a/public/browse/e2e-tests/protractor.conf.js
+++ /dev/null
@@ -1,19 +0,0 @@
-exports.config = {
- allScriptsTimeout: 11000,
-
- specs: [
- '*.js'
- ],
-
- capabilities: {
- 'browserName': 'chrome'
- },
-
- baseUrl: 'http://localhost:8000/app/',
-
- framework: 'jasmine',
-
- jasmineNodeOpts: {
- defaultTimeoutInterval: 30000
- }
-};
diff --git a/public/browse/e2e-tests/scenarios.js b/public/browse/e2e-tests/scenarios.js
deleted file mode 100755
index 59d816fc1f..0000000000
--- a/public/browse/e2e-tests/scenarios.js
+++ /dev/null
@@ -1,42 +0,0 @@
-'use strict';
-
-/* https://github.com/angular/protractor/blob/master/docs/toc.md */
-
-describe('faceted browsing', function() {
-
- browser.get('index.html');
-
- it('should automatically redirect to /view1 when location hash/fragment is empty', function() {
- expect(browser.getLocationAbsUrl()).toMatch("/view1");
- });
-
-
- describe('view1', function() {
-
- beforeEach(function() {
- browser.get('index.html#/view1');
- });
-
-
- it('should render view1 when user navigates to /view1', function() {
- expect(element.all(by.css('[ng-view] p')).first().getText()).
- toMatch(/partial for view 1/);
- });
-
- });
-
-
- describe('view2', function() {
-
- beforeEach(function() {
- browser.get('index.html#/view2');
- });
-
-
- it('should render view2 when user navigates to /view2', function() {
- expect(element.all(by.css('[ng-view] p')).first().getText()).
- toMatch(/partial for view 2/);
- });
-
- });
-});
diff --git a/public/browse/font/webhostinghub-glyphs.eot b/public/browse/font/webhostinghub-glyphs.eot
deleted file mode 100755
index 7e9248ceef3b9f72dd6a29ea4901c3ce603d2b74..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 503876
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