mirror of
https://github.com/google/nomulus.git
synced 2025-06-21 03:40:47 +02:00
Move JS and CSS files to a Javascript source dir (#156)
This commit is contained in:
parent
75c3792c4b
commit
153887df11
43 changed files with 8 additions and 17 deletions
24
core/src/main/javascript/google/registry/ui/compile_test.js
Normal file
24
core/src/main/javascript/google/registry/ui/compile_test.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Test existing solely to run the :check BUILD rule.
|
||||
*/
|
||||
|
||||
goog.setTestOnly();
|
||||
|
||||
goog.require('goog.testing.jsunit');
|
||||
|
||||
|
||||
function testNothing() {}
|
|
@ -0,0 +1,84 @@
|
|||
/** Admin Settings */
|
||||
|
||||
div#tlds div.tld {
|
||||
width: 209px;
|
||||
}
|
||||
|
||||
#newTld {
|
||||
width: 187px;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
div#tlds div.tld input,
|
||||
div#tlds div.tld button[type=button] {
|
||||
height: 27px;
|
||||
line-height: 27px;
|
||||
background: #ebebeb;
|
||||
vertical-align: top;
|
||||
border: none;
|
||||
border-bottom: solid 3px white;
|
||||
}
|
||||
|
||||
div#tlds div.tld input {
|
||||
width: 169px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: #555;
|
||||
padding-left: 5px ! important;
|
||||
}
|
||||
|
||||
div#tlds.editing div.tld input[readonly] {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
div#tlds.editing div.tld button[type=button] {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
margin-left: -2px;
|
||||
width: 30px;
|
||||
min-width: 30px;
|
||||
height: 30px;
|
||||
color: grey;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
div#tlds.editing div.tld button[type=button]:hover {
|
||||
-webkit-box-shadow: none;
|
||||
-moz-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
div#tlds.editing div.tld button[type=button] i {
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
div#tlds.editing .kd-errormessage {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
#ote-results-table {
|
||||
margin-left: 0.5em;
|
||||
margin-top: -5px;
|
||||
border-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.ote-fulfilled {
|
||||
background-color: #9df797;
|
||||
}
|
||||
|
||||
.ote-semifulfilled {
|
||||
background-color: #fcd18e;
|
||||
}
|
||||
|
||||
.ote-unfulfilled {
|
||||
background-color: #ffa9a9;
|
||||
}
|
||||
|
||||
.ote-results-header {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.ote-results-header-cell {
|
||||
vertical-align: bottom;
|
||||
}
|
77
core/src/main/javascript/google/registry/ui/css/console.css
Normal file
77
core/src/main/javascript/google/registry/ui/css/console.css
Normal file
|
@ -0,0 +1,77 @@
|
|||
.description {
|
||||
display: block;
|
||||
clear: both;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
line-height: 140%;
|
||||
}
|
||||
|
||||
.whoAreYou {
|
||||
width: 50%;
|
||||
margin: 5em auto;
|
||||
}
|
||||
|
||||
/* Console disabled page. */
|
||||
.whoAreYou-disabled {
|
||||
width: 50%;
|
||||
margin: 5em auto;
|
||||
}
|
||||
|
||||
.whoAreYou-disabled p img {
|
||||
display: block;
|
||||
margin: 3em auto;
|
||||
}
|
||||
|
||||
.whoAreYou-disabled h1 {
|
||||
border-top: solid 1px #ebebeb;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
/* XXX: Should be re-enabled when search works. */
|
||||
#kd-search {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* XXX: Should be generalized for use throughout console. */
|
||||
div.domain-registrar-contact div.tooltip {
|
||||
visibility: hidden;
|
||||
/* XXX: Should have auto-width. */
|
||||
width: 110px;
|
||||
left: -55px;
|
||||
height: 1em;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
color: white;
|
||||
background: #2d2d2d;
|
||||
padding: 0.5em;
|
||||
z-index: 2000;
|
||||
margin-top: -30px;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
div.domain-registrar-contact div.tooltip .pointer {
|
||||
outline: none;
|
||||
display: block;
|
||||
position: relative;
|
||||
bottom: -7px;
|
||||
left: 55px;
|
||||
margin: 0 0 0 -5px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
line-height: 0px;
|
||||
font-size: 0px;
|
||||
/* This sets the tooptip pointer color */
|
||||
border-bottom: transparent;
|
||||
border-left: 5px solid transparent;
|
||||
border-right: 5px solid transparent;
|
||||
border-top: 5px solid #2d2d2d;
|
||||
}
|
||||
|
||||
.reg-cryingAndroid {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.reg-bullets {
|
||||
padding-left: 1em;
|
||||
list-style: disc inside;
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
.domain-registrar-contacts {
|
||||
vertical-align: top;
|
||||
text-align: top;
|
||||
}
|
||||
|
||||
.domain-registrar-contact {
|
||||
display: table-cell;
|
||||
padding-right: 4em;
|
||||
padding-bottom: 2em;
|
||||
}
|
||||
|
||||
.domain-registrar-contact div {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
.domain-registrar-contact + br {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.domain-registrar-contact-name {
|
||||
font-weight: bold;
|
||||
display: inline;
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
.domain-registrar-contact-name i {
|
||||
float: right;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.domain-registrar-contact-name i.domain-registrar-contact-visible-in-whois {
|
||||
background: url('/assets/images/visibleOn_16.png') no-repeat right;
|
||||
}
|
||||
|
||||
/** Postal style for address. */
|
||||
td.setting-group-compact div.contact-address-city,
|
||||
td.setting-group-compact div.contact-address-state,
|
||||
td.setting-group-compact div.contact-address-zip,
|
||||
td.setting-group-compact div.contact-address-cc {
|
||||
width: initial;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/** Back to regular box flow for phone. */
|
||||
td.setting-group-compact input#phoneNumber {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vertical align shim for contact tds. Using border here instead of
|
||||
* padding since this is a table and padding is ignored.
|
||||
*
|
||||
* @see td.setting p
|
||||
*/
|
||||
td.domain-registrar-contacts {
|
||||
border-top: solid 0.5em white;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
#domain-registrar-contact-us .description * {
|
||||
color: #999 !important;
|
||||
line-height: 140%;
|
||||
}
|
||||
|
||||
#domain-registrar-contact-us p {
|
||||
margin-bottom: 1.5em;
|
||||
line-height: 140%;
|
||||
}
|
||||
|
||||
#registry-phone {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 1em;
|
||||
background-color: #eaeaea;
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
#domain-registrar-dashboard {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#domain-registrar-dashboard p {
|
||||
color: grey;
|
||||
}
|
||||
|
||||
#domain-registrar-dashboard super {
|
||||
color: red;
|
||||
font-size: 0.5em;
|
||||
vertical-align: super;
|
||||
font-weight: bold;
|
||||
padding-left: 0.5em;
|
||||
}
|
||||
|
||||
#domain-registrar-dashboard table {
|
||||
border-collapse: collapse;
|
||||
margin: 3em auto;
|
||||
}
|
||||
|
||||
#domain-registrar-dashboard div.dashbox {
|
||||
width: 230px;
|
||||
min-width: 230px;
|
||||
height: 260px;
|
||||
padding: 1em 2em;
|
||||
margin: 0 1.5em;
|
||||
color: #777;
|
||||
border-radius: 10px;
|
||||
background-color: #f9f9f9;
|
||||
text-align: center;
|
||||
line-height: 140%;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
#domain-registrar-dashboard div.dashbox h2 {
|
||||
font-weight: bold;
|
||||
color: #555;
|
||||
margin: 2em auto 1em auto;
|
||||
}
|
||||
|
||||
#domain-registrar-dashboard div.dashbox a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#domain-registrar-dashboard table img {
|
||||
display: block;
|
||||
margin: 1em auto;
|
||||
padding: 1em auto;
|
||||
}
|
||||
|
||||
#domain-registrar-dashboard table + p {
|
||||
margin: 2em auto;
|
||||
width: 75%;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#domain-registrar-dashboard p img {
|
||||
margin-right: 1em;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
}
|
File diff suppressed because one or more lines are too long
61
core/src/main/javascript/google/registry/ui/css/epp.css
Normal file
61
core/src/main/javascript/google/registry/ui/css/epp.css
Normal file
|
@ -0,0 +1,61 @@
|
|||
/* EPP styles */
|
||||
|
||||
.reg-add:before {
|
||||
content: '+ ';
|
||||
}
|
||||
|
||||
.contact h1 {
|
||||
width: 650px;
|
||||
background: url('/assets/images/ic_contacts_blue_12.png') no-repeat 0 0.7em;
|
||||
padding-left: 2em;
|
||||
margin-left: 0.75em;
|
||||
}
|
||||
|
||||
/* Remove button. */
|
||||
#contact-postalInfo table button {
|
||||
margin: 1em;
|
||||
float: right;
|
||||
}
|
||||
|
||||
#contact-postalInfoHeader {
|
||||
color: #777;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
#contact-postalInfo .info {
|
||||
font-size: 11px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
#contact-postalInfo table {
|
||||
margin: 0 0 1em 0;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
#contact-postalInfo tr {
|
||||
margin: auto 2em;
|
||||
}
|
||||
|
||||
#contact-postalInfo tr:first-child {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
#contact-postalInfo tr:last-child {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
#contact-postalInfo table,
|
||||
#contact-postalInfo input[readonly],
|
||||
#contact-postalInfo textarea[readonly] {
|
||||
background-color: #f1f1f1;
|
||||
border-color: #f1f1f1;
|
||||
}
|
||||
|
||||
#contact-postalInfoHeader button {
|
||||
display: block;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
#contact-postalInfoHeader button[disabled] {
|
||||
display: none;
|
||||
}
|
187
core/src/main/javascript/google/registry/ui/css/forms.css
Normal file
187
core/src/main/javascript/google/registry/ui/css/forms.css
Normal file
|
@ -0,0 +1,187 @@
|
|||
form.set,
|
||||
form.item,
|
||||
div.set,
|
||||
div.item {
|
||||
width: 700px;
|
||||
}
|
||||
|
||||
.set table,
|
||||
.item table {
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
/* Going to play some games here to get section leads to float left,
|
||||
* followed by their next rows in floated right column. */
|
||||
.item table,
|
||||
.set table {
|
||||
padding-top: 2em;
|
||||
margin-top: 1em;
|
||||
border-top: solid 1px #ebebeb;
|
||||
}
|
||||
|
||||
.set tr,
|
||||
.item tr {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 28px;
|
||||
}
|
||||
|
||||
.set td,
|
||||
.item td {
|
||||
padding: 0.5em 0;
|
||||
}
|
||||
|
||||
.item tr.section-lead th h2,
|
||||
.item tr.section-lead th h3 {
|
||||
font-size: 1em;
|
||||
font-weight: bold;
|
||||
color: #15c;
|
||||
}
|
||||
|
||||
tr.subsection h3::first-letter {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.set td:first-child,
|
||||
.item td:first-child {
|
||||
width: 260px;
|
||||
}
|
||||
|
||||
.set td:first-child span.description,
|
||||
.item td:first-child span.description {
|
||||
width: 170px;
|
||||
}
|
||||
|
||||
.description ol {
|
||||
list-style-type: decimal;
|
||||
margin-left: 2em;
|
||||
}
|
||||
|
||||
|
||||
/* Setting groups and labels. */
|
||||
|
||||
.set .kd-settings-pane-section td {
|
||||
border-bottom: solid 1px #ebebeb;
|
||||
}
|
||||
|
||||
td.setting-group-compact {
|
||||
padding-left: 0.5em;
|
||||
}
|
||||
|
||||
.setting-group-compact div {
|
||||
line-height: 140%;
|
||||
}
|
||||
|
||||
.item label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.set label + input {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.kd-settings-pane-section td label.setting-label {
|
||||
padding-top: 0.5em;
|
||||
}
|
||||
|
||||
td.label {
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
/* Compact sequence of labels. */
|
||||
.setting label + label {
|
||||
margin-left: 2em;
|
||||
}
|
||||
|
||||
form label,
|
||||
.setting-label {
|
||||
line-height: 13px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.setting label {
|
||||
display: inline-block;
|
||||
font-weight: normal;
|
||||
margin-right: 1em;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
div.checkbox-with-label {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
|
||||
/* Controls */
|
||||
|
||||
.set input:not([type="submit"]),
|
||||
.item input:not([type="submit"]) {
|
||||
width: 250px;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.item input[type='checkbox'],
|
||||
.item input[type='radio'] {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
input[readonly] {
|
||||
border: solid 1px white;
|
||||
margin-left: 0.5em;
|
||||
padding-top: 0 !important;
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
|
||||
.setting input[type=radio] {
|
||||
}
|
||||
|
||||
.setting input[type=radio],
|
||||
.setting input[type=checkbox] {
|
||||
position: relative;
|
||||
top: -3px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Consistent top space in second column items. Input elements need to
|
||||
* switch from using margin to padding when they're edit toggled, so that
|
||||
* the text doesn't move. The other elements are just defined here for
|
||||
* consistency.
|
||||
* @see td.domain-registrar-contacts
|
||||
*/
|
||||
input[readonly],
|
||||
td.setting p {
|
||||
margin-top: 0.5em;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
.item button[disabled] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.checkbox-with-label input[type='checkbox'],
|
||||
div.checkbox-with-label input[type='checkbox'] + label {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/*
|
||||
* 3 line street has just inputs in a row
|
||||
* @see td padding-bottom
|
||||
*/
|
||||
.setting input + input,
|
||||
.setting div {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.setting div {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
.setting input[type='checkbox'] {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.setting input + button,
|
||||
.setting-item-list {
|
||||
margin-left: 0.5em;
|
||||
}
|
4493
core/src/main/javascript/google/registry/ui/css/kd_components.css
Normal file
4493
core/src/main/javascript/google/registry/ui/css/kd_components.css
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,11 @@
|
|||
@import 'admin-settings.css';
|
||||
@import 'console.css';
|
||||
@import 'contact-settings.css';
|
||||
@import 'contact-us.css';
|
||||
@import 'dashboard.css';
|
||||
@import 'epp.css';
|
||||
@import 'forms.css';
|
||||
@import 'kd_components.css';
|
||||
@import 'registry.css';
|
||||
@import 'resources.css';
|
||||
@import 'security-settings.css';
|
298
core/src/main/javascript/google/registry/ui/css/registry.css
Normal file
298
core/src/main/javascript/google/registry/ui/css/registry.css
Normal file
|
@ -0,0 +1,298 @@
|
|||
body {
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden !important;
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
h1 {
|
||||
width: 100%;
|
||||
font-size: 1.5em;
|
||||
/* Bottom padding to get the paragraph text and Billing & resources to
|
||||
* line up. */
|
||||
padding: 0.75em 0.75em 3px 0;
|
||||
}
|
||||
|
||||
#kd-social a,
|
||||
#kd-social a:visited,
|
||||
#reg-content a,
|
||||
#reg-content a:visited {
|
||||
color: #15c;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
table {
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
/* Set some explicit form styles so there's no jumping during toggle
|
||||
* from readonly to editable. */
|
||||
input, textarea {
|
||||
border-style: solid;
|
||||
border-color: lightgrey;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
input[readonly], textarea[readonly] {
|
||||
resize: none;
|
||||
border-color: white;
|
||||
}
|
||||
|
||||
textarea {
|
||||
margin-bottom: 1em;
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
font-size: 13px;
|
||||
border: solid 1px #ddd;
|
||||
}
|
||||
|
||||
textarea[readonly] {
|
||||
background-color: #eaeaea;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: none;
|
||||
border-top: solid 1px #ebebeb;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#kd-googlebar {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
min-width: 985px;
|
||||
padding: 1em 2em;
|
||||
white-space: nowrap;
|
||||
height: 37px;
|
||||
}
|
||||
|
||||
#kd-googlebar a.logo,
|
||||
#kd-searchfield,
|
||||
#kd-searchbutton {
|
||||
position: static;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#kd-search {
|
||||
width: 470px;
|
||||
margin-top: 2px;
|
||||
padding-top: 0px;
|
||||
padding-left: 3em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#kd-search form {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
input#kd-searchfield,
|
||||
#kd-searchbutton {
|
||||
height: 29px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
input#kd-searchfield {
|
||||
width: 400px;
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
#kd-searchbutton {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
#kd-social {
|
||||
position: fixed;
|
||||
top: 1.25em;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
#kd-social .kd-name {
|
||||
float: none;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.kd-name a:before {
|
||||
content: ' | ';
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* Logo */
|
||||
a.logo {
|
||||
vertical-align: middle;
|
||||
font-size: 30px;
|
||||
font-weight: 300;
|
||||
font-family: "open sans", sans-serif;
|
||||
color: #63666a;
|
||||
}
|
||||
|
||||
a.logo * {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* Reg prefix. */
|
||||
|
||||
.reg-user-id {
|
||||
display: block;
|
||||
height: 1.5em;
|
||||
padding: 0;
|
||||
margin-top: -10px;
|
||||
}
|
||||
|
||||
/* Misc. */
|
||||
|
||||
#loady {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: -30px;
|
||||
}
|
||||
|
||||
.eppResponse {
|
||||
color: #333;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.kd-appbar {
|
||||
padding: 1em 0;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
li.kd-menulistitem {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
#reg-nav.shown {
|
||||
display: inherit;
|
||||
}
|
||||
|
||||
/* Begin fixed headers and nav selectors */
|
||||
#reg-app {
|
||||
float: left;
|
||||
margin-top: 64px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.kd-butterbar {
|
||||
position: absolute;
|
||||
display: block;
|
||||
margin-left: inherit;
|
||||
}
|
||||
|
||||
.kd-butterbar.shown {
|
||||
-webkit-transform: translateX(-50%);
|
||||
-ms-transform: translateX(-50%);
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.kd-appbar {
|
||||
/* same as in reg-content below. lines the left edge of the
|
||||
appbuttons and content area with the 'r' in registry. */
|
||||
padding-left: 173px;
|
||||
padding-top: .75em;
|
||||
}
|
||||
|
||||
.kd-content-sidebar {
|
||||
margin-left: 15px;
|
||||
padding-left: 0;
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
#reg-nav {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 136px;
|
||||
width: 155px;
|
||||
margin: 0 25px 0 0;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
#reg-navlist li {
|
||||
margin-left: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
#reg-navlist li ul {
|
||||
margin-left: 0em;
|
||||
padding-left: 2em;
|
||||
}
|
||||
|
||||
.reg-navlist-sub {
|
||||
padding-left: 3px;
|
||||
}
|
||||
|
||||
#reg-navlist a {
|
||||
margin-left: 0;
|
||||
padding-left: 2em;
|
||||
border-left: solid 3px white;
|
||||
}
|
||||
|
||||
#reg-navlist li ul,
|
||||
#reg-navlist a {
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
#reg-navlist li ul li {
|
||||
margin-left: -2em;
|
||||
}
|
||||
|
||||
#reg-navlist li ul li a {
|
||||
padding-left: 3em;
|
||||
}
|
||||
|
||||
#reg-navlist a.domain-active-nav {
|
||||
border-left: solid 3px red;
|
||||
font-weight: bold;
|
||||
color: #bf624B;
|
||||
}
|
||||
|
||||
#reg-content-and-footer {
|
||||
position: absolute;
|
||||
top: 136px;
|
||||
left: 173px;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 25px 0 1em 0;
|
||||
overflow-y: scroll !important;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
#reg-content {
|
||||
margin-bottom: 100px;
|
||||
}
|
||||
|
||||
#reg-content,
|
||||
.pageFooter {
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
#debug {
|
||||
position: absolute;
|
||||
top: 127px;
|
||||
right: 0;
|
||||
width: 15%;
|
||||
border-left: solid 1px grey;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
/* End fixed headers and nav selectors */
|
||||
|
||||
#reg-content,
|
||||
#reg-login {
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.reg-select {
|
||||
margin-left: 23px;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
#domain-registrar-resources h2 {
|
||||
border-top: solid 1px #eee;
|
||||
padding-top: 1em;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
#domain-registrar-resources h2 img {
|
||||
vertical-align: middle;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
#domain-registrar-resources h2 + p,
|
||||
#domain-registrar-resources button,
|
||||
#domain-registrar-resources em {
|
||||
margin-left: 34px; /* Folder icon + ^^ 10px more */
|
||||
}
|
||||
|
||||
#domain-registrar-resources h2 + p {
|
||||
padding-top: 1em;
|
||||
}
|
||||
|
||||
#domain-registrar-resources a {
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
#domain-registrar-resources em {
|
||||
color: red;
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/** Security Settings */
|
||||
#domain-registrar-phone-passcode,
|
||||
#domain-registrar-phone-passcode input {
|
||||
color: #3D9200;
|
||||
}
|
||||
|
||||
div#ips div.ip {
|
||||
width: 209px;
|
||||
}
|
||||
|
||||
#newIp {
|
||||
width: 187px;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
div#ips.editing div.ip input,
|
||||
div#ips.editing div.ip button[type=button] {
|
||||
display: inline-block;
|
||||
height: 27px;
|
||||
line-height: 27px;
|
||||
background: #ebebeb;
|
||||
vertical-align: top;
|
||||
border: none;
|
||||
border-bottom: solid 3px white;
|
||||
}
|
||||
|
||||
div#ips.editing div.ip input {
|
||||
width: 169px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: #555;
|
||||
padding-left: 5px ! important;
|
||||
}
|
||||
|
||||
div#ips.editing div.ip input[readonly] {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
div#ips.editing div.ip button[type=button] {
|
||||
float: right;
|
||||
margin-left: -2px;
|
||||
width: 30px;
|
||||
min-width: 30px;
|
||||
height: 30px;
|
||||
color: grey;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
div#ips.editing div.ip button[type=button]:hover {
|
||||
-webkit-box-shadow: none;
|
||||
-moz-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
div#ips.editing div.ip button[type=button] i {
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
div#ips.editing .kd-errormessage {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
.item td.certificate h4 {
|
||||
margin-top: 1em;
|
||||
text-transform: lowercase;
|
||||
border: solid 1px red;
|
||||
}
|
150
core/src/main/javascript/google/registry/ui/externs/json.js
Normal file
150
core/src/main/javascript/google/registry/ui/externs/json.js
Normal file
|
@ -0,0 +1,150 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview External JSON definitions. The purpose of this file is to give
|
||||
* type information to the JavaScript compiler so it won't rename these
|
||||
* properties.
|
||||
* @externs
|
||||
*/
|
||||
|
||||
/**
|
||||
* @suppress {duplicate}
|
||||
*/
|
||||
var registry = {};
|
||||
|
||||
|
||||
/**
|
||||
* @suppress {duplicate}
|
||||
*/
|
||||
registry.json = {};
|
||||
|
||||
registry.json.ote = {};
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* description: string,
|
||||
* requirement: number,
|
||||
* timesPerformed: number,
|
||||
* completed: boolean
|
||||
* }}
|
||||
*/
|
||||
registry.json.ote.OteStatusDetail;
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* clientId: string,
|
||||
* completed: boolean,
|
||||
* details: !Array.<registry.json.ote.OteStatusDetail>
|
||||
* }}
|
||||
*/
|
||||
registry.json.ote.OteStatusResult;
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* status: string,
|
||||
* message: string,
|
||||
* results: !Array.<registry.json.ote.OteStatusResult>
|
||||
* }}
|
||||
*/
|
||||
registry.json.ote.OteStatusResponse;
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @template T
|
||||
*/
|
||||
registry.json.Response = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* Request state which can be `SUCCESS` or `ERROR`.
|
||||
* @type {string}
|
||||
*/
|
||||
registry.json.Response.prototype.status;
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
registry.json.Response.prototype.message;
|
||||
|
||||
|
||||
/**
|
||||
* @type {string|undefined}
|
||||
*/
|
||||
registry.json.Response.prototype.field;
|
||||
|
||||
|
||||
/**
|
||||
* @type {!Array.<T>}
|
||||
*/
|
||||
registry.json.Response.prototype.results;
|
||||
|
||||
|
||||
// XXX: Might not need undefineds here.
|
||||
/**
|
||||
* @typedef {{
|
||||
* allowedTlds: !Array<string>,
|
||||
* clientIdentifier: string,
|
||||
* clientCertificate: string?,
|
||||
* clientCertificateHash: string?,
|
||||
* failoverClientCertificate: string?,
|
||||
* failoverClientCertificateHash: string?,
|
||||
* driveFolderId: string?,
|
||||
* ianaIdentifier: (number?|undefined),
|
||||
* icannReferralEmail: string,
|
||||
* ipAddressWhitelist: !Array<string>,
|
||||
* emailAddress: (string?|undefined),
|
||||
* lastUpdateTime: string,
|
||||
* url: (string?|undefined),
|
||||
* phonePasscode: (string?|undefined),
|
||||
* phoneNumber: (string?|undefined),
|
||||
* faxNumber: (string?|undefined),
|
||||
* localizedAddress: registry.json.RegistrarAddress,
|
||||
* whoisServer: (string?|undefined),
|
||||
* referralUrl: (string?|undefined),
|
||||
* contacts: !Array.<registry.json.RegistrarContact>
|
||||
* }}
|
||||
*/
|
||||
registry.json.Registrar;
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* street: !Array.<string>,
|
||||
* city: string,
|
||||
* state: (string?|undefined),
|
||||
* zip: (string?|undefined),
|
||||
* countryCode: string
|
||||
* }}
|
||||
*/
|
||||
registry.json.RegistrarAddress;
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* name: (string?|undefined),
|
||||
* emailAddress: string,
|
||||
* visibleInWhoisAsAdmin: boolean,
|
||||
* visibleInWhoisAsTech: boolean,
|
||||
* visibleInDomainWhoisAsAbuse: boolean,
|
||||
* phoneNumber: (string?|undefined),
|
||||
* faxNumber: (string?|undefined),
|
||||
* types: (string?|undefined)
|
||||
* }}
|
||||
*/
|
||||
registry.json.RegistrarContact;
|
79
core/src/main/javascript/google/registry/ui/js/component.js
Normal file
79
core/src/main/javascript/google/registry/ui/js/component.js
Normal file
|
@ -0,0 +1,79 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.Component');
|
||||
|
||||
goog.forwardDeclare('registry.Console');
|
||||
goog.require('goog.events.EventHandler');
|
||||
goog.require('registry.util');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Base component for UI.
|
||||
*
|
||||
* <pre>
|
||||
* ui/js/component.js - Base UI class.
|
||||
* ^
|
||||
* |
|
||||
* edit_item.js - Common controls for editable items.
|
||||
* ^
|
||||
* \
|
||||
* |-ui/js/resource_component.js - JSON resources
|
||||
* ^
|
||||
* \
|
||||
* |- ui/js/registrar/settings.js
|
||||
* </pre>
|
||||
*
|
||||
* @param {!registry.Console} cons the console singleton.
|
||||
* @constructor
|
||||
* @extends {goog.events.EventHandler}
|
||||
*/
|
||||
registry.Component = function(cons) {
|
||||
registry.Component.base(this, 'constructor');
|
||||
|
||||
/** @type {!registry.Console} */
|
||||
this.console = cons;
|
||||
|
||||
/**
|
||||
* The hashPath this component is mapped by. This is set by the
|
||||
* console after construction.
|
||||
* @type {string}
|
||||
*/
|
||||
this.basePath = '';
|
||||
|
||||
/**
|
||||
* Bean counter that's used by `addRemBtnHandlers`,
|
||||
* e.g. `typeCounts['host']++` when user adds or removes.
|
||||
* @type {!Object.<string, number>}
|
||||
* @protected
|
||||
*/
|
||||
this.typeCounts = {};
|
||||
|
||||
/**
|
||||
* Stateful UI/server session model.
|
||||
* @type {?Object.<string, ?>}
|
||||
*/
|
||||
this.model = null;
|
||||
};
|
||||
goog.inherits(registry.Component, goog.events.EventHandler);
|
||||
|
||||
|
||||
/**
|
||||
* Subclasses should override this to implement panel display.
|
||||
* @param {string} id The target resource id.
|
||||
*/
|
||||
registry.Component.prototype.bindToDom = function(id) {
|
||||
registry.util.unbutter();
|
||||
};
|
144
core/src/main/javascript/google/registry/ui/js/console.js
Normal file
144
core/src/main/javascript/google/registry/ui/js/console.js
Normal file
|
@ -0,0 +1,144 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.Console');
|
||||
|
||||
goog.require('goog.Disposable');
|
||||
goog.require('goog.History');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.events.KeyCodes');
|
||||
goog.require('goog.history.EventType');
|
||||
goog.require('registry.util');
|
||||
|
||||
goog.forwardDeclare('goog.events.KeyEvent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Abstract console for both admin and registrar console UIs.
|
||||
* @constructor
|
||||
* @extends {goog.Disposable}
|
||||
*/
|
||||
registry.Console = function() {
|
||||
registry.Console.base(this, 'constructor');
|
||||
|
||||
/**
|
||||
* @type {!goog.History}
|
||||
* @protected
|
||||
*/
|
||||
this.history = new goog.History();
|
||||
};
|
||||
goog.inherits(registry.Console, goog.Disposable);
|
||||
|
||||
/**
|
||||
* Registers the console's events and sets everything up.
|
||||
*
|
||||
* Should be called after the constructor.
|
||||
*
|
||||
* The reason this isn't done in the constructor is that this is a base class
|
||||
* designed to be extended. We have to wait for the actual implementation to
|
||||
* finish constructing before using it.
|
||||
*/
|
||||
registry.Console.prototype.setUp = function() {
|
||||
goog.events.listen(
|
||||
this.history,
|
||||
goog.history.EventType.NAVIGATE,
|
||||
goog.bind(this.handleHashChange, this));
|
||||
|
||||
this.bindToDom();
|
||||
|
||||
// goog.History always starts off as "not enabled", meaning it doesn't trigger
|
||||
// the listeners on change.
|
||||
//
|
||||
// When it's set to be enabled, it will start triggering the listeners on
|
||||
// every change, but it also triggers the listeners immediately with the
|
||||
// current history entry.
|
||||
//
|
||||
// This means the handleHashChange listener registered above will be called
|
||||
// now.
|
||||
this.history.setEnabled(true);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Helper to setup permanent page elements.
|
||||
*/
|
||||
registry.Console.prototype.bindToDom = function() {
|
||||
registry.util.unbutter();
|
||||
goog.events.listen(goog.dom.getRequiredElement('kd-searchbutton'),
|
||||
goog.events.EventType.CLICK,
|
||||
goog.bind(this.onSearch_, this));
|
||||
goog.events.listen(goog.dom.getRequiredElement('kd-searchfield'),
|
||||
goog.events.EventType.KEYUP,
|
||||
goog.bind(this.onSearchFieldKeyUp_, this));
|
||||
goog.events.listen(
|
||||
goog.dom.getElementByClass(goog.getCssName('kd-butterbar-dismiss')),
|
||||
goog.events.EventType.CLICK,
|
||||
registry.util.unbutter);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Subclasses should override to visit the hash token given by
|
||||
* `goog.History.getToken()`.
|
||||
*/
|
||||
registry.Console.prototype.handleHashChange = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} resourcePath Resource description path.
|
||||
*/
|
||||
registry.Console.prototype.view = function(resourcePath) {
|
||||
// Setting the new history token will also trigger the handleHashChange
|
||||
// listener registered in the setUp() function.
|
||||
this.history.setToken(resourcePath);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler for search bar.
|
||||
* @private
|
||||
*/
|
||||
registry.Console.prototype.onSearch_ = function() {
|
||||
var qElt = goog.dom.getRequiredElement('kd-searchfield');
|
||||
if (qElt.getAttribute('disabled')) {
|
||||
return;
|
||||
}
|
||||
var query = qElt.value;
|
||||
if (query == '') {
|
||||
return;
|
||||
}
|
||||
// Filtering this value change event.
|
||||
qElt.setAttribute('disabled', true);
|
||||
qElt.value = '';
|
||||
this.view(query);
|
||||
qElt.removeAttribute('disabled');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler for key press in the search input field.
|
||||
* @param {!goog.events.KeyEvent} e Key event to handle.
|
||||
* @return {boolean} Whether the event should be continued or cancelled.
|
||||
* @private
|
||||
*/
|
||||
registry.Console.prototype.onSearchFieldKeyUp_ = function(e) {
|
||||
if (e.keyCode == goog.events.KeyCodes.ENTER) {
|
||||
this.onSearch_();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
315
core/src/main/javascript/google/registry/ui/js/edit_item.js
Normal file
315
core/src/main/javascript/google/registry/ui/js/edit_item.js
Normal file
|
@ -0,0 +1,315 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.EditItem');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.classlist');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.soy');
|
||||
goog.require('registry.Component');
|
||||
goog.require('registry.soy.console');
|
||||
goog.require('registry.util');
|
||||
|
||||
goog.forwardDeclare('registry.Console');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* An editable item, with Edit and Save/Cancel buttons in the appbar.
|
||||
* @param {!registry.Console} cons
|
||||
* @param {function()} itemTmpl
|
||||
* @param {boolean} isEditable
|
||||
* @constructor
|
||||
* @extends {registry.Component}
|
||||
*/
|
||||
registry.EditItem = function(cons, itemTmpl, isEditable) {
|
||||
registry.EditItem.base(this, 'constructor', cons);
|
||||
|
||||
/**
|
||||
* @type {!Function}
|
||||
*/
|
||||
this.itemTmpl = itemTmpl;
|
||||
|
||||
/**
|
||||
* Optional current target resource id.
|
||||
* @type {?string}
|
||||
*/
|
||||
this.id = null;
|
||||
|
||||
/**
|
||||
* Should the "edit" button be enabled?
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.isEditable = isEditable;
|
||||
|
||||
/**
|
||||
* Transitional id for next resource during create.
|
||||
* @type {?string}
|
||||
*/
|
||||
this.nextId = null;
|
||||
};
|
||||
goog.inherits(registry.EditItem, registry.Component);
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.EditItem.prototype.bindToDom = function(id) {
|
||||
registry.EditItem.base(this, 'bindToDom', id);
|
||||
this.id = id;
|
||||
this.nextId = null;
|
||||
this.setupAppbar();
|
||||
};
|
||||
|
||||
|
||||
/** Setup appbar save/edit buttons. */
|
||||
registry.EditItem.prototype.setupAppbar = function() {
|
||||
goog.soy.renderElement(goog.dom.getRequiredElement('reg-app-buttons'),
|
||||
registry.soy.console.appbarButtons);
|
||||
goog.events.listen(goog.dom.getRequiredElement('reg-app-btn-add'),
|
||||
goog.events.EventType.CLICK,
|
||||
goog.bind(this.add, this));
|
||||
goog.events.listen(goog.dom.getRequiredElement('reg-app-btn-edit'),
|
||||
goog.events.EventType.CLICK,
|
||||
goog.bind(this.edit, this));
|
||||
goog.events.listen(goog.dom.getRequiredElement('reg-app-btn-save'),
|
||||
goog.events.EventType.CLICK,
|
||||
goog.bind(this.save, this));
|
||||
goog.events.listen(goog.dom.getRequiredElement('reg-app-btn-cancel'),
|
||||
goog.events.EventType.CLICK,
|
||||
goog.bind(this.cancel, this));
|
||||
goog.events.listen(goog.dom.getRequiredElement('reg-app-btn-back'),
|
||||
goog.events.EventType.CLICK,
|
||||
goog.bind(this.back, this));
|
||||
// Show the add/edit buttons only if isEditable.
|
||||
// "edit" is shown if we have an item's ID
|
||||
// "add" is shown if we don't have an item's ID
|
||||
registry.util.setVisible('reg-app-btns-edit', this.isEditable && !!this.id);
|
||||
registry.util.setVisible('reg-app-btn-add', this.isEditable && !this.id);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve item from server. Overrides should callback to
|
||||
* `#handleFetchItem(string, !Object)`.
|
||||
* @param {string} id item id.
|
||||
*/
|
||||
registry.EditItem.prototype.fetchItem = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Handle result decoding and display.
|
||||
* @param {string} id The requested ID/name, for error message.
|
||||
* @param {!Object} rsp The requested object.
|
||||
*/
|
||||
registry.EditItem.prototype.handleFetchItem = goog.abstractMethod;
|
||||
|
||||
|
||||
/** Subclasses should override to continue processing after fetch. */
|
||||
registry.EditItem.prototype.processItem = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* Show the item.
|
||||
* @param {!Object} objArgs
|
||||
*/
|
||||
registry.EditItem.prototype.renderItem = function(objArgs) {
|
||||
goog.soy.renderElement(goog.dom.getRequiredElement('reg-content'),
|
||||
this.itemTmpl,
|
||||
objArgs);
|
||||
this.runAfterRender(objArgs);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} if the component is currently being edited.
|
||||
*/
|
||||
registry.EditItem.prototype.isEditing = function() {
|
||||
return goog.dom.classlist.contains(
|
||||
goog.dom.getElement('reg-app'), goog.getCssName('editing'));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Toggles the editing state of a component. This will first hide the
|
||||
* elements of the `shown` class, then adds the `editing`
|
||||
* style to the component, then shows the elements that had the `hidden`
|
||||
* class.
|
||||
*/
|
||||
registry.EditItem.prototype.toggleEdit = function() {
|
||||
// Toggle appbar buttons.
|
||||
var addBtn = goog.dom.getRequiredElement('reg-app-btn-add');
|
||||
var editBtns = goog.dom.getRequiredElement('reg-app-btns-edit');
|
||||
var saveBtns = goog.dom.getRequiredElement('reg-app-btns-save');
|
||||
var editing = goog.dom.classlist.contains(saveBtns,
|
||||
registry.util.cssShown);
|
||||
if (editing) {
|
||||
registry.util.setVisible(saveBtns, false);
|
||||
if (this.id) {
|
||||
registry.util.setVisible(editBtns, true);
|
||||
} else {
|
||||
registry.util.setVisible(addBtn, true);
|
||||
}
|
||||
} else {
|
||||
if (this.id) {
|
||||
registry.util.setVisible(editBtns, false);
|
||||
} else {
|
||||
registry.util.setVisible(addBtn, false);
|
||||
}
|
||||
registry.util.setVisible(saveBtns, true);
|
||||
}
|
||||
// Then page contents.
|
||||
var parentElt = goog.dom.getElement('reg-content');
|
||||
var shownCssName = goog.getCssName('shown');
|
||||
var hiddenCssName = goog.getCssName('hidden');
|
||||
var shown = goog.dom.getElementsByClass(shownCssName, parentElt);
|
||||
var hidden = goog.dom.getElementsByClass(hiddenCssName, parentElt);
|
||||
|
||||
for (var i = 0; i < shown.length; i++) {
|
||||
goog.dom.classlist.addRemove(shown[i], shownCssName, hiddenCssName);
|
||||
}
|
||||
|
||||
// Then add editing styles.
|
||||
var editingCssName = goog.getCssName('editing');
|
||||
var startingEdit = !goog.dom.classlist.contains(parentElt, editingCssName);
|
||||
if (startingEdit) {
|
||||
goog.dom.classlist.remove(parentElt, editingCssName);
|
||||
} else {
|
||||
goog.dom.classlist.add(parentElt, editingCssName);
|
||||
}
|
||||
|
||||
// The show hiddens.
|
||||
for (var i = 0; i < hidden.length; i++) {
|
||||
goog.dom.classlist.addRemove(hidden[i], hiddenCssName, shownCssName);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Subclasses should override to enhance the default model.
|
||||
* @return {!Object.<string, ?>}
|
||||
*/
|
||||
registry.EditItem.prototype.newModel = function() {
|
||||
return {item: {}};
|
||||
};
|
||||
|
||||
|
||||
// N.B. setting these as abstract precludes their correct binding in
|
||||
// setupAppbar.
|
||||
/** Show add item panel. */
|
||||
registry.EditItem.prototype.add = function() {};
|
||||
|
||||
|
||||
/** Go back from item to collection view. */
|
||||
registry.EditItem.prototype.back = function() {};
|
||||
|
||||
|
||||
/** Sets up initial edit model state and then called edit(objArgs). */
|
||||
registry.EditItem.prototype.edit = function() {
|
||||
var objArgs = this.model;
|
||||
if (objArgs == null) {
|
||||
objArgs = this.newModel();
|
||||
}
|
||||
// XXX: This is vestigial. In the new msg format, this pollutes the
|
||||
// server-model state. Should be carried elsewhere.
|
||||
objArgs.readonly = false;
|
||||
this.renderItem(objArgs);
|
||||
this.setupEditor(objArgs);
|
||||
this.toggleEdit();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Save the current item. Calls either create if creating a new
|
||||
* object or update otherwise.
|
||||
*/
|
||||
registry.EditItem.prototype.save = function() {
|
||||
if (this.model == null) {
|
||||
this.sendCreate();
|
||||
} else {
|
||||
this.sendUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Resets to non-editing, readonly state, or visits home screen if the
|
||||
* page was in on a create panel.
|
||||
*/
|
||||
registry.EditItem.prototype.cancel = function() {
|
||||
this.toggleEdit();
|
||||
// XXX: The presence of a model is sufficient for non-collection pages, but an
|
||||
// empty id also means go to the collection in collection pages. Should
|
||||
// be simplified.
|
||||
if (this.model && this.id != '') {
|
||||
this.model.readonly = true;
|
||||
this.renderItem(this.model);
|
||||
} else {
|
||||
this.bindToDom('');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Called after this.renderItem(), to allow for further setup of
|
||||
* editing.
|
||||
*
|
||||
* TODO(b/122661518): merge this with runAfterRender so we don't have two
|
||||
* similar methods
|
||||
* @param {!Object} objArgs
|
||||
*/
|
||||
registry.EditItem.prototype.setupEditor = function(objArgs) {};
|
||||
|
||||
/**
|
||||
* Called at the end of this.renderItem() to allow for registration
|
||||
* of listeners and other tasks that depend on the existence of the items in the
|
||||
* DOM
|
||||
* @param {!Object} objArgs
|
||||
*/
|
||||
registry.EditItem.prototype.runAfterRender = function(objArgs) {};
|
||||
|
||||
|
||||
// XXX: These should really take @param {object} which is the form. Alas the
|
||||
// only override which doesn't work this way, ResourceComponent.sendCreate
|
||||
// breaks this opportunity. Hmmm...
|
||||
/** Subclasses should extract form values and send them to the server. */
|
||||
registry.EditItem.prototype.sendCreate = goog.abstractMethod;
|
||||
|
||||
|
||||
/** Subclasses should extract form values and send them to the server. */
|
||||
registry.EditItem.prototype.sendUpdate = goog.abstractMethod;
|
||||
|
||||
|
||||
/** Subclasses should extract form values and send them to the server. */
|
||||
registry.EditItem.prototype.sendDelete = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Subclasses should override to populate update queryParams with form
|
||||
* fields as needed. `queryParams.nextId` MUST be set to the
|
||||
* new object's ID.
|
||||
* @param {!Object} queryParams
|
||||
*/
|
||||
registry.EditItem.prototype.prepareUpdate = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Subclasses should provide a function to parse JSON response from server and
|
||||
* return a result object as described below.
|
||||
* @param {!Object} rsp Decoded JSON response from the server.
|
||||
* @return {!Object} a result object describing next steps. On
|
||||
* success, if next is defined, visit(ret.next) is called, otherwise
|
||||
* if err is set, the butterbar message is set to it.
|
||||
*/
|
||||
registry.EditItem.prototype.handleUpdateResponse = goog.abstractMethod;
|
137
core/src/main/javascript/google/registry/ui/js/forms.js
Normal file
137
core/src/main/javascript/google/registry/ui/js/forms.js
Normal file
|
@ -0,0 +1,137 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.forms');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.dom.classlist');
|
||||
goog.require('goog.dom.forms');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.events.KeyCodes');
|
||||
goog.require('registry.util');
|
||||
|
||||
goog.forwardDeclare('goog.events.KeyEvent');
|
||||
|
||||
|
||||
/**
|
||||
* Sets the focus on a form field (if it exists).
|
||||
* @param {Element|string} field Form field (or ID) to focus.
|
||||
*/
|
||||
registry.forms.focus = function(field) {
|
||||
field = goog.dom.getElement(field);
|
||||
if (!goog.isNull(field) && goog.dom.isFocusable(field)) {
|
||||
goog.dom.forms.focusAndSelect(field);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Displays a form field error, or butters if no field is specified.
|
||||
* @param {string} message Human-readable explanation of why this field is evil.
|
||||
* @param {string=} opt_field Erroneous field name.
|
||||
*/
|
||||
registry.forms.displayError = function(message, opt_field) {
|
||||
if (!goog.isDef(opt_field)) {
|
||||
registry.util.butter(message);
|
||||
return;
|
||||
}
|
||||
var input = goog.dom.getElement(opt_field) ||
|
||||
goog.dom.getElement(opt_field + '[0]');
|
||||
// XXX: Transitioning to use of form.eltId instead of DOM id. If DOM id
|
||||
// lookup fails, then search forms for the named field.
|
||||
if (goog.isDefAndNotNull(opt_field) && goog.isNull(input)) {
|
||||
for (var fNdx in document.forms) {
|
||||
var form = document.forms[fNdx];
|
||||
if (form[opt_field]) {
|
||||
input = form[opt_field];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!goog.isNull(input)) {
|
||||
goog.dom.classlist.add(input, goog.getCssName('kd-formerror'));
|
||||
goog.dom.insertSiblingAfter(
|
||||
goog.dom.createDom(goog.dom.TagName.DIV,
|
||||
goog.getCssName('kd-errormessage'),
|
||||
message),
|
||||
input);
|
||||
registry.forms.focus(input);
|
||||
} else {
|
||||
registry.util.butter(opt_field + ': ' + message);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** Removes error markup from whois settings form. */
|
||||
registry.forms.resetErrors = function() {
|
||||
registry.util.unbutter();
|
||||
goog.array.forEach(
|
||||
goog.dom.getElementsByClass(goog.getCssName('kd-formerror')),
|
||||
function(field) {
|
||||
goog.dom.classlist.remove(field, goog.getCssName('kd-formerror'));
|
||||
});
|
||||
goog.array.forEach(
|
||||
goog.dom.getElementsByClass(goog.getCssName('kd-errormessage')),
|
||||
goog.dom.removeNode);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds enter key listeners to all form fields.
|
||||
* @param {!Element} container Parent element containing INPUT fields.
|
||||
* @param {function()} callback Called when enter is pressed in a field.
|
||||
*/
|
||||
registry.forms.listenFieldsOnEnter = function(container, callback) {
|
||||
var handler = goog.partial(registry.forms.onFieldKeyUp_, callback);
|
||||
goog.array.forEach(
|
||||
goog.dom.getElementsByTagNameAndClass(
|
||||
goog.dom.TagName.INPUT, undefined, container),
|
||||
function(field) {
|
||||
goog.events.listen(field, goog.events.EventType.KEYUP, handler);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Event handler that saves form when enter is pressed in a form field.
|
||||
* @param {function()} callback Called when key pressed is ENTER.
|
||||
* @param {!goog.events.KeyEvent} e Key event to handle.
|
||||
* @return {boolean} Whether the event should be continued or cancelled.
|
||||
* @private
|
||||
*/
|
||||
registry.forms.onFieldKeyUp_ = function(callback, e) {
|
||||
if (e.keyCode == goog.events.KeyCodes.ENTER) {
|
||||
callback();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Toggles disabled state of a button or form element.
|
||||
* @param {!Element} element Form element to disable.
|
||||
* @param {boolean} enabled Enables element if true, or else disables it.
|
||||
*/
|
||||
registry.forms.setEnabled = function(element, enabled) {
|
||||
if (enabled) {
|
||||
goog.dom.classlist.remove(element, goog.getCssName('disabled'));
|
||||
} else {
|
||||
goog.dom.classlist.add(element, goog.getCssName('disabled'));
|
||||
element.blur();
|
||||
}
|
||||
};
|
137
core/src/main/javascript/google/registry/ui/js/menu_button.js
Normal file
137
core/src/main/javascript/google/registry/ui/js/menu_button.js
Normal file
|
@ -0,0 +1,137 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.MenuButton');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.classlist');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.EventHandler');
|
||||
goog.require('goog.events.EventType');
|
||||
|
||||
goog.forwardDeclare('goog.events.BrowserEvent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Kennedy style menu button.
|
||||
* @param {!Element} button Menu button element.
|
||||
* @constructor
|
||||
* @extends {goog.events.EventHandler}
|
||||
* @final
|
||||
*/
|
||||
registry.MenuButton = function(button) {
|
||||
registry.MenuButton.base(this, 'constructor');
|
||||
|
||||
/**
|
||||
* Outer menu button element.
|
||||
* @private {!Element}
|
||||
* @const
|
||||
*/
|
||||
this.button_ = button;
|
||||
this.listen(button, goog.events.EventType.CLICK, this.onButtonClick_);
|
||||
|
||||
/**
|
||||
* Label that displays currently selected item.
|
||||
* @private {!Element}
|
||||
* @const
|
||||
*/
|
||||
this.label_ =
|
||||
goog.dom.getRequiredElementByClass(goog.getCssName('label'), button);
|
||||
|
||||
/**
|
||||
* List of selectable items.
|
||||
* @private {!Element}
|
||||
* @const
|
||||
*/
|
||||
this.menu_ =
|
||||
goog.dom.getRequiredElementByClass(goog.getCssName('kd-menulist'),
|
||||
button);
|
||||
|
||||
goog.array.forEach(
|
||||
goog.dom.getElementsByClass(goog.getCssName('kd-menulistitem'), button),
|
||||
function(item) {
|
||||
this.listen(item, goog.events.EventType.CLICK, this.onItemClick_);
|
||||
},
|
||||
this);
|
||||
};
|
||||
goog.inherits(registry.MenuButton, goog.events.EventHandler);
|
||||
|
||||
|
||||
/**
|
||||
* Returns selected value in menu.
|
||||
* @return {string}
|
||||
*/
|
||||
registry.MenuButton.prototype.getValue = function() {
|
||||
return goog.dom.getTextContent(
|
||||
goog.dom.getRequiredElementByClass(
|
||||
goog.getCssName('selected'),
|
||||
this.button_));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Main menu button handler.
|
||||
* @param {!goog.events.BrowserEvent} e
|
||||
* @private
|
||||
*/
|
||||
registry.MenuButton.prototype.onButtonClick_ = function(e) {
|
||||
if (goog.dom.classlist.contains(this.button_, goog.getCssName('selected'))) {
|
||||
return;
|
||||
}
|
||||
e.stopPropagation();
|
||||
goog.dom.classlist.add(this.button_, goog.getCssName('selected'));
|
||||
goog.dom.classlist.add(this.menu_, goog.getCssName('shown'));
|
||||
this.listenOnce(goog.dom.getDocument().body, goog.events.EventType.CLICK,
|
||||
this.hideMenu_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Menu item selection handler.
|
||||
* @param {!goog.events.BrowserEvent} e
|
||||
* @private
|
||||
*/
|
||||
registry.MenuButton.prototype.onItemClick_ = function(e) {
|
||||
e.stopPropagation();
|
||||
if (goog.dom.classlist.contains(this.button_, goog.getCssName('disabled'))) {
|
||||
return;
|
||||
}
|
||||
goog.array.forEach(
|
||||
goog.dom.getElementsByClass(goog.getCssName('kd-menulistitem'),
|
||||
this.button_),
|
||||
function(item) {
|
||||
goog.dom.classlist.remove(item, goog.getCssName('selected'));
|
||||
},
|
||||
this);
|
||||
goog.asserts.assert(e.target instanceof Element);
|
||||
goog.dom.classlist.add(e.target, goog.getCssName('selected'));
|
||||
var text = goog.dom.getTextContent(e.target);
|
||||
goog.dom.setTextContent(this.label_, text);
|
||||
goog.events.fireListeners(this.button_, goog.events.EventType.CHANGE,
|
||||
false, {newValue: text});
|
||||
this.hideMenu_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Hide the menu.
|
||||
* @private
|
||||
*/
|
||||
registry.MenuButton.prototype.hideMenu_ = function() {
|
||||
goog.dom.classlist.remove(this.menu_, goog.getCssName('shown'));
|
||||
goog.dom.classlist.remove(this.button_, goog.getCssName('selected'));
|
||||
};
|
|
@ -0,0 +1,152 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.registrar.AdminSettings');
|
||||
|
||||
goog.forwardDeclare('registry.registrar.Console');
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.classlist');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.json');
|
||||
goog.require('goog.net.XhrIo');
|
||||
goog.require('goog.soy');
|
||||
goog.require('registry.Resource');
|
||||
goog.require('registry.ResourceComponent');
|
||||
goog.require('registry.soy.registrar.admin');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Admin Settings page, such as allowed TLDs for this registrar.
|
||||
* @param {!registry.registrar.Console} console
|
||||
* @param {!registry.Resource} resource the RESTful resource for the registrar.
|
||||
* @constructor
|
||||
* @extends {registry.ResourceComponent}
|
||||
* @final
|
||||
*/
|
||||
registry.registrar.AdminSettings = function(console, resource) {
|
||||
registry.registrar.AdminSettings.base(
|
||||
this, 'constructor', console, resource,
|
||||
registry.soy.registrar.admin.settings, console.params.isAdmin, null);
|
||||
};
|
||||
goog.inherits(registry.registrar.AdminSettings, registry.ResourceComponent);
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.AdminSettings.prototype.bindToDom = function(id) {
|
||||
registry.registrar.AdminSettings.base(this, 'bindToDom', 'fake');
|
||||
goog.dom.removeNode(goog.dom.getRequiredElement('reg-app-btn-back'));
|
||||
};
|
||||
|
||||
/** @override */
|
||||
registry.registrar.AdminSettings.prototype.runAfterRender = function(objArgs) {
|
||||
const oteButton = goog.dom.getElement('btn-ote-status');
|
||||
if (oteButton) {
|
||||
goog.events.listen(
|
||||
oteButton,
|
||||
goog.events.EventType.CLICK,
|
||||
goog.bind(
|
||||
this.oteStatusCheck_, this, objArgs.xsrfToken, objArgs.clientId),
|
||||
false, this);
|
||||
}
|
||||
};
|
||||
|
||||
/** @override */
|
||||
registry.registrar.AdminSettings.prototype.setupEditor = function(objArgs) {
|
||||
goog.dom.classlist.add(
|
||||
goog.dom.getRequiredElement('tlds'), goog.getCssName('editing'));
|
||||
var tlds = goog.dom.getElementsByClass(
|
||||
goog.getCssName('tld'), goog.dom.getRequiredElement('tlds'));
|
||||
goog.array.forEach(tlds, function(tld) {
|
||||
var remBtn = goog.dom.getChildren(tld)[0];
|
||||
goog.events.listen(
|
||||
remBtn, goog.events.EventType.CLICK,
|
||||
goog.bind(this.onTldRemove_, this, remBtn));
|
||||
}, this);
|
||||
this.typeCounts['reg-tlds'] =
|
||||
objArgs.allowedTlds ? objArgs.allowedTlds.length : 0;
|
||||
|
||||
goog.events.listen(
|
||||
goog.dom.getRequiredElement('btn-add-tld'), goog.events.EventType.CLICK,
|
||||
this.onTldAdd_, false, this);
|
||||
};
|
||||
|
||||
/**
|
||||
* JSON response prefix which prevents evaluation.
|
||||
* @private {string}
|
||||
* @const
|
||||
*/
|
||||
registry.registrar.AdminSettings.PARSER_BREAKER_ = ')]}\'\n';
|
||||
|
||||
/**
|
||||
* Click handler for OT&E status checking button.
|
||||
* @param {string} xsrfToken
|
||||
* @param {string} clientId
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.AdminSettings.prototype.oteStatusCheck_ = function(
|
||||
xsrfToken, clientId) {
|
||||
goog.net.XhrIo.send('/registrar-ote-status', function(e) {
|
||||
var response =
|
||||
/** @type {!registry.json.ote.OteStatusResponse} */
|
||||
(e.target.getResponseJson(
|
||||
registry.registrar.AdminSettings.PARSER_BREAKER_));
|
||||
var oteResultParent = goog.dom.getRequiredElement('ote-status-area-parent');
|
||||
if (response.status === 'SUCCESS') {
|
||||
var results = response.results[0];
|
||||
goog.soy.renderElement(
|
||||
oteResultParent, registry.soy.registrar.admin.oteResultsTable,
|
||||
{completed: results.completed, detailsList: results.details});
|
||||
} else {
|
||||
goog.soy.renderElement(
|
||||
oteResultParent, registry.soy.registrar.admin.oteErrorArea,
|
||||
{message: response.message});
|
||||
}
|
||||
}, 'POST', goog.json.serialize({'clientId': clientId}), {
|
||||
'X-CSRF-Token': xsrfToken,
|
||||
'Content-Type': 'application/json; charset=UTF-8'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Click handler for TLD add button.
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.AdminSettings.prototype.onTldAdd_ = function() {
|
||||
const tldInputElt = goog.dom.getRequiredElement('newTld');
|
||||
const tldElt = goog.soy.renderAsFragment(registry.soy.registrar.admin.tld, {
|
||||
name: 'allowedTlds[' + this.typeCounts['reg-tlds'] + ']',
|
||||
tld: tldInputElt.value,
|
||||
});
|
||||
goog.dom.appendChild(goog.dom.getRequiredElement('tlds'), tldElt);
|
||||
var remBtn = goog.dom.getFirstElementChild(tldElt);
|
||||
goog.dom.classlist.remove(remBtn, goog.getCssName('hidden'));
|
||||
goog.events.listen(
|
||||
remBtn, goog.events.EventType.CLICK,
|
||||
goog.bind(this.onTldRemove_, this, remBtn));
|
||||
this.typeCounts['reg-tlds']++;
|
||||
tldInputElt.value = '';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Click handler for TLD remove button.
|
||||
* @param {!Element} remBtn The remove button.
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.AdminSettings.prototype.onTldRemove_ = function(remBtn) {
|
||||
goog.dom.removeNode(goog.dom.getParentElement(remBtn));
|
||||
};
|
|
@ -0,0 +1,170 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.registrar.Console');
|
||||
|
||||
goog.require('goog.Uri');
|
||||
goog.require('goog.dispose');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.classlist');
|
||||
goog.require('goog.net.XhrIo');
|
||||
goog.require('registry.Console');
|
||||
goog.require('registry.Resource');
|
||||
goog.require('registry.registrar.AdminSettings');
|
||||
goog.require('registry.registrar.ContactSettings');
|
||||
goog.require('registry.registrar.ContactUs');
|
||||
goog.require('registry.registrar.Dashboard');
|
||||
goog.require('registry.registrar.Resources');
|
||||
goog.require('registry.registrar.SecuritySettings');
|
||||
goog.require('registry.registrar.WhoisSettings');
|
||||
goog.require('registry.util');
|
||||
|
||||
goog.forwardDeclare('registry.Component');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The Registrar Console.
|
||||
* @param {!Object} params Parameters to be passed into templates. These are
|
||||
* a combination of configurable parameters (e.g. phone number) and
|
||||
* user/session/registrar specific parameters. See
|
||||
* registrar/Console.soy#.main for expected contents.
|
||||
* @constructor
|
||||
* @extends {registry.Console}
|
||||
* @final
|
||||
*/
|
||||
registry.registrar.Console = function(params) {
|
||||
registry.registrar.Console.base(this, 'constructor');
|
||||
|
||||
/**
|
||||
* @type {!Object}
|
||||
*/
|
||||
this.params = params;
|
||||
|
||||
/**
|
||||
* Component that's currently embedded in the page.
|
||||
* @type {?registry.Component}
|
||||
* @private
|
||||
*/
|
||||
this.component_ = null;
|
||||
|
||||
/**
|
||||
* Last active nav element.
|
||||
* @type {?Element}
|
||||
*/
|
||||
this.lastActiveNavElt;
|
||||
|
||||
/**
|
||||
* A map from the URL fragment to the component to show.
|
||||
*
|
||||
* @type {!Object.<string, function(new:registry.Component,
|
||||
* !registry.registrar.Console,
|
||||
* !registry.Resource)>}
|
||||
*/
|
||||
this.pageMap = {};
|
||||
// Homepage. Displayed when there's no fragment, or when the fragment doesn't
|
||||
// correspond to any view
|
||||
this.pageMap[''] = registry.registrar.Dashboard;
|
||||
// Updating the Registrar settings
|
||||
this.pageMap['security-settings'] = registry.registrar.SecuritySettings;
|
||||
this.pageMap['contact-settings'] = registry.registrar.ContactSettings;
|
||||
this.pageMap['whois-settings'] = registry.registrar.WhoisSettings;
|
||||
this.pageMap['contact-us'] = registry.registrar.ContactUs;
|
||||
this.pageMap['resources'] = registry.registrar.Resources;
|
||||
// For admin use. The relevant tab is only shown in Console.soy for admins,
|
||||
// but we also need to remove it here, otherwise it'd still be accessible if
|
||||
// the user manually puts '#admin-settings' in the URL.
|
||||
//
|
||||
// Both the Console.soy and here, the "hiding the admin console for non
|
||||
// admins" is purely for "aesthetic / design" reasons and have NO security
|
||||
// implications.
|
||||
//
|
||||
// The security implications are only in the backend where we make sure all
|
||||
// changes are made by users with the correct access (in other words - we
|
||||
// don't trust the client-side to secure our application anyway)
|
||||
if (this.params.isAdmin) {
|
||||
this.pageMap['admin-settings'] = registry.registrar.AdminSettings;
|
||||
}
|
||||
};
|
||||
goog.inherits(registry.registrar.Console, registry.Console);
|
||||
|
||||
|
||||
/**
|
||||
* Changes the content area depending on hash path.
|
||||
*
|
||||
* <p>Hash path is expected to be of the form:
|
||||
*
|
||||
* <pre>
|
||||
* #type/id
|
||||
* </pre>
|
||||
*
|
||||
* <p>The `id` part may be appended by `()` to specify that the target
|
||||
* should be a resource create page.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
registry.registrar.Console.prototype.handleHashChange = function() {
|
||||
var hashToken = this.history.getToken();
|
||||
|
||||
var parts = hashToken.split('/');
|
||||
var type = '';
|
||||
var id = '';
|
||||
if (parts.length >= 1) {
|
||||
type = parts[0];
|
||||
}
|
||||
if (parts.length == 2) {
|
||||
id = parts[1];
|
||||
}
|
||||
|
||||
goog.net.XhrIo.cleanup();
|
||||
|
||||
var componentCtor = this.pageMap[type];
|
||||
if (componentCtor == undefined) {
|
||||
componentCtor = this.pageMap[''];
|
||||
}
|
||||
var oldComponent = this.component_;
|
||||
const resource = new registry.Resource(
|
||||
new goog.Uri('/registrar-settings'), this.params.clientId,
|
||||
this.params.xsrfToken);
|
||||
this.component_ = new componentCtor(this, resource);
|
||||
this.registerDisposable(this.component_);
|
||||
this.component_.basePath = type;
|
||||
this.component_.bindToDom(id);
|
||||
|
||||
this.changeNavStyle();
|
||||
|
||||
goog.dispose(oldComponent);
|
||||
};
|
||||
|
||||
|
||||
/** Change nav style. */
|
||||
registry.registrar.Console.prototype.changeNavStyle = function() {
|
||||
var hashToken = this.history.getToken();
|
||||
// Path except id
|
||||
var slashNdx = hashToken.lastIndexOf('/');
|
||||
slashNdx = slashNdx == -1 ? hashToken.length : slashNdx;
|
||||
var regNavlist = goog.dom.getRequiredElement('reg-navlist');
|
||||
var path = hashToken.substring(0, slashNdx);
|
||||
var active = regNavlist.querySelector('a[href="#' + path + '"]');
|
||||
if (goog.isNull(active)) {
|
||||
registry.util.log('Unknown path or path form in changeNavStyle.');
|
||||
return;
|
||||
}
|
||||
if (this.lastActiveNavElt) {
|
||||
goog.dom.classlist.remove(
|
||||
this.lastActiveNavElt, goog.getCssName('domain-active-nav'));
|
||||
}
|
||||
goog.dom.classlist.add(active, goog.getCssName('domain-active-nav'));
|
||||
this.lastActiveNavElt = active;
|
||||
};
|
|
@ -0,0 +1,252 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.registrar.ContactSettings');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.json');
|
||||
goog.require('goog.soy');
|
||||
goog.require('registry.Resource');
|
||||
goog.require('registry.ResourceComponent');
|
||||
goog.require('registry.soy.registrar.contacts');
|
||||
goog.require('registry.util');
|
||||
|
||||
goog.forwardDeclare('registry.registrar.Console');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Contact Settings page. Registrar Contacts are not really a proper
|
||||
* REST resource as they're still passed from the server as a member
|
||||
* field of Registrar, so this class behaves like an item page,
|
||||
* updating only that field of the Registrar object and not
|
||||
* implementing the create action from edit_item.
|
||||
* @param {!registry.registrar.Console} console
|
||||
* @param {!registry.Resource} resource the RESTful resource for the registrar.
|
||||
* @constructor
|
||||
* @extends {registry.ResourceComponent}
|
||||
* @final
|
||||
*/
|
||||
registry.registrar.ContactSettings = function(console, resource) {
|
||||
registry.registrar.ContactSettings.base(
|
||||
this, 'constructor', console, resource,
|
||||
registry.soy.registrar.contacts.contact, console.params.isOwner, null);
|
||||
};
|
||||
goog.inherits(registry.registrar.ContactSettings, registry.ResourceComponent);
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.ContactSettings.prototype.setupAppbar = function() {
|
||||
registry.registrar.ContactSettings.base(this, 'setupAppbar');
|
||||
// Setup delete only on existing items, not on creates.
|
||||
if (goog.isDefAndNotNull(this.model)) {
|
||||
var deleteBtn = goog.dom.createDom(
|
||||
goog.dom.TagName.BUTTON, {
|
||||
type: 'button',
|
||||
id: 'reg-app-btn-delete',
|
||||
className: goog.getCssName('kd-button')
|
||||
},
|
||||
'Delete');
|
||||
goog.events.listen(deleteBtn, goog.events.EventType.CLICK,
|
||||
goog.bind(this.sendDelete, this));
|
||||
goog.dom.insertSiblingBefore(deleteBtn,
|
||||
goog.dom.getRequiredElement('reg-app-btn-cancel'));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.ContactSettings.prototype.renderItem = function(rspObj) {
|
||||
var contentElt = goog.dom.getRequiredElement('reg-content');
|
||||
/** @type {!registry.json.RegistrarContact} */
|
||||
var contacts = rspObj.contacts;
|
||||
if (this.id) {
|
||||
var targetContactNdx;
|
||||
var targetContact;
|
||||
for (var c in contacts) {
|
||||
var ct = contacts[c];
|
||||
if (ct.emailAddress == this.id) {
|
||||
targetContactNdx = c;
|
||||
targetContact = ct;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!targetContact) {
|
||||
registry.util.butter('No contact with the given email.');
|
||||
return;
|
||||
}
|
||||
var typesList = targetContact.types.toLowerCase().split(',');
|
||||
var actualTypesLookup = {};
|
||||
for (var t in typesList) {
|
||||
actualTypesLookup[typesList[t]] = true;
|
||||
}
|
||||
goog.soy.renderElement(
|
||||
contentElt,
|
||||
registry.soy.registrar.contacts.contact,
|
||||
{
|
||||
item: targetContact,
|
||||
namePrefix: 'contacts[' + targetContactNdx + '].',
|
||||
actualTypesLookup: actualTypesLookup,
|
||||
readonly: (rspObj.readonly || false)
|
||||
});
|
||||
this.setupAppbar();
|
||||
} else {
|
||||
var contactsByType = {};
|
||||
for (var c in contacts) {
|
||||
var contact = contacts[c];
|
||||
var types = contact.types;
|
||||
if (!types) {
|
||||
// If the contact has no types, synthesize an "OTHER" type so that it
|
||||
// still will be displayed in the console.
|
||||
types = 'OTHER';
|
||||
}
|
||||
types = types.split(',');
|
||||
for (var t in types) {
|
||||
var type = types[t].toLowerCase();
|
||||
var contactsList = contactsByType[type];
|
||||
if (!contactsList) {
|
||||
contactsByType[type] = contactsList = [];
|
||||
}
|
||||
contactsList.push(contact);
|
||||
}
|
||||
}
|
||||
goog.soy.renderElement(
|
||||
contentElt,
|
||||
registry.soy.registrar.contacts.set,
|
||||
{contactsByType: contactsByType });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.ContactSettings.prototype.add = function() {
|
||||
var newContactNdx = this.model.contacts.length;
|
||||
goog.soy.renderElement(goog.dom.getRequiredElement('reg-content'),
|
||||
registry.soy.registrar.contacts.contact,
|
||||
{
|
||||
item: {},
|
||||
namePrefix: 'contacts[' + newContactNdx + '].',
|
||||
actualTypesLookup: {},
|
||||
readonly: false
|
||||
});
|
||||
this.toggleEdit();
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.ContactSettings.prototype.sendDelete = function() {
|
||||
var ndxToDel = null;
|
||||
for (var i = 0; i < this.model.contacts.length; i++) {
|
||||
var contact = this.model.contacts[i];
|
||||
if (contact.emailAddress == this.id) {
|
||||
ndxToDel = i;
|
||||
}
|
||||
}
|
||||
if (goog.isNull(ndxToDel)) {
|
||||
throw new Error('Email to delete does not match model.');
|
||||
}
|
||||
var modelCopy = /** @type {!Object}
|
||||
*/ (JSON.parse(goog.json.serialize(this.model)));
|
||||
goog.array.removeAt(modelCopy.contacts, ndxToDel);
|
||||
this.resource.update(modelCopy, goog.bind(this.handleDeleteResponse, this));
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.ContactSettings.prototype.prepareUpdate =
|
||||
function(modelCopy) {
|
||||
var form = registry.util.parseForm('item');
|
||||
var contact;
|
||||
// Handle update/create.
|
||||
if (this.id) {
|
||||
// Update contact, so overwrite it in the model before sending
|
||||
// back to server.
|
||||
var once = false;
|
||||
for (var c in form.contacts) {
|
||||
if (once) {
|
||||
throw new Error('More than one contact parsed from form: ' + c);
|
||||
}
|
||||
contact = form.contacts[c];
|
||||
modelCopy.contacts[c] = contact;
|
||||
once = true;
|
||||
}
|
||||
} else {
|
||||
// Add contact.
|
||||
contact = form.contacts.pop();
|
||||
modelCopy.contacts.push(contact);
|
||||
}
|
||||
contact.visibleInWhoisAsAdmin = contact.visibleInWhoisAsAdmin == 'true';
|
||||
contact.visibleInWhoisAsTech = contact.visibleInWhoisAsTech == 'true';
|
||||
contact.visibleInDomainWhoisAsAbuse =
|
||||
contact.visibleInDomainWhoisAsAbuse == 'true';
|
||||
contact.types = '';
|
||||
for (var tNdx in contact.type) {
|
||||
if (contact.type[tNdx]) {
|
||||
if (contact.types.length > 0) {
|
||||
contact.types += ',';
|
||||
}
|
||||
contact.types += ('' + tNdx).toUpperCase();
|
||||
}
|
||||
}
|
||||
delete contact['type'];
|
||||
// Override previous domain WHOIS abuse contact.
|
||||
if (contact.visibleInDomainWhoisAsAbuse) {
|
||||
for (var c in modelCopy.contacts) {
|
||||
if (modelCopy.contacts[c].emailAddress != contact.emailAddress) {
|
||||
modelCopy.contacts[c].visibleInDomainWhoisAsAbuse = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.nextId = contact.emailAddress;
|
||||
};
|
||||
|
||||
|
||||
// XXX: Should be hoisted up.
|
||||
/**
|
||||
* Handler for contact save that navigates to that item on success.
|
||||
* Does nothing on failure as UI will be left with error messages for
|
||||
* the user to resolve.
|
||||
* @param {!Object} rsp Decoded JSON response from the server.
|
||||
* @override
|
||||
*/
|
||||
registry.registrar.ContactSettings.prototype.handleCreateResponse =
|
||||
function(rsp) {
|
||||
this.handleUpdateResponse(rsp);
|
||||
if (rsp.status == 'SUCCESS') {
|
||||
this.console.view('contact-settings/' + this.nextId);
|
||||
}
|
||||
return rsp;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler for contact delete that navigates back to the collection on success.
|
||||
* Does nothing on failure as UI will be left with error messages for
|
||||
* the user to resolve.
|
||||
* @param {!Object} rsp Decoded JSON response from the server.
|
||||
* @override
|
||||
*/
|
||||
registry.registrar.ContactSettings.prototype.handleDeleteResponse =
|
||||
function(rsp) {
|
||||
this.handleUpdateResponse(rsp);
|
||||
if (rsp.status == 'SUCCESS') {
|
||||
this.id = null;
|
||||
this.console.view('contact-settings');
|
||||
}
|
||||
return rsp;
|
||||
};
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.registrar.ContactUs');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('registry.Resource');
|
||||
goog.require('registry.ResourceComponent');
|
||||
goog.require('registry.soy.registrar.console');
|
||||
|
||||
goog.forwardDeclare('registry.registrar.Console');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Contact Us page.
|
||||
* @param {!registry.registrar.Console} console
|
||||
* @param {!registry.Resource} resource the RESTful resource for the registrar.
|
||||
* @constructor
|
||||
* @extends {registry.ResourceComponent}
|
||||
* @final
|
||||
*/
|
||||
registry.registrar.ContactUs = function(console, resource) {
|
||||
registry.registrar.ContactUs.base(
|
||||
this, 'constructor', console, resource,
|
||||
registry.soy.registrar.console.contactUs, console.params.isOwner, null);
|
||||
};
|
||||
goog.inherits(registry.registrar.ContactUs, registry.ResourceComponent);
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.ContactUs.prototype.bindToDom = function(id) {
|
||||
registry.registrar.ContactUs.base(this, 'bindToDom', '');
|
||||
goog.dom.removeChildren(goog.dom.getRequiredElement('reg-app-buttons'));
|
||||
};
|
|
@ -0,0 +1,89 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.registrar.Dashboard');
|
||||
|
||||
goog.require('goog.Timer');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.soy');
|
||||
goog.require('goog.style');
|
||||
goog.require('registry.Component');
|
||||
goog.require('registry.soy.registrar.console');
|
||||
|
||||
goog.forwardDeclare('registry.registrar.Console');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Dashboard for Registrar Console.
|
||||
* @param {!registry.registrar.Console} console
|
||||
* @constructor
|
||||
* @extends {registry.Component}
|
||||
* @final
|
||||
*/
|
||||
registry.registrar.Dashboard = function(console) {
|
||||
registry.registrar.Dashboard.base(this, 'constructor', console);
|
||||
|
||||
/** @private {number} */
|
||||
this.x_ = 0;
|
||||
|
||||
/** @private {?Element} */
|
||||
this.gear_ = null;
|
||||
|
||||
/** @private {?goog.Timer} */
|
||||
this.timer_ = null;
|
||||
};
|
||||
goog.inherits(registry.registrar.Dashboard, registry.Component);
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.Dashboard.prototype.bindToDom = function(id) {
|
||||
registry.registrar.Dashboard.base(this, 'bindToDom', '');
|
||||
goog.dom.removeChildren(goog.dom.getRequiredElement('reg-app-buttons'));
|
||||
goog.soy.renderElement(goog.dom.getElement('reg-content'),
|
||||
registry.soy.registrar.console.dashboard,
|
||||
this.console.params);
|
||||
goog.events.listen(goog.dom.getElement('rotate'),
|
||||
goog.events.EventType.CLICK,
|
||||
goog.bind(this.rotate_, this));
|
||||
this.gear_ = goog.dom.getRequiredElement('gear');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Let's do the twist.
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.Dashboard.prototype.rotate_ = function() {
|
||||
this.timer_ = new goog.Timer(10);
|
||||
this.registerDisposable(this.timer_);
|
||||
goog.events.listen(this.timer_, goog.Timer.TICK,
|
||||
goog.bind(this.rotateCall_, this));
|
||||
this.timer_.start();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* No really this time!
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.Dashboard.prototype.rotateCall_ = function() {
|
||||
this.x_++;
|
||||
goog.style.setStyle(this.gear_, 'transform', 'rotate(' + this.x_ + 'deg)');
|
||||
if (this.x_ == 360) {
|
||||
this.timer_.stop();
|
||||
}
|
||||
};
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Entry point for the registrar console.
|
||||
*/
|
||||
|
||||
goog.provide('registry.registrar.main');
|
||||
|
||||
goog.require('registry.registrar.Console');
|
||||
|
||||
|
||||
/**
|
||||
* Instantiates a registry object, which syncs with the server.
|
||||
* Sub-templates are invoked based on location/pathname, to choose
|
||||
* either Registry or Registrar pages.
|
||||
*
|
||||
* @param {string} xsrfToken populated by server-side soy template.
|
||||
* @param {string} clientId The registrar clientId.
|
||||
* @param {boolean} isAdmin
|
||||
* @param {boolean} isOwner
|
||||
* @param {string} productName the product name displayed by the UI.
|
||||
* @param {string} integrationEmail
|
||||
* @param {string} supportEmail
|
||||
* @param {string} announcementsEmail
|
||||
* @param {string} supportPhoneNumber
|
||||
* @param {string} technicalDocsUrl
|
||||
* @param {string} environment
|
||||
* @export
|
||||
*/
|
||||
registry.registrar.main = function(
|
||||
xsrfToken, clientId, isAdmin, isOwner, productName, integrationEmail,
|
||||
supportEmail, announcementsEmail, supportPhoneNumber, technicalDocsUrl,
|
||||
environment) {
|
||||
const console = new registry.registrar.Console({
|
||||
xsrfToken: xsrfToken,
|
||||
clientId: clientId,
|
||||
isAdmin: isAdmin,
|
||||
isOwner: isOwner,
|
||||
productName: productName,
|
||||
integrationEmail: integrationEmail,
|
||||
supportEmail: supportEmail,
|
||||
announcementsEmail: announcementsEmail,
|
||||
supportPhoneNumber: supportPhoneNumber,
|
||||
technicalDocsUrl: technicalDocsUrl,
|
||||
environment: environment,
|
||||
});
|
||||
|
||||
console.setUp();
|
||||
};
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.registrar.Resources');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('registry.Resource');
|
||||
goog.require('registry.ResourceComponent');
|
||||
goog.require('registry.soy.registrar.console');
|
||||
|
||||
goog.forwardDeclare('registry.registrar.Console');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Resources and billing page.
|
||||
* @param {!registry.registrar.Console} console
|
||||
* @param {!registry.Resource} resource the RESTful resource for the registrar.
|
||||
* @constructor
|
||||
* @extends {registry.ResourceComponent}
|
||||
* @final
|
||||
*/
|
||||
registry.registrar.Resources = function(console, resource) {
|
||||
registry.registrar.Resources.base(
|
||||
this, 'constructor', console, resource,
|
||||
registry.soy.registrar.console.resources, console.params.isOwner, null);
|
||||
};
|
||||
goog.inherits(registry.registrar.Resources,
|
||||
registry.ResourceComponent);
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.Resources.prototype.bindToDom = function(id) {
|
||||
registry.registrar.Resources.base(this, 'bindToDom', 'fake');
|
||||
goog.dom.removeNode(goog.dom.getRequiredElement('reg-app-btn-back'));
|
||||
};
|
|
@ -0,0 +1,106 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.registrar.SecuritySettings');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.classlist');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.soy');
|
||||
goog.require('registry.Resource');
|
||||
goog.require('registry.ResourceComponent');
|
||||
goog.require('registry.soy.registrar.security');
|
||||
|
||||
goog.forwardDeclare('registry.registrar.Console');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Security Settings page.
|
||||
* @param {!registry.registrar.Console} console
|
||||
* @param {!registry.Resource} resource the RESTful resource for the registrar.
|
||||
* @constructor
|
||||
* @extends {registry.ResourceComponent}
|
||||
* @final
|
||||
*/
|
||||
registry.registrar.SecuritySettings = function(console, resource) {
|
||||
registry.registrar.SecuritySettings.base(
|
||||
this, 'constructor', console, resource,
|
||||
registry.soy.registrar.security.settings, console.params.isOwner, null);
|
||||
};
|
||||
goog.inherits(registry.registrar.SecuritySettings, registry.ResourceComponent);
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.SecuritySettings.prototype.bindToDom = function(id) {
|
||||
registry.registrar.SecuritySettings.base(this, 'bindToDom', 'fake');
|
||||
goog.dom.removeNode(goog.dom.getRequiredElement('reg-app-btn-back'));
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.SecuritySettings.prototype.setupEditor =
|
||||
function(objArgs) {
|
||||
goog.dom.classlist.add(goog.dom.getRequiredElement('ips'),
|
||||
goog.getCssName('editing'));
|
||||
var ips = goog.dom.getElementsByClass(goog.getCssName('ip'),
|
||||
goog.dom.getRequiredElement('ips'));
|
||||
goog.array.forEach(ips, function(ip) {
|
||||
var remBtn = goog.dom.getChildren(ip)[0];
|
||||
goog.events.listen(remBtn,
|
||||
goog.events.EventType.CLICK,
|
||||
goog.bind(this.onIpRemove_, this, remBtn));
|
||||
}, this);
|
||||
this.typeCounts['reg-ips'] = objArgs.ipAddressWhitelist ?
|
||||
objArgs.ipAddressWhitelist.length : 0;
|
||||
|
||||
goog.events.listen(goog.dom.getRequiredElement('btn-add-ip'),
|
||||
goog.events.EventType.CLICK,
|
||||
this.onIpAdd_,
|
||||
false,
|
||||
this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Click handler for IP add button.
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.SecuritySettings.prototype.onIpAdd_ = function() {
|
||||
var ipInputElt = goog.dom.getRequiredElement('newIp');
|
||||
var ipElt = goog.soy.renderAsFragment(registry.soy.registrar.security.ip, {
|
||||
name: 'ipAddressWhitelist[' + this.typeCounts['reg-ips'] + ']',
|
||||
ip: ipInputElt.value
|
||||
});
|
||||
goog.dom.appendChild(goog.dom.getRequiredElement('ips'), ipElt);
|
||||
var remBtn = goog.dom.getFirstElementChild(ipElt);
|
||||
goog.dom.classlist.remove(remBtn, goog.getCssName('hidden'));
|
||||
goog.events.listen(remBtn, goog.events.EventType.CLICK,
|
||||
goog.bind(this.onIpRemove_, this, remBtn));
|
||||
this.typeCounts['reg-ips']++;
|
||||
ipInputElt.value = '';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Click handler for IP remove button.
|
||||
* @param {!Element} remBtn The remove button.
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.SecuritySettings.prototype.onIpRemove_ =
|
||||
function(remBtn) {
|
||||
goog.dom.removeNode(goog.dom.getParentElement(remBtn));
|
||||
};
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.registrar.WhoisSettings');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('registry.Resource');
|
||||
goog.require('registry.ResourceComponent');
|
||||
goog.require('registry.soy.registrar.whois');
|
||||
|
||||
goog.forwardDeclare('registry.registrar.Console');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* WHOIS Settings page.
|
||||
* @param {!registry.registrar.Console} console
|
||||
* @param {!registry.Resource} resource the RESTful resource for the registrar.
|
||||
* @constructor
|
||||
* @extends {registry.ResourceComponent}
|
||||
* @final
|
||||
*/
|
||||
registry.registrar.WhoisSettings = function(console, resource) {
|
||||
registry.registrar.WhoisSettings.base(
|
||||
this, 'constructor', console, resource,
|
||||
registry.soy.registrar.whois.settings, console.params.isOwner, null);
|
||||
};
|
||||
goog.inherits(registry.registrar.WhoisSettings, registry.ResourceComponent);
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.WhoisSettings.prototype.bindToDom = function(id) {
|
||||
registry.registrar.WhoisSettings.base(this, 'bindToDom', 'fake');
|
||||
goog.dom.removeNode(goog.dom.getRequiredElement('reg-app-btn-back'));
|
||||
};
|
80
core/src/main/javascript/google/registry/ui/js/resource.js
Normal file
80
core/src/main/javascript/google/registry/ui/js/resource.js
Normal file
|
@ -0,0 +1,80 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.Resource');
|
||||
|
||||
goog.require('goog.json');
|
||||
goog.require('registry.Session');
|
||||
|
||||
goog.forwardDeclare('goog.Uri');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Provide a CRUD view of a server resource.
|
||||
*
|
||||
* @param {!goog.Uri} baseUri Target RESTful resource.
|
||||
* @param {string} id the ID of the target resource
|
||||
* @param {string} xsrfToken Security token to pass back to the server.
|
||||
* @extends {registry.Session}
|
||||
* @constructor
|
||||
*/
|
||||
registry.Resource = function(baseUri, id, xsrfToken) {
|
||||
registry.Resource.base(this, 'constructor', baseUri, xsrfToken);
|
||||
/** @const @private {string} the ID of the target resource. */
|
||||
this.id_ = id;
|
||||
};
|
||||
goog.inherits(registry.Resource, registry.Session);
|
||||
|
||||
|
||||
/**
|
||||
* Get the resource from the server.
|
||||
*
|
||||
* @param {!Object} args Params for server.
|
||||
* @param {!Function} callback for retrieved resource.
|
||||
*/
|
||||
registry.Resource.prototype.read = function(args, callback) {
|
||||
this.send_('read', args, callback);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Update the resource on the server.
|
||||
*
|
||||
* @param {!Object} args params for server.
|
||||
* @param {!Function} callback on success.
|
||||
* @throws {!Exception} if the 'op' field is set on args.
|
||||
*/
|
||||
registry.Resource.prototype.update = function(args, callback) {
|
||||
this.send_('update', args, callback);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* RESTful access to resources on the server.
|
||||
*
|
||||
* @param {string} opCode One of (create|read|update)
|
||||
* @param {!Object} argsObj arguments for the operation.
|
||||
* @param {!Function} callback For XhrIo result throws.
|
||||
* @private
|
||||
*/
|
||||
registry.Resource.prototype.send_ =
|
||||
function(opCode, argsObj, callback) {
|
||||
// NB: must be declared this way in order to avoid compiler renaming
|
||||
var req = {};
|
||||
req['op'] = opCode;
|
||||
req['args'] = argsObj;
|
||||
req['id'] = this.id_;
|
||||
this.sendXhrIo(goog.json.serialize(req), callback);
|
||||
};
|
|
@ -0,0 +1,191 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.ResourceComponent');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.json');
|
||||
goog.require('goog.object');
|
||||
goog.require('registry.EditItem');
|
||||
goog.require('registry.forms');
|
||||
goog.require('registry.util');
|
||||
|
||||
goog.forwardDeclare('registry.Console');
|
||||
goog.forwardDeclare('registry.Resource');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The ResourceComponent class respresents server state for a named
|
||||
* resource and binds UI CRUD operations on it, or its constituent
|
||||
* collection.
|
||||
* @param {!registry.Console} console console singleton.
|
||||
* @param {!registry.Resource} resource the RESTful resource.
|
||||
* @param {!Function} itemTmpl
|
||||
* @param {boolean} isEditable if true, the "edit" button will be enabled
|
||||
* @param {Function} renderSetCb may be null if this resource is only
|
||||
* ever an item.
|
||||
* @constructor
|
||||
* @extends {registry.EditItem}
|
||||
*/
|
||||
registry.ResourceComponent = function(
|
||||
console,
|
||||
resource,
|
||||
itemTmpl,
|
||||
isEditable,
|
||||
renderSetCb) {
|
||||
registry.ResourceComponent.base(
|
||||
this, 'constructor', console, itemTmpl, isEditable);
|
||||
|
||||
/** @type {!registry.Resource} */
|
||||
this.resource = resource;
|
||||
|
||||
/** @type {Function} */
|
||||
this.renderSetCb = renderSetCb;
|
||||
};
|
||||
goog.inherits(registry.ResourceComponent, registry.EditItem);
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.ResourceComponent.prototype.renderItem = function(rspObj) {
|
||||
// Augment the console parameters with the response object, we'll need both.
|
||||
var params = goog.object.clone(this.console.params);
|
||||
goog.object.extend(params, rspObj);
|
||||
registry.ResourceComponent.base(this, 'renderItem', params);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.ResourceComponent.prototype.bindToDom = function(id) {
|
||||
registry.ResourceComponent.base(this, 'bindToDom', id);
|
||||
this.fetchItem(id);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.ResourceComponent.prototype.back = function() {
|
||||
this.console.view(this.basePath);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.ResourceComponent.prototype.fetchItem = function(id) {
|
||||
this.resource.read({}, goog.bind(this.handleFetchItem, this, id));
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.ResourceComponent.prototype.handleFetchItem = function(id, rsp) {
|
||||
// XXX: Two different protocols are supported here. The new style used in the
|
||||
// registrar console is first, followed by the item/set style used by
|
||||
// the admin console.
|
||||
if ('status' in rsp) {
|
||||
// New style.
|
||||
if (rsp.status == 'SUCCESS') {
|
||||
this.model = rsp.results[0];
|
||||
this.model.readonly = true;
|
||||
this.processItem();
|
||||
this.renderItem(this.model);
|
||||
} else {
|
||||
// XXX: Happens if the server restarts while the client has an
|
||||
// open-session. This should retry.
|
||||
registry.forms.resetErrors();
|
||||
registry.forms.displayError(rsp['message'], rsp['field']);
|
||||
}
|
||||
} else if ('item' in rsp) {
|
||||
this.model = rsp.item;
|
||||
this.model.readonly = true;
|
||||
this.processItem();
|
||||
this.renderItem(this.model);
|
||||
} else if ('set' in rsp && this.renderSetCb != null) {
|
||||
// XXX: This conditional logic should be hoisted to edit_item when
|
||||
// collection support is improved.
|
||||
goog.dom.removeChildren(goog.dom.getRequiredElement('reg-app-buttons'));
|
||||
this.renderSetCb(goog.dom.getRequiredElement('reg-content'), rsp);
|
||||
} else {
|
||||
registry.util.log('unknown message type in handleFetchItem');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.ResourceComponent.prototype.sendUpdate = function() {
|
||||
var modelCopy = /** @type {!Object}
|
||||
*/ (JSON.parse(goog.json.serialize(this.model)));
|
||||
this.prepareUpdate(modelCopy);
|
||||
if (this.id) {
|
||||
this.resource.update(modelCopy, goog.bind(this.handleUpdateResponse, this));
|
||||
} else {
|
||||
this.resource.update(modelCopy, goog.bind(this.handleCreateResponse, this));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.ResourceComponent.prototype.prepareUpdate = function(modelCopy) {
|
||||
var form = registry.util.parseForm('item');
|
||||
for (var ndx in form) {
|
||||
modelCopy[ndx] = form[ndx];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.ResourceComponent.prototype.handleUpdateResponse = function(rsp) {
|
||||
if (rsp.status) {
|
||||
if (rsp.status != 'SUCCESS') {
|
||||
registry.forms.resetErrors();
|
||||
registry.forms.displayError(rsp['message'], rsp['field']);
|
||||
return rsp;
|
||||
}
|
||||
// XXX: Vestigial state from admin console. Shouldn't be possible to be
|
||||
// null.
|
||||
if (this.id) {
|
||||
this.fetchItem(this.id || '');
|
||||
this.toggleEdit();
|
||||
}
|
||||
return rsp;
|
||||
}
|
||||
// XXX: Should be removed when admin console uses new response format.
|
||||
if (rsp instanceof Object && 'results' in rsp) {
|
||||
registry.util.butter(rsp['results']);
|
||||
this.bindToDom(this.nextId || '');
|
||||
this.nextId = null;
|
||||
} else {
|
||||
registry.util.butter(rsp.toString());
|
||||
}
|
||||
return rsp;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handle resource create response.
|
||||
* @param {!Object} rsp Decoded JSON response from the server.
|
||||
* @return {!Object} a result object describing next steps. On
|
||||
* success, if next is defined, visit(ret.next) is called, otherwise
|
||||
* if err is set, the butterbar message is set to it.
|
||||
*/
|
||||
registry.ResourceComponent.prototype.handleCreateResponse =
|
||||
goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Handle resource delete response.
|
||||
* @param {!Object} rsp Decoded JSON response from the server.
|
||||
* @return {!Object} a result object describing next steps. On
|
||||
* success, if next is defined, visit(ret.next) is called, otherwise
|
||||
* if err is set, the butterbar message is set to it.
|
||||
*/
|
||||
registry.ResourceComponent.prototype.handleDeleteResponse =
|
||||
goog.abstractMethod;
|
107
core/src/main/javascript/google/registry/ui/js/session.js
Normal file
107
core/src/main/javascript/google/registry/ui/js/session.js
Normal file
|
@ -0,0 +1,107 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.Session');
|
||||
|
||||
goog.require('goog.json');
|
||||
goog.require('goog.net.XhrIo');
|
||||
goog.require('registry.util');
|
||||
|
||||
goog.forwardDeclare('goog.Uri');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* XHR launcher for JSON requests.
|
||||
* @param {!goog.Uri} defaultUri URI to which requests are POSTed.
|
||||
* @param {string} xsrfToken Cross-site request forgery protection token.
|
||||
* @constructor
|
||||
* @template REQUEST, RESPONSE
|
||||
*/
|
||||
registry.Session = function(defaultUri, xsrfToken) {
|
||||
|
||||
/**
|
||||
* URI to which requests are posted.
|
||||
* @protected {!goog.Uri}
|
||||
* @const
|
||||
*/
|
||||
this.uri = defaultUri;
|
||||
|
||||
/**
|
||||
* XHR request headers.
|
||||
* @private {!Object<string, string>}
|
||||
* @const
|
||||
*/
|
||||
this.headers_ = {
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
'X-CSRF-Token': xsrfToken,
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Abstract method to send a request to the server.
|
||||
* @param {REQUEST} body HTTP request body as a string or JSON object.
|
||||
* @param {function(RESPONSE)} onSuccess XHR success callback.
|
||||
* @param {function(string)=} opt_onError XHR error callback. The default action
|
||||
* is to show a bloody butterbar.
|
||||
* @final
|
||||
*/
|
||||
registry.Session.prototype.sendXhrIo =
|
||||
function(body, onSuccess, opt_onError) {
|
||||
goog.net.XhrIo.send(
|
||||
this.uri.toString(),
|
||||
goog.bind(this.onXhrComplete_, this, onSuccess,
|
||||
opt_onError || goog.bind(this.displayError_, this)),
|
||||
'POST',
|
||||
goog.isObject(body) ? goog.json.serialize(body) : body,
|
||||
this.headers_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler invoked when an asynchronous request is complete.
|
||||
* @param {function(RESPONSE)} onSuccess Success callback.
|
||||
* @param {function(string)} onError Success callback.
|
||||
* @param {{target: !goog.net.XhrIo}} e XHR event.
|
||||
* @private
|
||||
*/
|
||||
registry.Session.prototype.onXhrComplete_ = function(onSuccess, onError, e) {
|
||||
if (e.target.isSuccess()) {
|
||||
onSuccess(/** @type {!RESPONSE} */ (
|
||||
e.target.getResponseJson(registry.Session.PARSER_BREAKER_)));
|
||||
} else {
|
||||
onError(e.target.getLastError());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* JSON response prefix which prevents evaluation.
|
||||
* @private {string}
|
||||
* @const
|
||||
*/
|
||||
registry.Session.PARSER_BREAKER_ = ')]}\'\n';
|
||||
|
||||
|
||||
/**
|
||||
* Displays `message` to user in bloody butterbar.
|
||||
* @param {string} message
|
||||
* @private
|
||||
*/
|
||||
registry.Session.prototype.displayError_ = function(message) {
|
||||
registry.util.butter(
|
||||
this.uri.toString() + ': ' + message + '. Please reload.', true);
|
||||
};
|
216
core/src/main/javascript/google/registry/ui/js/util.js
Normal file
216
core/src/main/javascript/google/registry/ui/js/util.js
Normal file
|
@ -0,0 +1,216 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.util');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.classlist');
|
||||
goog.require('goog.soy');
|
||||
|
||||
|
||||
/**
|
||||
* Logging function that delegates to `console.log()`.
|
||||
* @param {...*} var_args
|
||||
*/
|
||||
registry.util.log = function(var_args) {
|
||||
if (goog.DEBUG) {
|
||||
if (goog.isDef(goog.global.console) &&
|
||||
goog.isDef(goog.global.console['log'])) {
|
||||
goog.global.console.log.apply(goog.global.console, arguments);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* CSS class for hiding an element whose visibility can be toggled.
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
registry.util.cssHidden = goog.getCssName('hidden');
|
||||
|
||||
|
||||
/**
|
||||
* CSS class for showing an element whose visibility can be toggled.
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
registry.util.cssShown = goog.getCssName('shown');
|
||||
|
||||
|
||||
/**
|
||||
* Changes element visibility by toggling CSS `shown` to `hidden`.
|
||||
* @param {!Element|string} element Element or id attribute of element.
|
||||
* @param {boolean} visible Shows `element` if true, or else hides it.
|
||||
*/
|
||||
registry.util.setVisible = function(element, visible) {
|
||||
goog.dom.classlist.addRemove(
|
||||
goog.dom.getElement(element),
|
||||
visible ? registry.util.cssHidden : registry.util.cssShown,
|
||||
visible ? registry.util.cssShown : registry.util.cssHidden);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Show a buttebar with the given message. A dismiss link will be added.
|
||||
* @param {string} message the message to show the user.
|
||||
* @param {boolean=} opt_isFatal indicates butterbar should be blood red. This
|
||||
* should only be used when an RPC returns a non-200 status.
|
||||
*/
|
||||
registry.util.butter = function(message, opt_isFatal) {
|
||||
goog.dom.setTextContent(
|
||||
goog.dom.getElementByClass(goog.getCssName('kd-butterbar-text')),
|
||||
message);
|
||||
var butterbar =
|
||||
goog.dom.getRequiredElementByClass(goog.getCssName('kd-butterbar'));
|
||||
registry.util.setVisible(butterbar, true);
|
||||
if (opt_isFatal) {
|
||||
goog.dom.classlist.add(butterbar, goog.getCssName('fatal'));
|
||||
} else {
|
||||
goog.dom.classlist.remove(butterbar, goog.getCssName('fatal'));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Hides the butterbar.
|
||||
*/
|
||||
registry.util.unbutter = function() {
|
||||
registry.util.setVisible(
|
||||
goog.dom.getRequiredElementByClass(goog.getCssName('kd-butterbar')),
|
||||
false);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Renders the tmpl at and then moves it before the given elt.
|
||||
* @param {Element|string} id dom id of the refElt to render before.
|
||||
* @param {function()} tmpl template to render.
|
||||
* @param {!Object} tmplParams params to pass to the template.
|
||||
* @return {!Element} the rendered row.
|
||||
*/
|
||||
registry.util.renderBeforeRow = function(id, tmpl, tmplParams) {
|
||||
var refElt = goog.dom.getElement(id);
|
||||
goog.soy.renderElement(refElt, tmpl, tmplParams);
|
||||
var prevSib = goog.dom.getPreviousElementSibling(refElt);
|
||||
goog.dom.removeNode(refElt);
|
||||
refElt.removeAttribute('id');
|
||||
goog.dom.insertSiblingAfter(refElt, prevSib);
|
||||
var newAnchorRefElt = goog.dom.createDom(refElt.tagName, {'id': id});
|
||||
goog.dom.insertSiblingAfter(newAnchorRefElt, refElt);
|
||||
return refElt;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Turns an HTML form's named elements into a JSON data structure with
|
||||
* Pablo's black magick.
|
||||
* @param {string} formName the name of the form to be parsed.
|
||||
* @throws {Error} if `formName` couldn't be found.
|
||||
* @return {!Object} the parsed form values as an object.
|
||||
*/
|
||||
registry.util.parseForm = function(formName) {
|
||||
var form = /** @type {HTMLFormElement}
|
||||
*/ (document.querySelector('form[name=' + formName + ']'));
|
||||
if (form == null) {
|
||||
throw new Error('No such form named ' + formName);
|
||||
}
|
||||
var obj = {};
|
||||
// Find names first, since e.g. radio buttons have two elts with the
|
||||
// same name.
|
||||
var eltNames = {};
|
||||
for (var i = 0; i < form.elements.length; i++) {
|
||||
var elt = form.elements[i];
|
||||
if (elt.name == '') {
|
||||
continue;
|
||||
}
|
||||
eltNames[elt.name] = null;
|
||||
}
|
||||
for (var eltName in eltNames) {
|
||||
var elt = form.elements[eltName];
|
||||
var val;
|
||||
if (elt.type == 'checkbox') {
|
||||
val = elt.checked;
|
||||
} else {
|
||||
val = elt.value;
|
||||
}
|
||||
registry.util.expandObject_(obj, eltName, val);
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Give the object a value at the given path. Paths are split on dot
|
||||
* and array elements are recognized.
|
||||
*
|
||||
* <ul>
|
||||
* <li>a = 1
|
||||
* <li>foo:a = 1.5
|
||||
* <li>b.c = 2
|
||||
* <li>b.d = 3
|
||||
* <li>c[0] = 4
|
||||
* <li>c[1] = 5
|
||||
* <li>d[0].a = 6
|
||||
* <li>d[1].foo:b = 7
|
||||
* <li>d[1].@b = 8
|
||||
* </ul>
|
||||
*
|
||||
* Yields the following object:
|
||||
* <pre>
|
||||
* {
|
||||
* a: '1',
|
||||
* 'foo:a': '1.5',
|
||||
* b: {
|
||||
* c: '2',
|
||||
* d: '3'
|
||||
* },
|
||||
* c: ['4','5'],
|
||||
* d: [{a:'6'},{'foo:b':'7'},{'@b':'8'}]
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @param {!Object} obj
|
||||
* @param {string} pathSpec
|
||||
* @param {string|Array.<string>|null} val
|
||||
* @private
|
||||
*/
|
||||
registry.util.expandObject_ = function(obj, pathSpec, val) {
|
||||
var path = pathSpec.split('.');
|
||||
for (var p = 0; p < path.length; p++) {
|
||||
var fieldName = path[p];
|
||||
var arrElt = fieldName.match(/^([\w:]+)\[(\d+)\]$/);
|
||||
if (arrElt) {
|
||||
var arrName = arrElt[1];
|
||||
var arrNdx = arrElt[2];
|
||||
if (!obj[arrName]) {
|
||||
obj[arrName] = [];
|
||||
}
|
||||
obj = obj[arrName];
|
||||
fieldName = arrNdx;
|
||||
if (!obj[arrNdx]) {
|
||||
obj[arrNdx] = {};
|
||||
}
|
||||
} else {
|
||||
if (!obj[fieldName]) {
|
||||
obj[fieldName] = {};
|
||||
}
|
||||
}
|
||||
if (p == path.length - 1) {
|
||||
obj[fieldName] = val;
|
||||
} else {
|
||||
obj = obj[fieldName];
|
||||
}
|
||||
}
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue