text:
/** Afiq Xilantra Azmi
    CoralHR **/
/** Bila finalize, clean up unused css, & embed kat html for faster loading experience **/

/**** kaler ni cantik laaaa #00CED1 ****/
/** CORAL CANDEYH **/::-moz-selection {background:coral; text-shadow:none; color:seashell}::selection{background:coral; text-shadow:none; color:seashell}
html, body {width:100%; height:100%; -webkit-font-smoothing: antialiased;}
@media screen and (max-width: 767px) {html, body {overflow-x: hidden; overflow-y: auto}}
.browsehappy {text-align: center;  color: #fff; padding: 20px; font-size: 2em}
.browsehappy a {text-decoration: underline}
/**Fix Firefox problem untuk table > img r **/
@-moz-document url-prefix() {
    header .img-responsive {width:55%}
    .img-responsive {width:100%}
}
.full {
    position: relative;
    padding-top: 150px;
    display: table;
    width: 100%;
    height: 100%;
    background: url(../img/bg1.jpg) no-repeat center center fixed;
    background-color: coral;
    color: #fff;
    overflow: hidden;
    margin: 0;
    -webkit-background-size: cover;
       -moz-background-size: cover;
         -o-background-size: cover;
            background-size: cover;
}
.full h1 {font-size: 5.3em; font-weight: 300}
@media screen and (max-width: 767px) {
  .full {padding-top: 20px;
    background: url(../img/bg0.jpg) no-repeat center center fixed;
    -webkit-background-size: cover;
       -moz-background-size: cover;
         -o-background-size: cover;
            background-size: cover;
}
.full h1 {font-size: 4.7em}}

section, form, footer {display: table; width:100%; height:auto; padding: 20px; background-color: #e5e5e5 !important; color: #444 !important}
section h2 {font-size: 3.3em}
section p {font-size: 1.7em}
footer p{ color: hsla(0, 0%, 0%, 0.64)}
footer small {color: hsla(0, 0%, 100%, 0.7)}
footer a {color: inherit}
.bg-coral {background-color: coral; color: white}
.bg-lightseagreen {background-color: lightseagreen; color: white}
.bg-deepskyblue {background-color: deepskyblue; color: white}
.bg-kuningHensem {background-color: #ffb700; padding: 40px; color: #fff}
@media screen and (max-width: 769px) {[class*="bg-"] {padding-left: 5px !important;padding-right: 5px !important;}}
.bg-coral .btn-margin {margin: 30px 0; color: coral}
.bg-lightseagreen .btn-margin {margin: 30px 0; color: coral}
.bg-lightseagreen .btn-marginBottom {margin: 270px 0 0 0; color: coral}
@media screen and (max-width: 769px) {.bg-lightseagreen .btn-marginBottom {margin: 30px 0}}
.bg-kuningHensem .btn-margin {margin: 30px 0; color: coral}
.bg-dark .btn-margin {margin: 30px 0; color: coral}
.bg-clean .btn-margin {margin: 30px 0; color: coral}
.btn-margin:hover, .btn-marginBottom:hover {color: #fff; background: deepskyblue}
.vertical-center {display: table-cell; vertical-align: middle}
.besar {padding: 150px 60px}
@media screen and (max-width: 769px) {.besar {padding: 20px}}
.blokKecik {max-width: 750px}
.socialize {max-width: 580px}
.margin-atas {margin-top: 150px}
.socialize .margin-atas {margin-top: 45px}
.icon-desc {padding: 20px 60px}
@media screen and (max-width: 769px) {.mobile-center {text-align: center !important;display: block;margin-left: auto !important;margin-right: auto !important; float: none !important} .mobile-justify {text-align: justify}}
.icon-space {margin-right: 20px}
.subscribe input {width: 100%; background-color: #e5e5e5; outline: none; border: none; color: #444; font-size: 4em; margin-top: 20px;
-webkit-transition: all 0.3s ease-in-out;
   -moz-transition: all 0.3s ease-in-out;
    -ms-transition: all 0.3s ease-in-out;
     -o-transition: all 0.3s ease-in-out;
        transition: all 0.3s ease-in-out;}
.subscribe:hover input {background-color: #fff}
.subscribe input::-webkit-input-placeholder {color: #444}
.subscribe input:-moz-placeholder {color: #444} /* Firefox 18- */
.subscribe input::-moz-placeholder {color: #444} /* Firefox 19+ */
.subscribe input:-ms-input-placeholder {color: #444}
@media screen and (max-width: 769px) {.subscribe input {font-weight: bold;font-size: 1.5em;text-align: center; padding: 15px 0; border-bottom: 4px solid teal}.subscribe button {color: #fff; font-size: 1.5em; text-align: center; padding: 15px}}

.share-buttons img {padding: 5px;border: 0;box-shadow: 0;display: inline}
.bulat-bulat {
    width: 150px;
    height: 150px;
    background-color: #fff;
    color: coral;
    vertical-align: middle;
    line-height: 190px;
    -webkit-border-radius: 50%;
            border-radius: 50%;
}
.cards {
    background-color: coral;
    color: #fff;
    text-shadow: 0 0 15px tomato;
    display: block;
    padding: 20px;
    font-size: 1.7em;
    margin-top: 30px;
    border-radius: 4px;
    min-height: 180px
}

.bg-dark {background-color: #222; color: #fff; }
@media screen and (max-width: 769px) {.bg-dark p{font-size: 1.4em }}
.bg-dark a{color: deepskyblue}
.bg-dark a:hover{color: #ffb700}

.besar-sikit {padding-top: 60px}

.bg-clean {background-color: #e5e5e5; padding-top: 0; color: #555}
.bg-clean.besar {padding-top: 50px; padding-bottom: 150px}

.btn-khas {background-color: hotpink; color: white !important}

.bg-transparent {
    color: rgba(255, 255, 255, 0.95);
    padding-top: 5em;
    margin-left: auto;
    margin-right: auto;
    background: url(../img/grobok.jpg) center center no-repeat fixed;
    background-color: coral;
    -webkit-background-size: cover;
       -moz-background-size: cover;
         -o-background-size: cover;
            background-size: cover;
}
.bg-transparent .btn-margin {color: rgba(32, 178, 170, 0.8); background-color: rgba(255, 255, 255, 0.81)}
@media screen and (max-width: 767px) {.bg-transparent {padding-bottom: 50px }.bg-transparent .btn-margin {padding: 10px; font-size: 1.6em}}

@media screen and (max-width: 767px) {.btn-mobile-kecik {font-size: 1.2em}}

.fixed {
    background: url(../img/747__1397630488_219.92.14.157.jpg) center center no-repeat fixed;
    height: 300px;
    -webkit-background-size: cover;
       -moz-background-size: cover;
         -o-background-size: cover;
            background-size: cover;
}

.playVideo {
    position: relative;
    display: inline-block;
    top: -350px;
    font-size: 4em;
    border: 4px solid white;
    padding: 10px;
    background: #fff;
    color: coral;
    cursor: pointer;
    -webkit-border-radius: 5px;
            border-radius: 5px;
    -webkit-font-smoothing: antialiased;
    -webkit-box-shadow: 0 3px 0 3px rgba(0,0,0,.1);
            box-shadow: 0 3px 0 3px rgba(0,0,0,.1);
    -webkit-transition: all .4s cubic-bezier(0.68, -0.55, 0.265, 1.55);
            transition: all .4s cubic-bezier(0.68, -0.55, 0.265, 1.55);

 }

/** Disable hover fx on mobile **/
@media screen and (max-width: 767px) {
    .playVideo {position:static;top: -160px;font-size: 2em}
    .full:hover .playVideo {
    -webkit-transform: none !important;
       -moz-transform: none !important;
        -ms-transform: none !important;
         -o-transform: none !important;
            transform: none !important;
    }
}

.full:hover .playVideo {
    transition-delay: .8s;
    -webkit-font-smoothing: antialiased;
    -webkit-transform: scale(1.4) translateY(-50px);
       -moz-transform: scale(1.4) translateY(-50px);
        -ms-transform: scale(1.4) translateY(-50px);
         -o-transform: scale(1.4) translateY(-50px);
            transform: scale(1.4) translateY(-50px);
}

.btn-full {width: 100%}

.btn-theChosen:after {
    font-family: 'FontAwesome';
    content: '\f0d7';
    color: teal;
    position: absolute;
    font-size: 2em;
    display: block;
    margin-left: 34.5%;
    margin-top: -15px;
}
@media screen and (max-width: 767px) {.btn-theChosen:after {margin-left: 28%}}

.isiTab-Penerangan {padding: 5px 67px; text-align: justify}
@media screen and (max-width: 767px) {.isiTab-Penerangan {padding: 10px; text-align: left}}

.icon-animate {
    -webkit-transition: all .5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
            transition: all .5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}

.isiTab-peneranganUtama {
    //border-radius: 4px;
    //background: grey;
    border-right: 1px solid rgba(0, 0, 0, 0.11)
}

.tab-content{display: none}
.tab-link.current{background: teal; color: #fff}
.tab-content.current{display: inherit}

@media screen and (max-width: 767px) {
.mobileFont-small {
    font-size: 1em;
     padding-left:5px;
    padding-right:5px;
    font-weight: 600;
}
.resetMargin {
    margin-right:-15px;
    margin-left:-15px;
    padding-right: 20px;
    width: 130px;
}
.isiTab-peneranganUtama {background-color: #444; color: #fff; border-radius: 4px}
}

.entahLaNak {
    padding-top: 100px;
    padding-bottom: 50px
}

@media screen and (max-width: 767px) {.entahLaNak {padding-top: 50px;}}

section h4 {font-weight: 500; font-size: 2em; background-color: lightslategray; color: #fff; width: 300px; padding: 20px 0; margin: 20px auto; border-radius: 4px; box-shadow: inset 0 0 4px 2px rgba(0, 0, 0, 0.2); border: 1px solid rgba(255, 255, 255, 0.9)}

@media screen and (max-width: 767px) {section h4 {width: 100%}}

.vr {
    height: 300px;
    border-left: 2px solid rgba(0, 0, 0, 0.12);
    position: absolute;
    margin-top: 50px;
    left: 50%;
}
@media screen and (max-width: 767px) {.vr {display: none}}

.kecik-sikit {max-width: 80%; margin: 0 auto}

.tellThemMore {
    padding:40px;
    color: white;
    border-radius: 4px;
    background-color: lightslategray;
    -webkit-box-shadow: 0 0 20px 1px rgba(0,0,0,.3);
            box-shadow: 0 0 20px 1px rgba(0,0,0,.3);
                border: 1px solid rgba(0, 0, 0, 0.1);
}
@media screen and (max-width: 767px) {.kecik-sikit {max-width: 100%;}.tellThemMore {padding: 20px; text-align: left; font-size: 1.4em}}

.logo {background: transparent; height: 400px}
@media screen and (max-width: 767px) {.logo {height: 250px}}

.custom-popup-class .mfp-container {
  padding-top: 40px;
  padding-bottom: 40px;
}
.custom-popup-class .mfp-content {
  width: 100%;
  max-width: 700px;
}
#popup {
  width: 100%;
  height: 0;
  overflow: hidden;
  /* This is scale ratio */
  padding-top: 56.25%;
}
#popup .mfp-close {
  top: -44px;
  color: #FFF;
  text-align: right;
  right: 1px;
}
.videoPopup {
  background: #fff;
  position: absolute;
  top: -3px;
  left: 0;
  width: 100%;
  height: 100%;
  -webkit-box-shadow: 0 0 40px 0 rgba(0,0,0,1);
          box-shadow: 0 0 40px 0 rgba(0,0,0,1);
}

/* Magnific Popup CSS */
.mfp-bg {
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 1042;
  overflow: hidden;
  position: fixed;
  background: #0b0b0b;
  opacity: 0.8;
  filter: alpha(opacity=80); }

.mfp-wrap {
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 1043;
  position: fixed;
  outline: none !important;
  -webkit-backface-visibility: hidden; }

.mfp-container {
  text-align: center;
  position: absolute;
  width: 100%;
  height: 100%;
  left: 0;
  top: 0;
  padding: 0 8px;
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box; }

.mfp-container:before {
  content: '';
  display: inline-block;
  height: 100%;
  vertical-align: middle; }

.mfp-align-top .mfp-container:before {
  display: none; }

.mfp-content {
  position: relative;
  display: inline-block;
  vertical-align: middle;
  margin: 0 auto;
  text-align: left;
  z-index: 1045; }

.mfp-inline-holder .mfp-content, .mfp-ajax-holder .mfp-content {
  width: 100%;
  cursor: auto; }

.mfp-ajax-cur {
  cursor: progress; }

.mfp-zoom-out-cur, .mfp-zoom-out-cur .mfp-image-holder .mfp-close {
  cursor: -moz-zoom-out;
  cursor: -webkit-zoom-out;
  cursor: zoom-out; }

.mfp-zoom {
  cursor: pointer;
  cursor: -webkit-zoom-in;
  cursor: -moz-zoom-in;
  cursor: zoom-in; }

.mfp-auto-cursor .mfp-content {
  cursor: auto; }

.mfp-close, .mfp-arrow, .mfp-preloader, .mfp-counter {
  -webkit-user-select: none;
  -moz-user-select: none;
  user-select: none; }

.mfp-loading.mfp-figure {
  display: none; }

.mfp-hide {
  display: none !important; }

.mfp-preloader {
  color: #cccccc;
  position: absolute;
  top: 50%;
  width: auto;
  text-align: center;
  margin-top: -0.8em;
  left: 8px;
  right: 8px;
  z-index: 1044; }
  .mfp-preloader a {
    color: #cccccc; }
    .mfp-preloader a:hover {
      color: white; }

.mfp-s-ready .mfp-preloader {
  display: none; }

.mfp-s-error .mfp-content {
  display: none; }

button.mfp-close, button.mfp-arrow {
  overflow: visible;
  cursor: pointer;
  background: transparent;
  border: 0;
  -webkit-appearance: none;
  display: block;
  outline: none;
  padding: 0;
  z-index: 1046;
  -webkit-box-shadow: none;
  box-shadow: none; }
button::-moz-focus-inner {
  padding: 0;
  border: 0; }

.mfp-close {
  width: 44px;
  height: 44px;
  line-height: 44px;
  position: absolute;
  right: 0;
  top: 0;
  text-decoration: none;
  text-align: center;
  opacity: 0.65;
  padding: 0 0 18px 10px;
  color: white;
  font-style: normal;
  font-size: 28px;
  font-family: Arial, Baskerville, monospace; }
  .mfp-close:hover, .mfp-close:focus {
    opacity: 1; }
  .mfp-close:active {
    top: 1px; }

.mfp-close-btn-in .mfp-close {
  color: #333333; }

.mfp-image-holder .mfp-close, .mfp-iframe-holder .mfp-close {
  color: white;
  right: -6px;
  text-align: right;
  padding-right: 6px;
  width: 100%; }

.mfp-counter {
  position: absolute;
  top: 0;
  right: 0;
  color: #cccccc;
  font-size: 12px;
  line-height: 18px; }

.mfp-arrow {
  position: absolute;
  opacity: 0.65;
  margin: 0;
  top: 50%;
  margin-top: -55px;
  padding: 0;
  width: 90px;
  height: 110px;
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0); }
  .mfp-arrow:active {
    margin-top: -54px; }
  .mfp-arrow:hover, .mfp-arrow:focus {
    opacity: 1; }
  .mfp-arrow:before, .mfp-arrow:after, .mfp-arrow .mfp-b, .mfp-arrow .mfp-a {
    content: '';
    display: block;
    width: 0;
    height: 0;
    position: absolute;
    left: 0;
    top: 0;
    margin-top: 35px;
    margin-left: 35px;
    border: medium inset transparent; }
  .mfp-arrow:after, .mfp-arrow .mfp-a {
    border-top-width: 13px;
    border-bottom-width: 13px;
    top: 8px; }
  .mfp-arrow:before, .mfp-arrow .mfp-b {
    border-top-width: 21px;
    border-bottom-width: 21px; }

.mfp-arrow-left {
  left: 0; }
  .mfp-arrow-left:after, .mfp-arrow-left .mfp-a {
    border-right: 17px solid white;
    margin-left: 31px; }
  .mfp-arrow-left:before, .mfp-arrow-left .mfp-b {
    margin-left: 25px;
    border-right: 27px solid #3f3f3f; }

.mfp-arrow-right {
  right: 0; }
  .mfp-arrow-right:after, .mfp-arrow-right .mfp-a {
    border-left: 17px solid white;
    margin-left: 39px; }
  .mfp-arrow-right:before, .mfp-arrow-right .mfp-b {
    border-left: 27px solid #3f3f3f; }

.mfp-iframe-holder {
  padding-top: 40px;
  padding-bottom: 40px; }
  .mfp-iframe-holder .mfp-content {
    line-height: 0;
    width: 100%;
    max-width: 900px; }
  .mfp-iframe-holder .mfp-close {
    top: -40px; }

.mfp-iframe-scaler {
  width: 100%;
  height: 0;
  overflow: hidden;
  padding-top: 56.25%; }
  .mfp-iframe-scaler iframe {
    position: absolute;
    display: block;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
    background: black; }

/* Main image in popup */
img.mfp-img {
  width: auto;
  max-width: 100%;
  height: auto;
  display: block;
  line-height: 0;
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
  padding: 40px 0 40px;
  margin: 0 auto; }

/* The shadow behind the image */
.mfp-figure {
  line-height: 0; }
  .mfp-figure:after {
    content: '';
    position: absolute;
    left: 0;
    top: 40px;
    bottom: 40px;
    display: block;
    right: 0;
    width: auto;
    height: auto;
    z-index: -1;
    box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
    background: #444444; }
  .mfp-figure small {
    color: #bdbdbd;
    display: block;
    font-size: 12px;
    line-height: 14px; }
  .mfp-figure figure {
    margin: 0; }

.mfp-bottom-bar {
  margin-top: -36px;
  position: absolute;
  top: 100%;
  left: 0;
  width: 100%;
  cursor: auto; }

.mfp-title {
  text-align: left;
  line-height: 18px;
  color: #f3f3f3;
  word-wrap: break-word;
  padding-right: 36px; }

.mfp-image-holder .mfp-content {
  max-width: 100%; }

.mfp-gallery .mfp-image-holder .mfp-figure {
  cursor: pointer; }

@media screen and (max-width: 800px) and (orientation: landscape), screen and (max-height: 300px) {
  /**
       * Remove all paddings around the image on small screen
       */
  .mfp-img-mobile .mfp-image-holder {
    padding-left: 0;
    padding-right: 0; }
  .mfp-img-mobile img.mfp-img {
    padding: 0; }
  .mfp-img-mobile .mfp-figure {
    /* The shadow behind the image */ }
    .mfp-img-mobile .mfp-figure:after {
      top: 0;
      bottom: 0; }
    .mfp-img-mobile .mfp-figure small {
      display: inline;
      margin-left: 5px; }
  .mfp-img-mobile .mfp-bottom-bar {
    background: rgba(0, 0, 0, 0.6);
    bottom: 0;
    margin: 0;
    top: auto;
    padding: 3px 5px;
    position: fixed;
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box; }
    .mfp-img-mobile .mfp-bottom-bar:empty {
      padding: 0; }
  .mfp-img-mobile .mfp-counter {
    right: 5px;
    top: 3px; }
  .mfp-img-mobile .mfp-close {
    top: 0;
    right: 0;
    width: 35px;
    height: 35px;
    line-height: 35px;
    background: rgba(0, 0, 0, 0.6);
    position: fixed;
    text-align: center;
    padding: 0; } }

@media all and (max-width: 900px) {
  .mfp-arrow {
    -webkit-transform: scale(0.75);
    transform: scale(0.75); }
  .mfp-arrow-left {
    -webkit-transform-origin: 0;
    transform-origin: 0; }
  .mfp-arrow-right {
    -webkit-transform-origin: 100%;
    transform-origin: 100%; }
  .mfp-container {
    padding-left: 6px;
    padding-right: 6px; } }

.mfp-ie7 .mfp-img {
  padding: 0; }
.mfp-ie7 .mfp-bottom-bar {
  width: 600px;
  left: 50%;
  margin-left: -300px;
  margin-top: 5px;
  padding-bottom: 5px; }
.mfp-ie7 .mfp-container {
  padding: 0; }
.mfp-ie7 .mfp-content {
  padding-top: 44px; }
.mfp-ie7 .mfp-close {
  top: 0;
  right: 0;
  padding-top: 0; }

/************************************************************************/
.mfp-with-zoom .mfp-container,
.mfp-with-zoom.mfp-bg {
  opacity: 0;
  /*-webkit-backface-visibility: hidden;
  /* ideally, transition speed should match zoom duration */
  -webkit-transition: all 0.3s ease-out;
  -moz-transition: all 0.3s ease-out;
  -o-transition: all 0.3s ease-out;
  transition: all 0.3s ease-out;
}

.mfp-with-zoom.mfp-ready .mfp-container {
    opacity: 1;
}
.mfp-with-zoom.mfp-ready.mfp-bg {
    opacity: 0.8;
}

.mfp-with-zoom.mfp-removing .mfp-container,
.mfp-with-zoom.mfp-removing.mfp-bg {
  opacity: 0;
}

/**efX**/
.animated {
  -webkit-animation-duration: 1s;
  animation-duration: 1s;
  -webkit-animation-fill-mode: both;
  animation-fill-mode: both;
}

.animated.infinite {
  -webkit-animation-iteration-count: infinite;
  animation-iteration-count: infinite;
}

@-webkit-keyframes swing {
  20% {
    -webkit-transform: rotate(15deg);
    transform: rotate(15deg);
  }

  40% {
    -webkit-transform: rotate(-10deg);
    transform: rotate(-10deg);
  }

  60% {
    -webkit-transform: rotate(5deg);
    transform: rotate(5deg);
  }

  80% {
    -webkit-transform: rotate(-5deg);
    transform: rotate(-5deg);
  }

  100% {
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
  }
}

@keyframes swing {
  20% {
    -webkit-transform: rotate(15deg);
    -ms-transform: rotate(15deg);
    transform: rotate(15deg);
  }

  40% {
    -webkit-transform: rotate(-10deg);
    -ms-transform: rotate(-10deg);
    transform: rotate(-10deg);
  }

  60% {
    -webkit-transform: rotate(5deg);
    -ms-transform: rotate(5deg);
    transform: rotate(5deg);
  }

  80% {
    -webkit-transform: rotate(-5deg);
    -ms-transform: rotate(-5deg);
    transform: rotate(-5deg);
  }

  100% {
    -webkit-transform: rotate(0deg);
    -ms-transform: rotate(0deg);
    transform: rotate(0deg);
  }
}

.swing {
  -webkit-transform-origin: top center;
  -ms-transform-origin: top center;
  transform-origin: top center;
  -webkit-animation-name: swing;
  animation-name: swing;
}

@-webkit-keyframes fadeIn {
  0% {
    opacity: 0;
  }

  100% {
    opacity: 1;
  }
}

@keyframes fadeIn {
  0% {
    opacity: 0;
  }

  100% {
    opacity: 1;
  }
}

 .fadeIn {
  -webkit-animation-name: fadeIn;
  animation-name: fadeIn;
}

@-webkit-keyframes fadeInDown {
  0% {
    opacity: 0;
    -webkit-transform: translateY(-20px);
    transform: translateY(-20px);
  }

  100% {
    opacity: 1;
    -webkit-transform: translateY(0);
    transform: translateY(0);
  }
}

@keyframes fadeInDown {
  0% {
    opacity: 0;
    -webkit-transform: translateY(-20px);
    -ms-transform: translateY(-20px);
    transform: translateY(-20px);
  }

  100% {
    opacity: 1;
    -webkit-transform: translateY(0);
    -ms-transform: translateY(0);
    transform: translateY(0);
  }
}

.fadeInDown {
  -webkit-animation-name: fadeInDown;
  animation-name: fadeInDown;
}

@-webkit-keyframes fadeInUp {
  0% {
    opacity: 0;
    -webkit-transform: translateY(20px);
    transform: translateY(20px);
  }

  100% {
    opacity: 1;
    -webkit-transform: translateY(0);
    transform: translateY(0);
  }
}

@keyframes fadeInUp {
  0% {
    opacity: 0;
    -webkit-transform: translateY(20px);
    -ms-transform: translateY(20px);
    transform: translateY(20px);
  }

  100% {
    opacity: 1;
    -webkit-transform: translateY(0);
    -ms-transform: translateY(0);
    transform: translateY(0);
  }
}

.fadeInUp {
  -webkit-animation-name: fadeInUp;
  animation-name: fadeInUp;
}

@-webkit-keyframes flipInX {
  0% {
    -webkit-transform: perspective(400px) rotateX(90deg);
    transform: perspective(400px) rotateX(90deg);
    opacity: 0;
  }

  40% {
    -webkit-transform: perspective(400px) rotateX(-10deg);
    transform: perspective(400px) rotateX(-10deg);
  }

  70% {
    -webkit-transform: perspective(400px) rotateX(10deg);
    transform: perspective(400px) rotateX(10deg);
  }

  100% {
    -webkit-transform: perspective(400px) rotateX(0deg);
    transform: perspective(400px) rotateX(0deg);
    opacity: 1;
  }
}

@keyframes flipInX {
  0% {
    -webkit-transform: perspective(400px) rotateX(90deg);
    -ms-transform: perspective(400px) rotateX(90deg);
    transform: perspective(400px) rotateX(90deg);
    opacity: 0;
  }

  40% {
    -webkit-transform: perspective(400px) rotateX(-10deg);
    -ms-transform: perspective(400px) rotateX(-10deg);
    transform: perspective(400px) rotateX(-10deg);
  }

  70% {
    -webkit-transform: perspective(400px) rotateX(10deg);
    -ms-transform: perspective(400px) rotateX(10deg);
    transform: perspective(400px) rotateX(10deg);
  }

  100% {
    -webkit-transform: perspective(400px) rotateX(0deg);
    -ms-transform: perspective(400px) rotateX(0deg);
    transform: perspective(400px) rotateX(0deg);
    opacity: 1;
  }
}

.flipInX {
  -webkit-backface-visibility: visible !important;
  -ms-backface-visibility: visible !important;
  backface-visibility: visible !important;
  -webkit-animation-name: flipInX;
  animation-name: flipInX;
}

@-webkit-keyframes slideInDown {
  0% {
    opacity: 0;
    -webkit-transform: translateY(-2000px);
    transform: translateY(-2000px);
  }

  100% {
    -webkit-transform: translateY(0);
    transform: translateY(0);
  }
}

@keyframes slideInDown {
  0% {
    opacity: 0;
    -webkit-transform: translateY(-2000px);
    -ms-transform: translateY(-2000px);
    transform: translateY(-2000px);
  }

  100% {
    -webkit-transform: translateY(0);
    -ms-transform: translateY(0);
    transform: translateY(0);
  }
}

.slideInDown {
  -webkit-animation-name: slideInDown;
  animation-name: slideInDown;
}

@-webkit-keyframes slideInUp {
  0% {
    opacity: 0;
    -webkit-transform: translateY(2000px);
    transform: translateY(2000px);
  }

  100% {
    opacity: 1;
    -webkit-transform: translateY(0);
    transform: translateY(0);
  }
}

@keyframes slideInUp {
  0% {
    opacity: 0;
    -webkit-transform: translateY(2000px);
    -ms-transform: translateY(2000px);
    transform: translateY(2000px);
  }

  100% {
    opacity: 1;
    -webkit-transform: translateY(0);
    -ms-transform: translateY(0);
    transform: translateY(0);
  }
}

.slideInUp {
  -webkit-animation-name: slideInUp;
  animation-name: slideInUp;
}

@-webkit-keyframes zoomInUp {
  0% {
    opacity: 0;
    -webkit-transform: scale(.1) translateY(2000px);
    transform: scale(.1) translateY(2000px);
    -webkit-animation-timing-function: ease-in-out;
    animation-timing-function: ease-in-out;
  }

  60% {
    opacity: 1;
    -webkit-transform: scale(.475) translateY(-60px);
    transform: scale(.475) translateY(-60px);
    -webkit-animation-timing-function: ease-out;
    animation-timing-function: ease-out;
  }
}

@keyframes zoomInUp {
  0% {
    opacity: 0;
    -webkit-transform: scale(.1) translateY(2000px);
    -ms-transform: scale(.1) translateY(2000px);
    transform: scale(.1) translateY(2000px);
    -webkit-animation-timing-function: ease-in-out;
    animation-timing-function: ease-in-out;
  }

  60% {
    opacity: 1;
    -webkit-transform: scale(.475) translateY(-60px);
    -ms-transform: scale(.475) translateY(-60px);
    transform: scale(.475) translateY(-60px);
    -webkit-animation-timing-function: ease-out;
    animation-timing-function: ease-out;
  }
}

.zoomInUp {
  -webkit-animation-name: zoomInUp;
  animation-name: zoomInUp;
}

@-webkit-keyframes rotateInUpLeft {
  0% {
    -webkit-transform-origin: left bottom;
    transform-origin: left bottom;
    -webkit-transform: rotate(90deg);
    transform: rotate(90deg);
    opacity: 0;
  }

  100% {
    -webkit-transform-origin: left bottom;
    transform-origin: left bottom;
    -webkit-transform: rotate(0);
    transform: rotate(0);
    opacity: 1;
  }
}

@keyframes rotateInUpLeft {
  0% {
    -webkit-transform-origin: left bottom;
    -ms-transform-origin: left bottom;
    transform-origin: left bottom;
    -webkit-transform: rotate(90deg);
    -ms-transform: rotate(90deg);
    transform: rotate(90deg);
    opacity: 0;
  }

  100% {
    -webkit-transform-origin: left bottom;
    -ms-transform-origin: left bottom;
    transform-origin: left bottom;
    -webkit-transform: rotate(0);
    -ms-transform: rotate(0);
    transform: rotate(0);
    opacity: 1;
  }
}

.rotateInUpLeft {
  -webkit-animation-name: rotateInUpLeft;
  animation-name: rotateInUpLeft;
}

.bingkai {
    margin-bottom: 50px;
     -webkit-box-shadow: 0 0 20px 0 rgba(0,0,0,.5);
             box-shadow: 0 0 20px 0 rgba(5,0,0,.5);
}
.fontSize-up{font-size: 1.7em} @keyframes slideOutUp {
  0% {
    -webkit-transform: translateY(0);
    -ms-transform: translateY(0);
    transform: translateY(0);
  }

  100% {
    opacity: 0;
    -webkit-transform: translateY(-2000px);
    -ms-transform: translateY(-2000px);
    transform: translateY(-2000px);
  }
}

.slideOutUp {
  -webkit-animation-name: slideOutUp;
  animation-name: slideOutUp;
}

/**Animation untuk tracker**/
.awanTerbanggggg {
 -webkit-animation-name: kapalTerbang;          /** Namanya siapa **/
 -webkit-animation-duration: 6s;                     /** Berapa lama nak animate **/
 -webkit-animation-iteration-count: infinite;              /** Entahlah nak **/
  -webkit-animation-timing-function: ease-in-out;        /** Easing **/
 -webkit-animation-fill-mode: forwards;              /** Entahlah nak **/
 }

@-webkit-keyframes kapalTerbang {
  from {-webkit-transform: rotate(-50deg) translateX(-500px) translateY(370px)}
  to   {-webkit-transform: rotate(5deg) translateX(450px) translateY(-360px)}
}

@keyframes kapalTerbang {
  from {
      -moz-transform: rotate(-50deg) translateX(-500px) translateY(370px);
       -ms-transform: rotate(-50deg) translateX(-500px) translateY(370px);
        -o-transform: rotate(-50deg) translateX(-500px) translateY(370px);
           transform: rotate(-50deg) translateX(-500px) translateY(370px);
  }
  to   {
      -moz-transform: rotate(5deg) translateX(450px) translateY(-360px);
       -ms-transform: rotate(5deg) translateX(450px) translateY(-360px);
        -o-transform: rotate(5deg) translateX(450px) translateY(-360px);
           transform: rotate(5deg) translateX(450px) translateY(-360px);
  }
}

.map-tracker {
-webkit-animation-name: tracker;
        animation-name: tracker;
-webkit-animation-duration: 1s;
        animation-duration: 1s;
-webkit-animation-iteration-count: infinite;
        animation-iteration-count: infinite;
-webkit-animation-timing-function: ease-in-out;
        animation-timing-function: ease-in-out;
-webkit-animation-fill-mode: forwards;
        animation-fill-mode: forwards;
-webkit-animation-direction: alternate;
        animation-direction: alternate;
-webkit-animation-delay: 0s;
        animation-delay: 0s;
 }

@-webkit-keyframes tracker {
  from {-webkit-transform: translateX(-5px);
  }
  to   {-webkit-transform: translateX(5px);
  }
}

@keyframes tracker {
  from {
      -moz-transform: translateX(-5px);
       -ms-transform: translateX(-5px);
        -o-transform: translateX(-5px);
           transform: translateX(-5px);
  }
  to   {
      -moz-transform: translateX(-5px);
       -ms-transform: translateX(-5px);
        -o-transform: translateX(-5px);
           transform: translateX(-5px);
  }
}

.delay1 { -webkit-animation-delay: .5s; animation-delay: .5s; }
.delay2 { -webkit-animation-delay: 1s; animation-delay: 1s; }
.delay3 { -webkit-animation-delay: 1.5s; animation-delay: 1.5s; }
.delay4 { -webkit-animation-delay: 2s; animation-delay: 2s; }
.delay5 { -webkit-animation-delay: 2.5s; animation-delay: 2.5s; }
.delay6 { -webkit-animation-delay: 2.8s; animation-delay: 2.8s; }
/**Line kat mobile**/
.st7, .st8 {
  stroke-dasharray: 1100;
  stroke-dashoffset: 1100;
  -webkit-animation: dash 5s linear alternate infinite;
          animation: dash 5s linear alternate infinite}
@-webkit-keyframes dash {
  from {stroke-dashoffset: 1100}
  to {stroke-dashoffset: 0}
@keyframes dash {
  from {stroke-dashoffset: 1100}
  to {stroke-dashoffset: 0}
  }
.linePanjang {
  stroke-dasharray: 1100;
  stroke-dashoffset: 1100;
  -webkit-animation: coraline 5s linear alternate infinite;
          animation: coraline 5s linear alternate infinite}

@-webkit-keyframes coraline {
  from {stroke-dashoffset: 1100}
  to {stroke-dashoffset: 0}
@keyframes coraline {
  from {stroke-dashoffset: 1100}
  to {stroke-dashoffset: 0}
  }


text:
layout: post
title: How to Choose an Application Server?
---

[The Aquarium](http://blogs.sun.com/theaquarium/entry/what_a_difference_18_months) has a new post giving quotes about different application servers that have been evaluated in the latest [Forrester](http://www.forrester.com/Research/Document/Excerpt/0,,37138,00.html) tests.

Although you need to read the full report to get all the details of the evaluation, the quotes posted are quite interesting.

redpajama_set_name: RedPajamaGithub


text:
import { Component } from 'react';
import Dropzone from 'react-dropzone';
import { connect } from 'react-redux';
import { compose } from 'redux';

import { LoadingSpinner } from '@waldur/core/LoadingSpinner';
import { TranslateProps, withTranslation } from '@waldur/i18n';
import { IssueReload } from '@waldur/issues/IssueReload';
import { RootState } from '@waldur/store/reducers';

import * as actions from './actions';
import './IssueAttachmentsContainer.scss';
import { IssueAttachmentsList } from './IssueAttachmentsList';
import { getAttachments, getUploading, getIsLoading } from './selectors';
import { Attachment } from './types';

interface PureIssueAttachmentsContainerProps extends TranslateProps {
  getAttachments(): void;
  putAttachments(files: File[]): void;
  issue: { [key: string]: string };
  loading: boolean;
  attachments: Attachment[];
  uploading: number;
}

export class PureIssueAttachmentsContainer extends Component<PureIssueAttachmentsContainerProps> {
  state = {
    dropzoneActive: false,
  };

  dropzoneNode: Dropzone;

  componentDidMount() {
    this.props.getAttachments();
  }

  onDragEnter = () => this.setState({ dropzoneActive: true });

  onDragLeave = () => this.setState({ dropzoneActive: false });

  onDrop = (files) => {
    this.setState({ dropzoneActive: false });
    this.props.putAttachments(files);
  };

  openDownloadModal = () => this.dropzoneNode.open();

  render() {
    const { attachments, loading, uploading, issue, translate } = this.props;
    const { dropzoneActive } = this.state;

    return (
      <Dropzone
        disableClick={true}
        style={{ position: 'relative' }}
        onDrop={this.onDrop}
        onDragEnter={this.onDragEnter}
        onDragLeave={this.onDragLeave}
        ref={(node) => (this.dropzoneNode = node)}
      >
        {dropzoneActive && (

              {translate('Drop files to attach them to the issue.')}

        )}

{translate('Attachments')}

              <IssueReload issueUrl={issue.url} />

            {loading ? (
              <LoadingSpinner />
            ) : (

                    {translate('Drop files to attach, or')}{' '}

                      {translate('browse')}.

                <IssueAttachmentsList
                  attachments={attachments}
                  uploading={uploading}
                />

            )}

      </Dropzone>
    );
  }
}

const mapStateToProps = (state: RootState) => ({
  attachments: getAttachments(state),
  loading: getIsLoading(state),
  uploading: getUploading(state),
});

const mapDispatchToProps = (dispatch, ownProps) => ({
  getAttachments: (): void =>
    dispatch(actions.issueAttachmentsGet(ownProps.issue.url)),
  putAttachments: (files: File[]): void =>
    dispatch(actions.issueAttachmentsPut(ownProps.issue.url, files)),
});

const enhance = compose(
  connect(mapStateToProps, mapDispatchToProps),
  withTranslation,
);

export const IssueAttachmentsContainer = enhance(PureIssueAttachmentsContainer);


text:
function category_list_load(filelist){
    var model_container = document.getElementById('model');
    for (var i = 0; i <= filelist.length; i++) {
        if (typeof filelist[i] != "undefined"){
            var link = document.createElement("a");
            link.addEventListener("click", model_list_populate, false);
            var tmp_param = filelist[i].split("/");
            if (typeof tmp_param[1] != "undefined"){
                var img = "model_list/models/"+ tmp_param[1].substring(0, 2)  + "/" + tmp_param[1].substring(0,4) +"/"+ tmp_param[1]+"/"+tmp_param[1]+"_thumb.jpg";
                link.innerHTML = tmp_param[0] + "
";
                model_container.appendChild(link);
            }
        }
    }
}

function category_list_populate(event) {
    var param = event.target.name;
    var model_container = document.getElementById('model');
    model_container.innerHTML = "";
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange=function() {
        if (xmlhttp.readyState==4 && xmlhttp.status==200) {
            var filelist = JSON.parse(xmlhttp.responseText);
            category_list_load(filelist);
        }
    }
    xmlhttp.open("GET","./annotationTools/php/3d/model_server.php?task=submodel&param="+param,true);
    xmlhttp.send();
}

function createObject(container, name) {
    if (name == 'NONE') {
        if (container.children.length>1) {
            container.remove(container.children[1]);
            render();
        }
    return;
    }
    var model3d_material = new THREE.MeshNormalMaterial({side:THREE.DoubleSide,opacity:0.2});
    model3d_loader.load(name, function (object) {
    object.traverse(function (child) {
        if (child instanceof THREE.Mesh) {
            child.material = model3d_material;
        }
    });

    object.name = name;
    window.select.model = object;
    if (container.children.length>1) {
        container.remove(container.children[1]);
    }
    container.add(object);
    render();
    });
}

function CreateModelList(){
    var html_str = '
 \

 \

 \

 ';
    InsertAfterDiv(html_str, 'body');
    $('#model_column').css('display', 'block');
}

function model_click_load(event) {
    var param = event.target.name;
    var name = "model_list/models/"+param.substring(0,2) + "/" + param.substring(0,4) + "/" + param+"/untitled.obj";
    createObject(window.select.cube, name);
    setup_model_list();
}

function model_list_load(filelist){
    var model_counter = 0;
    var model_container = document.getElementById('model');
    inner_counter = 0;
    for (var i = model_counter; i <= filelist.length; i++) {
        if (i == filelist.length){
            model_counter = i;
            break;
        }
        if (inner_counter >= 10){
            model_counter = model_counter + 10;
            break;
        }
        inner_counter = inner_counter + 1;
        var link = document.createElement("a");
        link.addEventListener("click", model_click_load, false);
        var tmp_param = filelist[i];
        var name = "model_list/models/"+tmp_param.substring(0,2) + "/" + tmp_param.substring(0,4) + "/" + tmp_param+"/"+tmp_param+"_thumb.jpg";
        link.innerHTML = "
"
        model_container.appendChild(link);
    }
}

function model_list_populate(event) {
    var param = event.target.name + ".txt";
    var model_container = document.getElementById('model');
    model_container.innerHTML = "";
    var scrollAmount = $("#model").scrollTop();
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange=function() {
        if (xmlhttp.readyState==4 && xmlhttp.status==200) {
            var filelist = JSON.parse(xmlhttp.responseText);
            model_list_load(filelist);
            $("#model").scroll(function(event){
                var scrollAmount = $("#model").scrollTop();
                var scrollPercent = scrollAmount/$("#model").prop('scrollHeight')*100;
                if (scrollPercent > 40 && $("#model").prop('scrollHeight') < 4000){
                    model_list_load(filelist);
                }
                else if (scrollPercent > 80) {
                    model_list_load(filelist);
                }
            });
        }
    }
    xmlhttp.open("GET","./annotationTools/php/3d/model_server.php?task=submodel&param="+param,true);
    xmlhttp.send();
}

function setup_model_list() {
    var model_container = document.getElementById('model');
    if (!model_container){
        return ;
    }
    model_container.innerHTML = "
Browse 3D models
";
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange=function() {
    if (xmlhttp.readyState==4 && xmlhttp.status==200) {
        var filelist = JSON.parse(xmlhttp.responseText);
        for (var i = 0; i < filelist.length; i++) {
                var link = document.createElement("a");
                link.className = "model_type";
                link.addEventListener("click", category_list_populate, false);
                category_name = filelist[i].replace(".list", "");
                category_name = category_name.split("/")[category_name.split("/").length-1];
                link.innerHTML = category_name;
                link.name = filelist[i];
                model_container.appendChild(link);
            }
        }
    }
    xmlhttp.open("GET","./annotationTools/php/3d/model_server.php?task=model_list",true);
    xmlhttp.send();
}


text:
package com.wwpass.connection;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import org.apache.commons.codec.DecoderException;
import org.junit.*;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;

import com.google.code.tempusfugit.concurrency.ConcurrentTestRunner;
import com.wwpass.connection.exceptions.WWPassProtocolException;
import com.wwpass.connection.util.TestUtils;

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
//@RunWith(ConcurrentTestRunner.class)
public class WWPassConnectionTest extends Assert {

	private static final byte[] BYTE_DATA = {0,1,-2,3,-4,-5,6};

	private static String certFile;
	private static String keyFile;
	private static byte[] imgData;

	private static TestUtils testUtils;
	private static List<String> tickets = new ArrayList<String>();
	private static WWPassConnection conn = null;

	static {
		try {
			Properties props = new Properties();
			props.load(ClassLoader.class.getResourceAsStream("/setup.properties"));

			certFile = props.getProperty("CERT_FILE");
			keyFile = props.getProperty("KEY_FILE");

			InputStream is = ClassLoader.class.getResourceAsStream("/img/bytes.jpg");
			byte[] buffer = new byte[8192];
		    int bytesRead;
		    ByteArrayOutputStream output = new ByteArrayOutputStream();
		    while ((bytesRead = is.read(buffer)) != -1)
		    {
		        output.write(buffer, 0, bytesRead);
		    }
		    imgData = output.toByteArray();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@BeforeClass
	public static void setup() throws GeneralSecurityException, IOException,
			InterruptedException {

		conn = new WWPassConnection(certFile, keyFile);
		testUtils = new TestUtils();
		String[] ticket = testUtils.getTicket().split(" ", 2);
		if ("200".equals(ticket[0])) {
			String newticket = conn.putTicket(ticket[1], 300);
			tickets.add(newticket);
			tickets.add(ticket[1]);
			tickets.add("wrong ticket format");
			tickets.add("");
		} else {
			throw new IllegalArgumentException(
					"Error occured while getting user ticket. Error code: "
							+ ticket[0]);
		}

		conn.writeData(tickets.get(0), "");
	}

    @AfterClass
    public static void clean() {
        System.gc();
    }

	@Test
	public void testGetTicket() throws WWPassProtocolException, UnsupportedEncodingException, IOException, InterruptedException {

		// Normal case
		String ticket = conn.getTicket();
		String[] newTicket = testUtils.authenticateTicket(ticket).split(" ", 2);
		if ("200".equals(newTicket[0])) {
			try {
				conn.getPUID(ticket);
			} catch (WWPassProtocolException e) {
				fail("Expected no exception, but catch " + e.getMessage());
			}
		} else {
			throw new IllegalArgumentException(
					"Error occured while getting user ticket. Error code: "
							+ newTicket[0] + ". Message: " + newTicket[1]);
		}

		// Custom TTL case
		ticket = conn.getTicket(3);
		Thread.sleep(5000);
		newTicket = testUtils.authenticateTicket(ticket).split(" ", 2);
		assertTrue("Expected error code 400, but returned: " + newTicket[0],
					"400".equals(newTicket[0]));

		// Custom TTL and "p" auth_type case
		ticket =conn.getTicket("p", 3);
		Thread.sleep(3000);
		newTicket = testUtils.authenticateTicketWithP(ticket).split(" ", 2);
		assertTrue("Expected error code 400, but returned: " + newTicket[0],
				"400".equals(newTicket[0]));

		// "p" auth_type case
		ticket = conn.getTicket("p");
		newTicket = testUtils.authenticateTicketWithP(ticket).split(" ", 2);
		if ("200".equals(newTicket[0])) {
			try {
				conn.getPUID(ticket);
			} catch (WWPassProtocolException e) {
				fail("Expected no exception, but catch " + e.getMessage());
			}
		} else {
			throw new IllegalArgumentException(
					"Error occured while getting user ticket. Error code: "
							+ newTicket[0] + ". Message: " + newTicket[1]);
		}
		//Thread.sleep(3000);
	}

	@Test
	public void testPutTicket() throws WWPassProtocolException, UnsupportedEncodingException, IOException, InterruptedException {

		try {

			// Invalid ticket case
			try {
				conn.putTicket(tickets.get(1));
				fail("Expected an WWPassProtocolException with message \"Invalid or timed out ticket\"");
			} catch (WWPassProtocolException e) {
				assertTrue("Expected message: \"Invalid or timed out ticket\", actual message: " + e.getMessage(),
						e.getMessage().contains("Invalid or timed out ticket"));
			}

            // Normal usage case
            try {
                conn.putTicket(tickets.get(0));
            } catch (WWPassProtocolException e) {
                fail("Expected no exception, but catched: " + e.getMessage());
            }

			// Custom TTL case
			String[] userTicket = testUtils.getTicket().split(" ", 2);
			if ("200".equals(userTicket[0])) {
				String ticketTTL = conn.putTicket(userTicket[1], 3);
				Thread.sleep(5000);
				try {
					conn.getPUID(ticketTTL);
					fail("Expected an WWPassProtocolException with message \"Nonexistent or timed out ticket\"");
				} catch (WWPassProtocolException e) {
					assertTrue("Expected message: \"Nonexistent or timed out ticket\", actual message: " + e.getMessage(),
							e.getMessage().contains("Nonexistent or timed out ticket")
							|| e.getMessage().contains("Invalid ticket"));
				}
			} else {
				throw new IllegalArgumentException("Error occured while getting user ticket. Error code: "
						+ userTicket[0] + ". Message: " + userTicket[1]);
			}

			// Custom TTL and "p" auth_type case
			userTicket = testUtils.getTicketWithP().split(" ", 2);
			if ("200".equals(userTicket[0])) {
				String ticketTTL = conn.putTicket(userTicket[1], "p", 3);
				Thread.sleep(5000);
				try {
					conn.getPUID(ticketTTL);
					fail("Expected an WWPassProtocolException with message \"Nonexistent or timed out ticket\"");
				} catch (WWPassProtocolException e) {
					assertTrue("Expected message: \"Nonexistent or timed out ticket\", actual message: " + e.getMessage(),
							e.getMessage().contains("Nonexistent or timed out ticket")
							|| e.getMessage().contains("Invalid ticket"));
				}
			} else {
				throw new IllegalArgumentException("Error occured while getting user ticket. Error code: "
						+ userTicket[0] + ". Message: " + userTicket[1]);
			}

			// "p" auth_type case
			userTicket = testUtils.getTicketWithP().split(" ", 2);
			if ("200".equals(userTicket[0])) {
				String ticketP = conn.putTicket(userTicket[1], "p");
				try {
					conn.getPUID(ticketP);
				} catch (WWPassProtocolException e) {
					fail("Expected no exception, but catched: " + e.getMessage());
				}
			} else {
				throw new IllegalArgumentException("Error occured while getting user ticket. Error code: "
						+ userTicket[0] + ". Message: " + userTicket[1]);
			}

		} catch (WWPassProtocolException e) {
			fail("Expected no exception while putting valid ticket, but catched: " + e.getMessage());
		}

	}
	@Test
	public void testGetPuid() throws UnsupportedEncodingException, IOException, WWPassProtocolException, InterruptedException {

		// Normal behavior
		String puid = conn.getPUID(tickets.get(0));
		assertArrayEquals("PUID is not as expected",
					"4006fb737838a514d7fcacae94f8968b".getBytes(),
					puid.getBytes());

		// Invalid ticket
		try {
			puid = conn.getPUID(tickets.get(1));
			fail("Expected an WWPassProtocolException with message \"Invalid or timed out ticket\"");
		} catch (WWPassProtocolException e) {
			assertTrue("Expected message: \"Invalid or timed out ticket\", actual message: " + e.getMessage(),
					e.getMessage().contains("Invalid or timed out ticket"));
		}

		// Invalid ticket format
		try {
			puid = conn.getPUID(tickets.get(2));
			fail("Expected an WWPassProtocolException with message \"Invalid ticket format\"");
		} catch (WWPassProtocolException e) {
			assertTrue("Expected message: \"Invalid ticket format\", actual message: " + e.getMessage(),
					e.getMessage().contains("Invalid ticket format"));
		}

		// Empty ticket
		try {
			puid = conn.getPUID(tickets.get(3));
			fail("Expected an WWPassProtocolException with message \"Invalid ticket format\"");
		} catch (WWPassProtocolException e) {
			assertTrue("Expected message: \"Invalid ticket format\", actual message: " + e.getMessage(),
					e.getMessage().contains("Invalid ticket format"));
		}

		// *************************** //
		//   Testing "p" auth_type     //
		// *************************** //

		// Normal behavior
		String[] ticketP = testUtils.getTicketWithP().split(" ", 2);
		if ("200".equals(ticketP[0])) {
			puid = conn.getPUID(ticketP[1], "p");
			assertArrayEquals("PUID is not as expected",
					"4006fb737838a514d7fcacae94f8968b".getBytes(), puid.getBytes());
		} else {
			throw new IllegalArgumentException(
					"Error occured while getting user ticket. Error code: "
							+ ticketP[0] + ". Message: " + ticketP[1]);
		}

		// Ticket was authorized with less factors than requested
		try {
			conn.getPUID(tickets.get(0), "p");
			fail("Expected an WWPassProtocolException with message \"Ticket was authorized with less factors than requested\"");
		} catch (WWPassProtocolException e) {
			assertTrue("Expected message: \"Ticket was authorized with less factors than requested\", actual message: "	+ e.getMessage(),
						e.getMessage().contains("Ticket was authorized with less factors than requested"));
		}
	}

	@Test
	public void testReadDataAsString() throws WWPassProtocolException, UnsupportedEncodingException, IOException {

		// Read data from empty container case
		String data = conn.readDataAsString(tickets.get(0));
		assertArrayEquals("Expected an empty string, but returned: " + data.getBytes(),
							"".getBytes(),
							data.getBytes());

		conn.writeData(tickets.get(0), BYTE_DATA);
		data = conn.readDataAsString(tickets.get(0));
		assertNotEquals("Expected that returned data not equals to written", BYTE_DATA, data.getBytes());

		conn.writeData(tickets.get(0), "test data", "string");
		data = conn.readDataAsString(tickets.get(0), "string");
		assertArrayEquals("Expected that written and readed data are equal", "test data".toCharArray(), data.toCharArray());

	}

	@Test
	public void testReadWriteData() throws UnsupportedEncodingException,
			IOException, WWPassProtocolException, DecoderException {

		InputStream dataIs;
		BufferedInputStream bis = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

		// Invalid ticket read case
		try {
			conn.readData(tickets.get(1));
			fail("Expected an WWPassProtocolException with message \"Invalid ticket URL\"");
		} catch (WWPassProtocolException e) {
			assertTrue("Expected message: \"Invalid ticket URL\", actual message: " + e.getMessage(),
					e.getMessage().contains("Invalid ticket URL"));
		}

		// Invalid ticket format read case
		try {
			conn.readData(tickets.get(2));
			fail("Expected an WWPassProtocolException with message \"Invalid ticket format\"");
		} catch (WWPassProtocolException e) {
			assertTrue("Expected message: \"Invalid ticket format\", actual message: " + e.getMessage(),
					e.getMessage().contains("Invalid ticket format"));
		}
		// Invalid ticket write case
		try {
			conn.writeData(tickets.get(1), "invalid ticket case");
			fail("Expected an WWPassProtocolException with message \"Invalid ticket URL\"");
		} catch (WWPassProtocolException e) {
			assertTrue("Expected message: \"Invalid ticket URL\", actual message: " + e.getMessage(),
						e.getMessage().contains("Invalid ticket URL"));
		}
		// Write and read string data case
		conn.writeData(tickets.get(0), "test data");
		dataIs = conn.readData(tickets.get(0));

        try {
	        bis = new BufferedInputStream(dataIs);

	        byte[] buffer = new byte[8192];
		    int bytesRead;

		    while ((bytesRead = bis.read(buffer)) != -1)
		    {
		    	baos.write(buffer, 0, bytesRead);
		    }
		    assertArrayEquals("Expected an empty string", "test data".getBytes(), baos.toByteArray());
        } finally {
        	bis.close();
        	baos.reset();
        	baos.close();
        }

		// Write and read byte data (small size)
		conn.writeData(tickets.get(0), BYTE_DATA, "testbyte");
		dataIs = conn.readData(tickets.get(0), "testbyte");

        try {
	        bis = new BufferedInputStream(dataIs);

	        byte[] buffer = new byte[8192];
		    int bytesRead;

		    while ((bytesRead = bis.read(buffer)) != -1)
		    {
		    	baos.write(buffer, 0, bytesRead);
		    }
		    assertArrayEquals("Expected an non string data", BYTE_DATA, baos.toByteArray());
        } finally {
        	bis.close();
        	baos.reset();
        	baos.close();
        }

		// Write and read byte data (large size)
	    conn.writeData(tickets.get(0), imgData);
	    dataIs = conn.readData(tickets.get(0));
        try {
	        bis = new BufferedInputStream(dataIs);

	        byte[] buffer = new byte[8192];
		    int bytesRead;

		    while ((bytesRead = bis.read(buffer)) != -1)
		    {
		    	baos.write(buffer, 0, bytesRead);
		    }
		    assertArrayEquals("Expected an image represented as bytes array data", imgData,	baos.toByteArray());
        } finally {
        	bis.close();
        	baos.reset();
        	baos.close();
        }

		// Valid ticket/container doesn't exist case
		dataIs = conn.readData(tickets.get(0), "doesn't exist");
        try {
	        bis = new BufferedInputStream(dataIs);

	        byte[] buffer = new byte[8192];
		    int bytesRead;

		    while ((bytesRead = bis.read(buffer)) != -1)
		    {
		    	baos.write(buffer, 0, bytesRead);
		    }
		    assertArrayEquals("Expected \"None\", but returned: " + new String(baos.toByteArray()),
					"None".getBytes(),	baos.toByteArray());
        } finally {
        	bis.close();
        	baos.reset();
        	baos.close();
        }

		// Valid container/valid ticket case
		conn.writeData(tickets.get(0), "test data", "test");
		dataIs = conn.readData(tickets.get(0), "test");
        try {
	        bis = new BufferedInputStream(dataIs);

	        byte[] buffer = new byte[8192];
		    int bytesRead;

		    while ((bytesRead = bis.read(buffer)) != -1)
		    {
		    	baos.write(buffer, 0, bytesRead);
		    }
		    assertArrayEquals("Expected \"test data\", but returned: " + new String(baos.toByteArray()),
		    		"test data".getBytes(),	baos.toByteArray());
        } finally {
        	bis.close();
        	baos.reset();
        	baos.close();
        }

		// Invalid container/valid ticket case
		try {
			conn.readData(tickets.get(0), "0123456789ABCDEFG");
			fail("Expected an WWPassProtocolException with message \"Container ID too long\"");
		} catch (WWPassProtocolException e) {
			assertTrue("Expected message: \"Container ID too long\", actual message: " + e.getMessage(),
						e.getMessage().contains("Container ID too long"));
		}

		// Valid ticket/invalid container case
		try {
			conn.writeData(tickets.get(0), "test data", "0123456789ABCDEFG");
			fail("Expected an WWPassProtocolException with message \"Container ID too long\"");
		} catch (WWPassProtocolException e) {
			assertTrue("Expected message: \"Container ID too long\", actual message: "	+ e.getMessage(),
						e.getMessage().contains("Container ID too long"));
		}

	}

	@Test
	public void testLockAndUnlock() throws UnsupportedEncodingException,
			IOException, WWPassProtocolException, InterruptedException {

		// Invalid ticket read case
		try {
			conn.lock(tickets.get(1), 1);
			fail("Expected an WWPassProtocolException with message \"Invalid or timed out ticket\"");
		} catch (WWPassProtocolException e) {
			assertTrue("Expected message: \"Invalid or timed out ticket\", actual message: " + e.getMessage(),
					e.getMessage().contains("Invalid ticket URL"));
		}

		// lockTimeout == 0 case
		try {
			conn.lock(tickets.get(0), 0);
		} catch (WWPassProtocolException e) {
			assertFalse("Expecting no exception, but catched the WWPassProtocolException with message: " + e.getMessage(),
						e.getMessage().contains("Already locked"));
		}

		// Getting "Already locked" error
		try {
			conn.lock(tickets.get(0), 10);
			conn.lock(tickets.get(0), 10);
			fail("Expected an WWPassProtocolException with message \"Already locked\"");
		} catch (WWPassProtocolException e) {
			assertTrue("Expected message: \"Already locked\", actual message: "	+ e.getMessage(),
					e.getMessage().contains("Already locked"));
		}

		// Getting "Not locked" error
		conn.unlock(tickets.get(0));
		try {
			conn.unlock(tickets.get(0));
			fail("Expected an WWPassProtocolException with message \"Not locked\"");
		} catch (WWPassProtocolException e) {
			assertTrue("Expected message: \"Not locked\", actual message: "	+ e.getMessage(),
					e.getMessage().contains("Not locked"));
		}

		// Checking that lock is released after timeout
		try {
			conn.lock(tickets.get(0), 5);
			Thread.sleep(5000);
			conn.lock(tickets.get(0), 1);
		} catch (WWPassProtocolException e) {
			assertFalse("Expecting no exception, but catched the WWPassProtocolException with message \"Already locked\".",
						e.getMessage().contains("Already locked"));
		}

		try {
			conn.lock(tickets.get(0), 1, "test_lock");
			conn.unlock(tickets.get(0), "test_lock");
		} catch (WWPassProtocolException e) {
			fail("Expected no exception, but catched: " + e.getMessage());
		}
	}

	@Test
	public void testGetName() throws UnsupportedEncodingException,
			IOException, WWPassProtocolException {

		String name = conn.getName();
		assertArrayEquals("Excpected name \"SDK%20Test\", but actual value is \"" + name + "\".",
							"SDK%20Test".getBytes(),
							name.getBytes());
	}

	@Test
	public void testWriteReadLockUnlock()
			throws WWPassProtocolException, UnsupportedEncodingException,
					IOException, InterruptedException {
		conn.writeData(tickets.get(0), "");

		conn.readDataAndLock(tickets.get(0), 2);
		try {
			conn.lock(tickets.get(0), 1);
			fail("Expected WWPassProtocolException with message \"Already locked\"");
		} catch(WWPassProtocolException e) {
			assertTrue("Expected message: \"Already locked\", actual message: "	+ e.getMessage(),
					e.getMessage().contains("Already locked"));
		}
		Thread.sleep(2000);

		conn.lock(tickets.get(0), 2);
		try {
			conn.writeDataAndUnlock(tickets.get(0), BYTE_DATA);
		} catch (WWPassProtocolException e) {
			fail("Expected no exception, but catched: " + e.getMessage());
		}
		try {
			conn.unlock(tickets.get(0));
			fail("Expected an WWPassProtocolException with message \"Not locked\"");
		} catch (WWPassProtocolException e) {
			assertTrue("Expected message: \"Not locked\", actual message: "	+ e.getMessage(),
					e.getMessage().contains("Not locked"));
		}

		conn.writeData(tickets.get(0), "test data", "lock_string");
		conn.readDataAndLock(tickets.get(0), "lock_string", 2);
		try {
			conn.writeDataAndUnlock(tickets.get(0), BYTE_DATA, "lock_string");
		} catch (WWPassProtocolException e) {
			fail("Expected no exception, but catched: " + e.getMessage());
		}
		try {
			conn.unlock(tickets.get(0));
			fail("Expected an WWPassProtocolException with message \"Not locked\"");
		} catch (WWPassProtocolException e) {
			assertTrue("Expected message: \"Not locked\", actual message: "	+ e.getMessage(),
					e.getMessage().contains("Not locked"));
		}
	}

	// Functions to work with SP-only containers

	@Test
	public void testCreatePFID() throws UnsupportedEncodingException,
			IOException, WWPassProtocolException {
		byte[] pfid1 = conn.createPFID();
		byte[] pfid2 = conn.createPFID("test data");

		//String data1 = conn.readDataSP(pfid1);
		//assertArrayEquals("Expected an empty string, but recieved: " + data1, "".getBytes(), data1.getBytes());
		InputStream dataIs = conn.readDataSP(pfid1);
		BufferedInputStream bis = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        try {
	        bis = new BufferedInputStream(dataIs);

	        byte[] buffer = new byte[8192];
		    int bytesRead;

		    while ((bytesRead = bis.read(buffer)) != -1)
		    {
		    	baos.write(buffer, 0, bytesRead);
		    }
		    assertArrayEquals("Expected an empty string, but recieved: " + new String(baos.toByteArray()),
		    					"".getBytes(), baos.toByteArray());
        } finally {
        	bis.close();
        	baos.close();
        }

		//String data2  = conn.readDataSP(pfid2);
		//assertArrayEquals("Excpected \"test data\", but recieved: " + data2, "test data".getBytes(), data2.getBytes());
		dataIs = conn.readDataSP(pfid2);
        try {
	        bis = new BufferedInputStream(dataIs);

	        byte[] buffer = new byte[8192];
		    int bytesRead;

		    while ((bytesRead = bis.read(buffer)) != -1)
		    {
		    	baos.write(buffer, 0, bytesRead);
		    }
		    assertArrayEquals("Excpected \"test data\", but recieved: " + new String(baos.toByteArray()),
		    					"test data".getBytes(), baos.toByteArray());
        } finally {
        	bis.close();
        	baos.close();
        }

        conn.readDataSPandLock(pfid2, 5);
        try {
            conn.lockSP(pfid2, 5);
            fail("Ecpected WWPassProtocolException with message \"Already locked\"");
        } catch (WWPassProtocolException e) {
            assertTrue("Expected message: \"Already locked\", actual message: "	+ e.getMessage(),
                    e.getMessage().contains("Already locked"));
        }
        conn.writeDataSPandUnlock(pfid2, "test data two");
        try {
            conn.unlockSP(pfid2);
            fail("Expected WWPassProtocolException with message \"Not locked\"");
        } catch (WWPassProtocolException e) {
            assertTrue("Expected message: \"Not locked\", actual message: "	+ e.getMessage(),
                    e.getMessage().contains("Not locked"));
        }

		try {
			conn.removePFID(pfid1);
			conn.removePFID(pfid2);
		} catch (WWPassProtocolException e) {
			fail("Expecting no exception while removing test PFIDs, but catched the WWPassProtocolException with message: " + e.getMessage());
		}
	}

	@Test
	public void testRemovePFID() throws UnsupportedEncodingException, IOException {

		// Invalid PFID case
		try {
			conn.removePFID(BYTE_DATA);
			fail("Expected WWPassProtocolException with message \"Invalid or absent PFID\"");
		} catch (WWPassProtocolException e) {
			assertTrue("Expected message \"Invalid or absent PFID\", actual message: " + e.getMessage(),
						e.getMessage().contains("Invalid or absent PFID"));
		}

		// Normal usage case
		byte[] pfid = conn.createPFID();
		try {
			conn.removePFID(pfid);
		} catch (WWPassProtocolException e) {
			fail("Expecting no exception while removing test PFIDs, but catched the WWPassProtocolException with message: " + e.getMessage());
		}
		try {
			conn.removePFID(pfid);
			fail("Expected WWPassProtocolException with message \"ID does not exist\"");
		} catch (WWPassProtocolException e) {
			assertTrue("Expected message \"ID does not exist\", actual message: " + e.getMessage(),
						e.getMessage().contains("ID does not exist"));
		}
	}

	@Test
	public void testWriteDataSP() throws WWPassProtocolException, UnsupportedEncodingException, IOException {
		byte[] pfid = conn.createPFID();

		// Write and read string data case
		conn.writeDataSP(pfid, "test data");
		/*String response = conn.readDataSP(pfid);
		assertArrayEquals("Expected readed data \"test data\", actual readed data is " + response,
							"test data".getBytes(),
							response.getBytes());*/
		InputStream dataIs = conn.readDataSP(pfid);
		BufferedInputStream bis = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        try {
	        bis = new BufferedInputStream(dataIs);

	        byte[] buffer = new byte[8192];
		    int bytesRead;

		    while ((bytesRead = bis.read(buffer)) != -1)
		    {
		    	baos.write(buffer, 0, bytesRead);
		    }
		    assertArrayEquals("Expected readed data \"test data\", actual readed data is " + new String(baos.toByteArray()),
		    					"test data".getBytes(),
		    					baos.toByteArray());
        } finally {
        	bis.close();
        	baos.reset();
        	baos.close();
        }
		try {
			conn.removePFID(pfid);
		} catch (WWPassProtocolException e) {
			fail("Expecting no exception while removing test PFIDs, but catched the WWPassProtocolException with message: " + e.getMessage());
		}

		// Write and read byte data case
		pfid = conn.createPFID();
		conn.writeDataSP(pfid, BYTE_DATA);
/*		byte[] byteResponse = conn.readDataBytesSP(pfid);
		assertArrayEquals("Writed and readed byte data are not equals", BYTE_DATA, byteResponse);*/
		dataIs = conn.readDataSP(pfid);
        try {
	        bis = new BufferedInputStream(dataIs);

	        byte[] buffer = new byte[8192];
		    int bytesRead;

		    while ((bytesRead = bis.read(buffer)) != -1)
		    {
		    	baos.write(buffer, 0, bytesRead);
		    }
		    assertArrayEquals("Writed and readed byte data are not equals. Returned data: " + new String(baos.toByteArray()),
		    					BYTE_DATA,
		    					baos.toByteArray());
        } finally {
        	bis.close();
        	baos.reset();
        	baos.close();
        }
		try {
			conn.removePFID(pfid);
		} catch (WWPassProtocolException e) {
			fail("Expecting no exception while removing test PFIDs, but catched the WWPassProtocolException with message: " + e.getMessage());
		}

		// Write and read large byte data (JPG image) case
		pfid = conn.createPFID();
		conn.writeDataSP(pfid, imgData);
/*		byte[] newImgData = conn.readDataBytesSP(pfid);
		assertArrayEquals("Writed and readed byte data are not equals", imgData, newImgData);*/
		dataIs = conn.readDataSP(pfid);
        try {
	        bis = new BufferedInputStream(dataIs);

	        byte[] buffer = new byte[8192];
		    int bytesRead;

		    while ((bytesRead = bis.read(buffer)) != -1)
		    {
		    	baos.write(buffer, 0, bytesRead);
		    }
		    assertArrayEquals("Writed and readed byte data are not equals", imgData, baos.toByteArray());
        } finally {
        	bis.close();
        	baos.reset();
        	baos.close();
        }
		try {
			conn.removePFID(pfid);
		} catch (WWPassProtocolException e) {
			fail("Expecting no exception while removing test PFIDs, but catched the WWPassProtocolException with message: " + e.getMessage());
		}

        // Write and read String data case
        pfid = conn.createPFID("test data");
        String stringData = conn.readDataSPasString(pfid);
        assertArrayEquals("Expected that readed and writed string data are equal", "test data".getBytes(), stringData.getBytes());

        // Test for read String and lock SP
        conn.readDataSPasStringAndLock(pfid, 3);
        try{
            conn.lockSP(pfid, 1);
            fail("Expected WWPassProtocolException while trying to lock already locked SP container");
        } catch (WWPassProtocolException e) {
            assertTrue("Expected message: \"Already locked\", actual message: "	+ e.getMessage(),
                    e.getMessage().contains("Already locked"));
        }
        try {
            conn.removePFID(pfid);
        } catch (WWPassProtocolException e) {
            fail("Expecting no exception while removing test PFIDs, but catched the WWPassProtocolException with message: " + e.getMessage());
        }

	}

	@Test
	public void testLockAndUnlockSP() throws UnsupportedEncodingException,
			IOException, WWPassProtocolException, InterruptedException {

		// Custom lock timeout case
		byte[] pfid = conn.createPFID();
		try {
			conn.lockSP(pfid, 10);
			conn.lockSP(pfid, 10);
			fail("Expected an WWPassProtocolException with message \"Already locked\"");
		} catch (WWPassProtocolException e) {
			assertTrue("Expected message: \"Already locked\", actual message: "	+ e.getMessage(),
					e.getMessage().contains("Already locked"));
		}

		// Trying unlock doesn't lock container
		conn.unlockSP(pfid);
		try {
			conn.unlockSP(pfid);
			fail("Expected an WWPassProtocolException with message \"Not locked\"");
		} catch (WWPassProtocolException e) {
			assertTrue("Expected message: \"Not locked\", actual message: "	+ e.getMessage(),
					e.getMessage().contains("Not locked"));
		}

		// Checking that timeout expires
		try {
			conn.lockSP(pfid, 5);
			Thread.sleep(5000);
			conn.unlockSP(pfid);
		} catch (WWPassProtocolException e) {
			assertTrue("Expected message: \"Not locked\", actual message: "	+ e.getMessage(),
					e.getMessage().contains("Not locked"));
		}

		// Removing test data
		try {
			conn.removePFID(pfid);
		} catch (WWPassProtocolException e) {
			fail("Expecting no exception while removing test PFIDs, but catched the WWPassProtocolException with message: " + e.getMessage());
		}
	}
}


text:
import template from './desktopToolBar.html';
import controller from './desktopToolBar.controller';
import './desktopToolBar.scss';

let desktopToolBarComponent = {
  restrict: 'E',
  bindings: {},
  template,
  controller
};

export default desktopToolBarComponent;


text:
// Copyright 2016 Google Inc. 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.

package server

import (
	"fmt"
	"time"

	"github.com/google/trillian"
	"github.com/google/trillian/extension"
	"github.com/google/trillian/merkle"
	"github.com/google/trillian/merkle/hashers"
	"github.com/google/trillian/storage"
	"github.com/google/trillian/trees"

	"github.com/golang/glog"
	"github.com/golang/protobuf/ptypes/any"
	"golang.org/x/net/context"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

const (
	// Used internally by GetLeaves.
	mostRecentRevision = -1
)

// TODO(codingllama): There is no access control in the server yet and clients could easily modify
// any tree.

// TrillianMapServer implements the RPC API defined in the proto
type TrillianMapServer struct {
	registry extension.Registry
}

// NewTrillianMapServer creates a new RPC server backed by registry
func NewTrillianMapServer(registry extension.Registry) *TrillianMapServer {
	return &TrillianMapServer{registry}
}

// IsHealthy returns nil if the server is healthy, error otherwise.
func (t *TrillianMapServer) IsHealthy() error {
	return t.registry.MapStorage.CheckDatabaseAccessible(context.Background())
}

// Init creates the initial revision 0 SignedMapHead, if one doesn't already exist.
func (t *TrillianMapServer) Init(ctx context.Context, mapID int64) error {
	tree, hasher, err := t.getTreeAndHasher(ctx, mapID, false /* readonly */)
	if err != nil {
		return err
	}
	ctx = trees.NewContext(ctx, tree)

	tx, err := t.registry.MapStorage.BeginForTree(ctx, mapID)
	if err != storage.ErrMapNeedsInit && err != nil {
		return err
	}
	defer tx.Close()

	if err == nil {
		// Init() not needed.
		return nil
	}

	glog.V(2).Infof("%v: Need to init map root revision 0", mapID)

	// TODO(phad): Refactor the SetLeaves func to avoid the duplication that follows.
	smtWriter, err := merkle.NewSparseMerkleTreeWriter(
		ctx,
		mapID,
		0, /* write revision */
		hasher, func() (storage.TreeTX, error) {
			ttx, err := t.registry.MapStorage.BeginForTree(ctx, mapID)
			if err == storage.ErrMapNeedsInit {
				err = nil // Init() is ongoing, so ignore this.
			}
			return ttx, err
		})
	if err != nil {
		return err
	}

	rootHash, err := smtWriter.CalculateRoot()
	if err != nil {
		return fmt.Errorf("CalculateRoot(): %v", err)
	}

	rev0Root, err := t.makeSignedMapRoot(ctx, tree, time.Now(), rootHash, mapID, 0 /*revision*/, nil /* metadata */)
	if err != nil {
		return fmt.Errorf("makeSignedMapRoot(): %v", err)
	}

	if err = tx.StoreSignedMapRoot(ctx, *rev0Root); err != nil {
		return err
	}

	if err := tx.Commit(); err != nil {
		glog.Warningf("%v: Commit failed for SetLeaves: %v", mapID, err)
		return err
	}

	return nil
}

// GetLeaves implements the GetLeaves RPC method.  Each requested index will
// return an inclusion proof to either the leaf, or nil if the leaf does not
// exist.
func (t *TrillianMapServer) GetLeaves(ctx context.Context, req *trillian.GetMapLeavesRequest) (*trillian.GetMapLeavesResponse, error) {
	return t.getLeavesByRevision(ctx, req.MapId, req.Index, mostRecentRevision)
}

// GetLeavesByRevision implements the GetLeavesByRevision RPC method.
func (t *TrillianMapServer) GetLeavesByRevision(ctx context.Context, req *trillian.GetMapLeavesByRevisionRequest) (*trillian.GetMapLeavesResponse, error) {
	if req.Revision < 0 {
		return nil, fmt.Errorf("map revision %d must be >= 0", req.Revision)
	}
	return t.getLeavesByRevision(ctx, req.MapId, req.Index, req.Revision)
}

func (t *TrillianMapServer) getLeavesByRevision(ctx context.Context, mapID int64, indices [][]byte, revision int64) (*trillian.GetMapLeavesResponse, error) {
	if err := t.Init(ctx, mapID); err != nil {
		return nil, err
	}

	tree, hasher, err := t.getTreeAndHasher(ctx, mapID, true /* readonly */)
	if err != nil {
		return nil, fmt.Errorf("could not get map %v: %v", mapID, err)
	}
	ctx = trees.NewContext(ctx, tree)

	tx, err := t.registry.MapStorage.SnapshotForTree(ctx, mapID)
	if err != nil {
		return nil, fmt.Errorf("could not create database snapshot: %v", err)
	}
	defer tx.Close()

	var root *trillian.SignedMapRoot
	if revision < 0 {
		// need to know the newest published revision
		r, err := tx.LatestSignedMapRoot(ctx)
		if err != nil {
			return nil, fmt.Errorf("could not fetch the latest SignedMapRoot: %v", err)
		}
		root = &r
	} else {
		r, err := tx.GetSignedMapRoot(ctx, revision)
		if err != nil {
			return nil, fmt.Errorf("could not fetch SignedMapRoot %v: %v", revision, err)
		}
		root = &r
	}

	smtReader := merkle.NewSparseMerkleTreeReader(root.MapRevision, hasher, tx)

	inclusions := make([]*trillian.MapLeafInclusion, 0, len(indices))
	found := 0
	for _, index := range indices {
		if got, want := len(index), hasher.Size(); got != want {
			return nil, status.Errorf(codes.InvalidArgument,
				"index len(%x): %v, want %v", index, got, want)
		}
		// Fetch the leaf if it exists.
		leaves, err := tx.Get(ctx, root.MapRevision, [][]byte{index})
		if err != nil {
			return nil, fmt.Errorf("could not fetch leaf %x: %v", index, err)
		}
		var leaf *trillian.MapLeaf
		if len(leaves) == 1 {
			leaf = &leaves[0]
			found++
		} else {
			// Empty leaf for proof of non-existence.
			leafHash, err := hasher.HashLeaf(mapID, index, nil)
			if err != nil {
				return nil, fmt.Errorf("HashLeaf(nil): %v", err)
			}
			leaf = &trillian.MapLeaf{
				Index:     index,
				LeafValue: nil,
				LeafHash:  leafHash,
			}
		}

		// Fetch the proof regardless of whether the leaf exists.
		proof, err := smtReader.InclusionProof(ctx, root.MapRevision, index)
		if err != nil {
			return nil, fmt.Errorf("could not get inclusion proof for leaf %x: %v", index, err)
		}

		inclusions = append(inclusions, &trillian.MapLeafInclusion{
			Leaf:      leaf,
			Inclusion: proof,
		})
	}
	glog.Infof("%v: wanted %v leaves, found %v", mapID, len(indices), found)

	if err := tx.Commit(); err != nil {
		return nil, fmt.Errorf("could not commit db transaction: %v", err)
	}

	return &trillian.GetMapLeavesResponse{
		MapLeafInclusion: inclusions,
		MapRoot:          root,
	}, nil
}

// SetLeaves implements the SetLeaves RPC method.
func (t *TrillianMapServer) SetLeaves(ctx context.Context, req *trillian.SetMapLeavesRequest) (*trillian.SetMapLeavesResponse, error) {
	mapID := req.MapId
	if err := t.Init(ctx, mapID); err != nil {
		return nil, err
	}

	tree, hasher, err := t.getTreeAndHasher(ctx, mapID, false /* readonly */)
	if err != nil {
		return nil, err
	}
	ctx = trees.NewContext(ctx, tree)

	tx, err := t.registry.MapStorage.BeginForTree(ctx, req.MapId)
	if err != nil {
		return nil, err
	}
	defer tx.Close()

	glog.V(2).Infof("%v: Writing at revision %v", mapID, tx.WriteRevision())
	smtWriter, err := merkle.NewSparseMerkleTreeWriter(
		ctx,
		req.MapId,
		tx.WriteRevision(),
		hasher, func() (storage.TreeTX, error) {
			return t.registry.MapStorage.BeginForTree(ctx, req.MapId)
		})
	if err != nil {
		return nil, err
	}

	for _, l := range req.Leaves {
		if got, want := len(l.Index), hasher.Size(); got != want {
			return nil, status.Errorf(codes.InvalidArgument,
				"len(%x): %v, want %v", l.Index, got, want)
		}
		if l.LeafValue == nil {
			// Leaves are empty by default. Do not allow clients to store
			// empty leaf values as this messes up the calculation of empty
			// branches.
			continue
		}
		// TODO(gbelvin) use LeafHash rather than computing here. #423
		leafHash, err := hasher.HashLeaf(mapID, l.Index, l.LeafValue)
		if err != nil {
			return nil, fmt.Errorf("HashLeaf(): %v", err)
		}
		l.LeafHash = leafHash

		if err = tx.Set(ctx, l.Index, *l); err != nil {
			return nil, err
		}
		if err = smtWriter.SetLeaves(ctx, []merkle.HashKeyValue{
			{
				HashedKey:   l.Index,
				HashedValue: l.LeafHash,
			},
		}); err != nil {
			return nil, err
		}
	}

	rootHash, err := smtWriter.CalculateRoot()
	if err != nil {
		return nil, fmt.Errorf("CalculateRoot(): %v", err)
	}

	newRoot, err := t.makeSignedMapRoot(ctx, tree, time.Now(), rootHash, req.MapId, tx.WriteRevision(), req.Metadata)
	if err != nil {
		return nil, fmt.Errorf("makeSignedMapRoot(): %v", err)
	}

	// TODO(al): need an smtWriter.Rollback() or similar I think.
	if err = tx.StoreSignedMapRoot(ctx, *newRoot); err != nil {
		return nil, err
	}

	if err := tx.Commit(); err != nil {
		glog.Warningf("%v: Commit failed for SetLeaves: %v", mapID, err)
		return nil, err
	}

	return &trillian.SetMapLeavesResponse{MapRoot: newRoot}, nil
}

func (t *TrillianMapServer) makeSignedMapRoot(ctx context.Context, tree *trillian.Tree, smrTs time.Time,
	rootHash []byte, mapID, revision int64, meta *any.Any) (*trillian.SignedMapRoot, error) {
	smr := &trillian.SignedMapRoot{
		TimestampNanos: smrTs.UnixNano(),
		RootHash:       rootHash,
		MapId:          mapID,
		MapRevision:    revision,
		Metadata:       meta,
	}
	signer, err := trees.Signer(ctx, tree)
	if err != nil {
		return nil, fmt.Errorf("trees.Signer(): %v", err)
	}
	sig, err := signer.SignMapRoot(smr)
	if err != nil {
		return nil, fmt.Errorf("SignMapRoot(): %v", err)
	}
	smr.Signature = sig
	return smr, nil
}

// GetSignedMapRoot implements the GetSignedMapRoot RPC method.
func (t *TrillianMapServer) GetSignedMapRoot(ctx context.Context, req *trillian.GetSignedMapRootRequest) (*trillian.GetSignedMapRootResponse, error) {
	if err := t.Init(ctx, req.MapId); err != nil {
		return nil, err
	}
	tx, err := t.registry.MapStorage.SnapshotForTree(ctx, req.MapId)
	if err != nil {
		return nil, err
	}
	defer tx.Close()

	r, err := tx.LatestSignedMapRoot(ctx)
	if err != nil {
		return nil, err
	}

	if err := tx.Commit(); err != nil {
		glog.Warningf("%v: Commit failed for GetSignedMapRoot: %v", req.MapId, err)
		return nil, err
	}

	return &trillian.GetSignedMapRootResponse{
		MapRoot: &r,
	}, nil
}

// GetSignedMapRootByRevision implements the GetSignedMapRootByRevision RPC
// method.
func (t *TrillianMapServer) GetSignedMapRootByRevision(ctx context.Context, req *trillian.GetSignedMapRootByRevisionRequest) (*trillian.GetSignedMapRootResponse, error) {
	if req.Revision < 0 {
		return nil, fmt.Errorf("map revision %d must be >= 0", req.Revision)
	}
	if err := t.Init(ctx, req.MapId); err != nil {
		return nil, err
	}
	tx, err := t.registry.MapStorage.SnapshotForTree(ctx, req.MapId)
	if err != nil {
		return nil, err
	}
	defer tx.Close()

	r, err := tx.GetSignedMapRoot(ctx, req.Revision)
	if err != nil {
		return nil, err
	}

	if err := tx.Commit(); err != nil {
		glog.Warningf("%v: Commit failed for GetSignedMapRootByRevision: %v", req.MapId, err)
		return nil, err
	}

	return &trillian.GetSignedMapRootResponse{
		MapRoot: &r,
	}, nil
}

func (t *TrillianMapServer) getTreeAndHasher(ctx context.Context, treeID int64, readonly bool) (*trillian.Tree, hashers.MapHasher, error) {
	tree, err := trees.GetTree(
		ctx,
		t.registry.AdminStorage,
		treeID,
		trees.GetOpts{TreeType: trillian.TreeType_MAP, Readonly: readonly})
	if err != nil {
		return nil, nil, err
	}
	th, err := hashers.NewMapHasher(tree.HashStrategy)
	if err != nil {
		return nil, nil, err
	}
	return tree, th, nil
}


text:
package edu.frostburg.computerclub.sdk.datastructures;

import edu.frostburg.computerclub.sdk.datastructures.List;
import java.util.NoSuchElementException;
import java.util.function.Supplier;
import org.junit.Ignore;
import org.junit.Test;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;

/**
 *
 * @author Kevin Raoofi
 */
@Ignore
public class ListTest {

    public final Supplier<List> listImpl;

    public ListTest(Supplier<List> listImpl) {
        this.listImpl = listImpl;
    }

    /**
     * Test of add method
     */
    @Test
    public void testAdd_GenericType() {
        System.out.println("add");

        Object e = "some value";

        List instance = listImpl.get();
        assertEquals(0, instance.size());

        instance.add(e);
        assertEquals(1, instance.size());
    }

    /**
     * Test of add method
     */
    @Test
    public void testAdd_int_GenericType() {
        System.out.println("add");
        int index = 0;
        Object e = "some value";

        List instance = listImpl.get();
        assertEquals(0, instance.size());

        instance.add(index, e);
        assertEquals(1, instance.size());
    }

    /**
     * Test of remove method
     */
    @Test
    public void testRemove_GenericType() {
        System.out.println("remove");
        Object e = "some value";
        List instance = listImpl.get();
        Object expResult = null;
        Object result = instance.remove(e);
        assertEquals(expResult, result);

        assertEquals(expResult, result);

        instance.add(e);
        result = instance.remove(e);

        assertEquals(e, result);
    }

    @Test(expected = NoSuchElementException.class)
    public void testRemove_intFail() {
        System.out.println("remove");

        List instance = listImpl.get();
        Object expResult = "some value";

        Object result;
        result = instance.remove(0);
    }

    /**
     * Test of remove method
     */
    @Test
    public void testRemove_int() {
        System.out.println("remove");

        List instance = listImpl.get();
        Object expResult = "some value";

        Object result;

        instance.add("obj1");
        instance.add("obj2");

        assertEquals("obj2", instance.remove(1));
    }

    /**
     * Test of set method
     */
    @Test(expected = NoSuchElementException.class)
    public void testSetFail() {
        System.out.println("set");
        int index = 0;
        Object e = "some value";
        List instance = listImpl.get();
        instance.set(index, e);
    }

    /**
     * Test of set method
     */
    @Test
    public void testSet() {
        System.out.println("set");

        Object e = "some value";
        List instance = listImpl.get();

        instance.add("another value");

        instance.set(0, e);
        assertEquals(e, instance.get(0));
    }

    /**
     * Test of get method
     */
    @Test
    public void testGet() {
        System.out.println("get");
        int index = 0;
        List instance = listImpl.get();
        Object obj1 = "some value";
        Object obj2 = "another value";

        instance.add(obj1);
        instance.add(obj2);

        assertEquals(obj1, instance.get(0));
        assertEquals(obj2, instance.get(1));
    }

    /**
     * Test of contains method
     */
    @Test
    public void testContains() {
        System.out.println("contains");
        Object e = "some value";
        List instance = listImpl.get();

        assertEquals(false, instance.contains(e));
        instance.add(e);
        assertEquals(true, instance.contains(e));
        instance.remove(e);
        assertEquals(false, instance.contains(e));
    }

    /**
     * Test of clear method
     */
    @Test
    public void testClear() {
        System.out.println("clear");
        List instance = listImpl.get();

        for (int i = 0; i < 20; i++) {
            instance.add(i);
        }

        assertEquals(20, instance.size());

        instance.clear();

        assertEquals(0, instance.size());
    }

    /**
     * Test of size method
     */
    @Test
    public void testSize() {
        System.out.println("size");
        List instance = listImpl.get();

        assertEquals(0, instance.size());

        instance.add(null);
        instance.add(null);
        instance.add(null);
        instance.add(null);

        assertEquals(4, instance.size());
    }

    /**
     * Test of iterator method
     */
    @Test
    public void testIterator() {
        System.out.println("iterator");
        final List<Integer> instance = listImpl.get();

        final int[] elements = new int[]{1, 2, 3, 4, 5};
        final int[] results = new int[elements.length];
        int index = 0;

        for (int i : elements) {
            instance.add(i);
        }

        for (int i : instance) {
            results[index++] = i;
        }

        assertArrayEquals(elements, results);
    }

}


text:
#include "limit.h"
#include <vector>

using namespace std;

struct Edge
{
	int u,v,c;
	double w1,w2;
};

class Graph
{
private:
	static bool built;
	static int n;
	static int m;
	static vector<int> degree;
	static vector<int> index;
	static vector<Edge> edges;

	static void qsort_edges(int h, int t);

public:
	static void	Build();
	static int	GetN();
	static int	GetM();
	static int	GetDegree(int node);
	static int	GetNeighbor(int node);
	static Edge	GetEdge(int node, int idx);
	static void BuildIC();
	static void BuildWC();
	static void Build2GC();
	static void Build2WC(char file[]);
	static void Build2DWC();
	static void Build2IC();
	static void Build2DIC(double, double);
	static void Build2RIC(double, double);
	static void Build2TIC(double, double, double);
	static void Build2DT(double, double, double);
	static void Build2T(double, double, double);
	static void Stats();
};

#endif


text:
[![Build Status](https://secure.travis-ci.org/agriffis/pure-getopt.png?branch=master)](http://travis-ci.org/agriffis/pure-getopt)

pure-getopt is a drop-in replacement for GNU getopt, implemented in pure
Bash compatible back to 2.05b. It makes no external calls and faithfully
reimplements GNU getopt features, including:

 * all three calling forms in the synopsis
 * all getopt options
 * matching error messages
 * matching return codes
 * proper handling of abbreviated long options
 * alternative parsing mode (long options with single dash)
 * GETOPT_COMPATIBLE flag
 * POSIXLY_CORRECT flag
 * leading + or - on options string

# How to use it

Cut and paste the entire function into your script, prior to calling
"getopt". Done! :-)

I don't recommend sourcing getopt.bash into your script. The problem is
that you've just traded a dependency on GNU getopt for a dependency on
pure-getopt. Most of the time it should be okay to just insert the function
directly in your script.

If you have a long script and you'd rather put the getopt function at the
bottom rather than the top, this pattern might be useful:

```bash
#!/bin/bash

main() {
    declare argv
    argv=$(getopt -o xy:z:: --long foo,bar:,baz:: -- "$@") || return
    eval "set -- $argv"

    declare a
    for a; do
        case $a in
            ...
        esac
    done
}

# INSERT getopt function here
getopt() {
    ...
}

# CALL main at very bottom, passing script args
main "$@"
```

# Differences between pure-getopt and GNU getopt

The only intentional divergences between pure-getopt and GNU getopt are
either inconsequential or due to bugs in GNU getopt:

 1. GNU getopt mishandles ambiguities in abbreviated long options, for
    example this doesn't produce an error message:

        getopt -o '' --long xy,xz -- --x

    but this does produce an error message:

        getopt -o '' --long xy,xz: -- --x

    Pure-getopt generates an error message in both cases, diverging from
    GNU getopt to fix this bug.

 2. In the case of an ambiguous long option with an argument, GNU getopt
    generates an error message that includes the argument:

        getopt: option '--x=foo' is ambiguous; possibilities: '--xy' '--xz'

    Pure-getopt considers this a bug in GNU getopt, since the value might
    be very long and inappropriate for printing to the screen, and since
    GNU getopt ordinarily omits the value in its error messages.
    Pure-getopt's error message in this case is:

        getopt: option '--x' is ambiguous; possibilities: '--xy' '--xz'

 3. Pure-getopt uses a different method of quoting the output. The result
    should be the same as GNU getopt when eval'd by the shell. If you find
    a case where it's different, please report it as a bug!

 4. Pure-getopt has a test suite; GNU getopt in util-linux does not.

# References

 * [getopt in util-linux](http://software.frodo.looijaard.name/getopt/)