cssAudio - Activefile-genericCSS - ActiveGeneric - ActiveHTML - ActiveImage - ActiveJS - ActiveSVG - ActiveText - Activefile-genericVideo - ActiveLovehtmlicon-new-collectionicon-personicon-teamlog-outoctocatpop-outspinnerstartv

Pen Settings

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

Quick-add: + add another resource

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

Quick-add: + add another resource

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

            
              <div ng-app="app">
  
  <script type="text/ng-template" id="default.html">

  <div class="ly-device ly-device-{{device.id}} ly-type-{{type.id}}" ng-class="{'ly-sensor': !hasFunctions}">

  <!-- ---------------- -->
  <!--  Device message  -->
  <!-- ---------------- -->

  <div class="ly-message ly-generic-message ly-animation-fast" ng-if="view.path=='/message'">
    <p class="ly-description">{{message.title}}</p>
    <p class="ly-description-sub">{{message.description}}</p>
  </div>

  <!-- ----------------- -->
  <!--  Loading message  -->
  <!-- ----------------- -->

  <div class="ly-message ly-loading" ng-if="view.path=='/loading'">
    <p class="ly-description">Loading Device</p>
    <div class="ly-spinner">
      <div class="ly-bounce-1"></div>
      <div class="ly-bounce-2"></div>
      <div class="ly-bounce-3"></div>
    </div>
  </div>

  <!-- ---------------- -->
  <!--  Device Content  -->
  <!-- ---------------- -->

  <div class="ly-content" ng-class="{'ly-animation': view.path=='/default'}" ng-if="view.path!='/loading' && view.path!='/message'">

    <!-- ------------- -->
    <!-- Device header -->
    <!-- ------------- -->

    <div class="ly-header">

      <!-- Name -->

      <p class="ly-name ly-device-name">{{device.name}}</p>

      <!-- Top menu -->

      <ul class="ly-menu">
        <li><a class="ly-menu-settings" href="" ng-click="showSettings()"></i> Settings</a></li>
      </ul>

    </div>

    <!-- Device status -->

    <div class="ly-status" ng-class="{'ly-status-disabled': !status.function}" ng-if="hasStatuses" ng-click="execute(status.function)">

      <!-- Default function button -->

      <div class="ly-execute">
        <button class="ly-button"></button>
        <div class="ly-spinner ly-animation" ng-if="device.pending">
          <div class="ly-bounce-1"></div>
          <div class="ly-bounce-2"></div>
          <div class="ly-bounce-3"></div>
        </div>
      </div>

      <!-- Description -->

      <div class="ly-description">
        <p class="ly-name ly-updated-animation">{{status.name || 'Undefined status'}}</p>
        <p class="ly-extras"><span am-time-ago="device.updated_at"></span> from {{device.updated_from}}</p>
      </div>
    </div>

    <!-- ---------------- -->
    <!-- Device functions -->
    <!-- ---------------- -->

    <div class="ly-functions" ng-if="hasFunctions">
      <div class="ly-function-container" ng-repeat="function in functions">
        <div class="ly-function ly-function-{{function.id}}" ng-click="execute(function)">

          <!-- Function button -->

          <div class="ly-execute">
            <button class="ly-button"></button>
            <p class="ly-name">{{function.name}}</p>
          </div>
        </div>

        <!-- Function form -->

        <div class="ly-function-form ly-modal ly-animation-fast" ng-if="function.visibleForm">
          <div class="ly-modal-backdrop" ng-click="function.visibleForm=false"></div>
          <div class="ly-modal-window">
            <a class="ly-close" href="" ng-click="function.visibleForm=false">×</a>
            <div class="ly-header">
              <p class="ly-name">{{function.name}}</p>
            </div>
            <form class="ly-form" ng-submit="execute(function)">
              <div class="ly-fields">
                <div class="ly-field ly-editable"
                  ng-repeat="property in function.properties"
                  ng-class="{'ly-no-underline': (property.type=='range' || property.type=='color') }"
                  ng-if="property.toFill">

                  <p class="ly-description">
                    <span>Set {{property.name | lowercase}}</span>
                    <span ng-if="property.type=='range'">to {{property.expected}}</span>
                  </p>

                  <input class="ly-value ly-{{property.type}}"
                    type="{{property.type}}"
                    min="{{property.range.min}}"
                    max="{{property.range.max}}"
                    step="{{property.range.step}}"
                    ng-if="property.accepted==null"
                    ng-model="property.expected">
                  </input>

                  <select class="ly-value"
                    ng-model="property.expected"
                    ng-options="key as value for (key , value) in property.accepted"
                    ng-if="property.accepted && property.type=='text'">
                  </select>

                </div>
              </div>
              <div class="ly-actions">
                <button class="ly-button ly-update-button" ng-click="execute(function)">Execute</button>
              </div>
            </form>
          </div>
        </div>

      </div>
    </div>

    <!-- ----------------- -->
    <!-- Device properties -->
    <!-- ----------------- -->

    <div class="ly-properties">
      <div class="ly-property ly-property-{{property.id}}" ng-repeat="property in device.properties">
        <button class="ly-button"></button>
        <p class="ly-description">
          <span class="ly-name">{{property.name}}</span>
          <span class="ly-value" ng-if="property.type!='password'">
            <span>{{property.expected}}</span>
            <span class="ly-color-ball" style="border-color:{{property.expected}}" ng-if="property.type=='color'"></span>
          </span>
          <span class="ly-value" ng-if="property.type=='password'" style="padding-top:1em">********</span>
        </p>
      </div>
    </div>

    <!-- ----------------- -->
    <!--  Device Settings  -->
    <!-- ----------------- -->

    <div class="ly-settings ly-modal ly-animation-fast" ng-if="view.path=='/settings'">
      <div class="ly-modal-backdrop" ng-click="showDefault() || resetForm()"></div>
      <div class="ly-modal-window">
        <a class="ly-close" href="" ng-click="showDefault() || resetForm()">×</a>

        <div class="ly-settings-device">
          <div class="ly-header">
            <p class="ly-name">Settings</p>
          </div>

          <div class="ly-subheader">
            <p class="ly-name">Device details</p>
          </div>

          <form class="ly-form" name="form" ng-submit="form.$valid && update()">
            <div class="ly-fields">
              <div class="ly-field">
                <p class="ly-description">ID {{form.$valid}}</p>
                <input class="ly-value ly-id" type="text" disabled ng-model="device.id"></input>
              </div>
              <div class="ly-field" ng-show="isMaker()">
                <p class="ly-description">Secret</p>
                <input class="ly-value ly-secret" type="text" disabled ng-model="privates.secret"></input>
              </div>
              <div class="ly-field">
                <p class="ly-description">Type</p>
                <p class="ly-value">
                <a class="ly-type-value" href="http://types.lelylan.com/types/{{type.id}}" target="blank">{{type.name}}</a>
                </p>
              </div>
            </div>
          </form>

          <div class="ly-subheader">
            <p class="ly-name">Device info</p>
          </div>

          <form class="ly-form" name="form" ng-submit="form.$valid && update()">
            <div class="ly-fields">
              <div class="ly-field ly-editable">
                <p class="ly-description" ng-class="{'ly-error': form.name.$error.required}">Name (required)</p>
                <input class="ly-value ly-name" type="text" placeholder="Device name" name="name" ng-model="device.name" required></input>
              </div>
              <div class="ly-field ly-editable" ng-show="isMaker()">
                <p class="ly-description">Physical URI <a href="http://dev.lelylan.com/makers#mqtt-set-the-physical-uri" target="blank">(what is this?)</a></p>
                <input class="ly-value ly-physical" type="text" placeholder="http://nodes.lelylan.com/mqtt/devices/1" ng-model="device.physical.uri"></input>
              </div>
            </div>
            <div class="ly-actions">
              <button class="ly-button ly-update-button" ng-class="{'ly-disabled-button': !form.$valid}">save</button>
              <div class="ly-delete-action" ng-show="isMaker()">
                <a class="ly-delete-link" href="" ng-click="view.path='/delete'">Delete your device?</a>
              </div>
            </div>
          </form>
        </div>
      </div>
    </div>

    <!-- ------------- -->
    <!-- Device Delete -->
    <!-- ------------- -->

    <div class="ly-settings ly-delete ly-modal ly-animation-fast" ng-if="view.path=='/delete'">
      <div class="ly-modal-backdrop" ng-click="showDefault()"></div>
      <div class="ly-modal-window">
        <a class="ly-close" href="" ng-click="showDefault()">×</a>
        <div class="ly-settings-delete">
          <div class="ly-header">
            <p class="ly-name">Delete</p>
          </div>
          <form class="ly-form" ng-submit="destroy(confirm)">
            <div>
              <p class="ly-info">
              Deleting your device is irreversible.
              Enter the device name to confirm you want to permanently delete it.
              </p>
            </div>

            <div class="ly-field ly-editable">
              <input class="ly-value ly-name" type="text" autofocus placeholder="Device name" ng-model="confirm"></input>
            </div>
            <div class="ly-actions">
              <button class="ly-button ly-delete-button" ng-class="{'ly-disabled-button': confirm!=device.name}">delete</button>
            </div>
          </form>
        </div>
      </div>

    </div>
  </div>
  </div>

  </script>

  <device device-id="1" device-template="default.html"></device>

</div>
            
          
!
            
              /*
 * Fonts
 */

@font-face {
  font-family: 'Lato';
}

/*
 * Device component reset
 */

.ly-device * {
  font-size: 1em;
  font-family: "Lato", "Helvetica Neue", sans-serif;
  background: none repeat scroll 0 0 transparent;
  border: medium none;
  border-spacing: 0;
  font-size: 1em;
  font-weight: 100;
  list-style: none outside none;
  margin: 0;
  padding: 0;
  text-align: left;
  text-decoration: none;
  text-indent: 0;
  line-height: 1.2em;
}

.ly-device .fa {
  font-family: FontAwesome;
}


/*
 * Device generics
 */

.ly-device {
  font-family: "Lato", "Helvetica Neue", sans-serif;
  background-color: #fff;
  color: #333;
  border: 1px solid #eee;
  position: relative;
}

.ly-device p {
  margin: 0;
}

.ly-device .ly-error {
  color: #FF7373 !important;
}



/*
 * Links
 */

.ly-device a {
  text-decoration: none;
  color: #239cbb
}


/*
 * Buttons
 */

.ly-device .ly-modal .ly-button {
  background-color: #239cbb;
  padding: 0.6em 0;
  cursor: pointer;
  color: #fff;
  text-align: center;
}

.ly-device .ly-button {
  cursor: pointer;
}


/*
 * Link and button transitions
 */

.ly-device .ly-button,
.ly-device a {
  -webkit-transition: color 0.3s ease, text-decoration 0.3s ease, background 0.3s ease;
  -moz-transition: color 0.3s ease, text-decoration 0.3s ease, background 0.3s ease;
  transition: color 0.3s ease, text-decoration 0.3s ease, background 0.3s ease;
}


/*
 * Device spinner
 */

.ly-device .ly-spinner > div {
  width: .5em;
  height: .5em;
  background-color: #bbb;
  border-radius: 100%;
  display: inline-block;

  -webkit-animation: bouncedelay 1.4s infinite ease-in-out;
  animation: bouncedelay 1.4s infinite ease-in-out;
  -webkit-animation-fill-mode: both;
  animation-fill-mode: both;
}

.ly-device .ly-spinner .ly-bounce-1 {
  -webkit-animation-delay: -0.32s;
  animation-delay: -0.32s;
}

.ly-device .ly-spinner .ly-bounce-2 {
  -webkit-animation-delay: -0.16s;
  animation-delay: -0.16s;
}

@-webkit-keyframes bouncedelay {
  0%, 80%, 100% { -webkit-transform: scale(0.0) }
  40% { -webkit-transform: scale(1.0) }
}

@keyframes bouncedelay {
  0%, 80%, 100% {
    transform: scale(0.0);
    -webkit-transform: scale(0.0);
  } 40% {
    transform: scale(1.0);
    -webkit-transform: scale(1.0);
  }
}



/*
 * Device loading section
 */

.ly-device .ly-message {
  /*background-color: #d5d5d5;*/
  padding: 1em;
  color: #aaa;
}

.ly-device .ly-message .ly-spinner,
.ly-device .ly-message .ly-description,
.ly-device .ly-message .ly-description-sub {
  text-align: center
}

.ly-device .ly-message .ly-description {
  font-size: 1.4em;
}


/*
 * Header section
 */

.ly-device .ly-header {
  /*background-color: #ddd;*/
}

.ly-device .ly-header {
  padding: 0.6em;
  border-bottom: 1px solid #f8f8f8;
}

.ly-device .ly-header .ly-name {
  font-size: 2em;
}

.ly-device .ly-header .ly-menu {
  padding-left: 0;
  padding-right: 0;
  list-style: none;
  display: inline;
}

.ly-device .ly-header .ly-menu li {
  display: inline;
  padding-right: 0.4em;
}

.ly-device .ly-header .ly-menu li a {
  font-size: 1em;
  text-decoration: none;
}


/*
 * Status section
 */

.ly-device .ly-status {
  /*background-color: #e5e5e5;*/
}

.ly-device .ly-status {
  padding: 1em 0.6em;
  border-bottom: 1px solid #f8f8f8;
  cursor: pointer;
}

.ly-device .ly-status .ly-execute {
  position: relative;
  float: left;
}

.ly-device .ly-status .ly-execute .ly-button {
  float: left;
  width: 2.8em;
  height: 2.8em;
  border: 3px solid #239cbb;
  border-radius: 100%;
}

.ly-device .ly-status .ly-execute .ly-spinner {
  position: absolute;
  top: 0.6em;
  left: 0.7em;
}

.ly-device .ly-status .ly-execute .ly-spinner > div {
  width: .25em;
  height: .25em;
  background-color: #239cbb;
}

.ly-device .ly-status .ly-description {
  margin-left: 3.5em;
}

.ly-device .ly-status .ly-description .ly-name {
  font-size: 1.7em;
  color: #239cbb;
  line-height: 1.05em;
}

.ly-device .ly-status .ly-description .ly-extras {
  font-size: 0.9em;
  color: #888;
}

.ly-device .ly-status-disabled {
  cursor: default;
}

.ly-device .ly-status-disabled .ly-description .ly-name {
  color: #666;
}

.ly-device .ly-status-disabled .ly-execute .ly-button {
  border: 2px solid #aaa;
  cursor: default !important;
}


/*
 * Function
 */

.ly-device .ly-functions {
  /*background-color: #eee;*/
}

.ly-device .ly-functions {
  padding: 0.6em 0em;
  border-bottom: 1px solid #f8f8f8;
}

.ly-device .ly-function {
  padding: 0.3em 1em 0.3em 1.1em;
  padding-bottom: 0.6em;
  cursor: pointer;
}

.ly-device .ly-function .ly-execute {
  clear: both;
}

.ly-device .ly-function .ly-execute .ly-button {
  float: left;
  width: 1.8em;
  height: 1.8em;
  border: 2.5px solid #239cbb;
  border-radius: 100%;
}

.ly-device .ly-function .ly-execute .ly-name {
  margin-left: 2em;
  font-size: 1.5em;
}


/*
 * Modals
 */

.ly-device .ly-modal .ly-modal-backdrop {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  width: 100%;
  height:100%;
  z-index: 2000;
  opacity: 0.9;
  background-color: #eee;
}

.ly-device .ly-modal .ly-modal-window {
  position: absolute;
  z-index: 2001;
  top: 0;
  left: 0;
  width: 100.6%;
  background: #fff;
  border: 1px solid #eee;
  margin-top: -1px;
  margin-right: -1px;
  margin-left: -1px;
}

.ly-device .ly-modal .ly-modal-window .ly-close {
  position: absolute;
  color: #aaa;
  font-size: 2.5em;
  line-height: 1em;
  right: 0.3em;
  top: 0em;
  z-index: 2000;
}


.ly-device .ly-modal .ly-modal-window .ly-subheader {
  font-size: 1.3em;
  padding: .7em .6em;
  background-color: #f8f8f8;
  border-top: 1px solid #eee;
  border-bottom: 1px solid #eee;
}

/*
 * Function form
 */

.ly-device .ly-form .ly-head {
  padding: 0.6em;
  border-bottom: 1px solid #eee;
}

.ly-device .ly-form .ly-head .ly-name {
  font-size: 2.2em;
}

.ly-device .ly-form .ly-description {
  font-size: 1.2em;
}

.ly-device .ly-form .ly-fields {
  padding: 0.6em 0em;
  padding-bottom: 1em;
  margin-bottom: 1em;
}

.ly-device .ly-form .ly-field {
  padding: 0.3em 1em;
}

.ly-device .ly-form .ly-field .ly-description {
  margin-bottom:0.5em;
}

.ly-device .ly-form .ly-field .ly-name {
  color: #999;
  font-size: 0.9em;
}

.ly-device .ly-form .ly-button {
  font-family: "Lato", "Helvetica Neue", sans-serif;
  position: absolute;
  width: 100%;
  padding: 0;
  padding-top: 0.7em;
  padding-bottom: 0.7em;
  font-size: 1.5em;
  text-transform: uppercase;
  box-shadow: 1px 1px 3px #999;
}

.ly-device .ly-form .ly-field input[type="range"] {
  width: 93%
}

.ly-device .ly-form .ly-field .ly-color {
  height: 1.8em;
}

.ly-device .ly-form .ly-field .ly-range {
  margin-top: 2.3em !important;
}



/*
 * Properties
 */

.ly-device .ly-properties {
  /*background-color: #f4f4f4;*/
}

.ly-device .ly-properties {
  padding: 0.6em 0em;
}

.ly-device .ly-property {
  position: relative;
  padding: 0.3em 1.2em 0.3em 1.3em;
  clear: both;
}

.ly-device .ly-property .ly-button {
  float: left;
  width: 1.4em;
  height: 1.4em;
  border: 1.5px solid #aaa;
  border-radius: 100%;
  cursor: default;
}

.ly-device .ly-property .ly-description {
  margin-left: 2.4em;
  font-size: 1.2em;
}

.ly-device .ly-property  .ly-description .ly-name {
  color: #aaa;
  font-size: 0.9em;
}

.ly-device .ly-property  .ly-description .ly-color-ball {
  border: 10px solid;
  border-radius: 100%;
  float: right;
}

/*.ly-sensor .ly-properties .ly-property {*/
  /*padding-left: 0.8em;*/
/*}*/

/*.ly-sensor .ly-properties .ly-description {*/
  /*margin-left: 2.0em;*/
/*}*/


/*
 * Settings
 */

.ly-device .ly-form .ly-fields {
  padding: 0;
}

.ly-device .ly-form .ly-field {
  padding: 0;
  height: 3.4em;
  position: relative;
  /*border-bottom: 1px solid #f8f8f8;*/
}

.ly-device .ly-form .ly-editable {
  height: 4.2em;
}

.ly-device .ly-form .ly-no-underline .ly-value {
  border-bottom: none !important;
}

.ly-device .ly-form .ly-field .ly-description {
  position: absolute;
  padding: 1em 1em 0;
  color: #bbb;
  font-size: 0.8em;
  text-transform: uppercase;
}

.ly-device .ly-form .ly-field .ly-value {
  position: absolute;
  color: #666;
  font-family: "Lato", "Helvetica Neue", sans-serif;
  font-size: 1em;
  width: 92%;
  margin: 2em 0.8em 0.8em;
}

.ly-device .ly-form .ly-editable .ly-value {
  padding-bottom: 0.1em;
  border-bottom: 1px dashed #ddd;
}

.ly-device .ly-form .ly-actions .ly-button {
  font-size: 1.2em;
  margin-left: 3.5%;
  margin-bottom: .8em;
  width: 93%;
  position: relative;
}

.ly-device .ly-form .ly-actions .ly-delete-action {
  padding-top: 1em;
  padding-bottom: 1em;
  background-color: #f8f8f8;
  font-size: 1.2em;
  padding-left: .8em;
}

.ly-device .ly-form .ly-actions .ly-delete-link,
.ly-device .ly-form .ly-actions .ly-delete-link:hover {
  color: rgb(233, 0, 0);
}



.ly-device .ly-form .ly-field ::-webkit-input-placeholder { color:#ddd; font-weight: 100; }
.ly-device .ly-form .ly-field ::-moz-placeholder { color:#ddd; } /* firefox 19+ */
.ly-device .ly-form .ly-field :-ms-input-placeholder { color:#ddd; } /* ie */
.ly-device .ly-form .ly-field input:-moz-placeholder { color:#ddd; }

.ly-device .ly-form .ly-field select {
  background: transparent;
  -webkit-appearance: none;
}

button:focus,
select:focus,
input:focus {
  outline: 0;
}


/*
 * Settings (Delete)
 */

.ly-device .ly-modal .ly-info {
  color: rgb(223, 0, 0);
  font-size: 1.2em;
  padding: 0.5em;
  padding-top: 1em;
}

.ly-device .ly-delete .ly-value {
  font-size: 1.3em;
  margin: 0.5em 0.5em 0.8em;
}

.ly-device .ly-delete .ly-field {
  height: 5.5em;
}

.ly-device .ly-delete .ly-actions .ly-cancel-button {
  background-color: #ccc;
}

.ly-device .ly-settings .ly-actions {
  margin-top: 1em;
}

.ly-device .ly-settings .ly-settings-device .ly-header {
  border-bottom: none;
}

.ly-device .ly-settings .ly-actions .ly-disabled-button {
  background-color: #ccc !important;
}

.ly-device .ly-settings .ly-actions .ly-disabled-button:hover {
  background-color: #ccc !important;
  cursor: not-allowed;
}


/*
 * Elements animation
 */

.ly-animation.ng-enter {
  -webkit-animation: fadeIn 0.6s;
  animation: fadeIn 0.6s;
}

.ly-animation.ng-leave {
  -webkit-animation: fadeOut 0.6s;
  animation: fadeOut 0.6s;
}

.ly-animation-fast.ng-enter {
  -webkit-animation: fadeIn 0.3s;
  animation: fadeIn 0.3s;
}

.ly-animation-fast.ng-leave {
  -webkit-animation: fadeOut 0.3s;
  animation: fadeOut 0.3s;
}


/* Override animated.css */

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

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

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

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

  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(-30deg);
    -ms-transform: perspective(400px) rotateX(-30deg);
    transform: perspective(400px) rotateX(-30deg);
  }

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

  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;
}



/*
 * Compact personalizations
 */

.ly-compact .ly-functions {
  padding: 0em;
  border-bottom: none;
}

.ly-compact .ly-open-button {
  color: #239cbb;
  position: absolute;
  top: -0.05em;
  right: 0.1em;
  font-size: 4.2em;
}

            
          
!
            
              var app = angular.module('app', ['lelylan.directives.device', 'ngMockE2E']);

// mock all requests we need
app.run(function($httpBackend, $timeout, Profile) {
  Profile.set({id: '1'});

  device = {
  "uri": "http://api.lelylan.com/devices/1",
  "id": "1",
  "name": "IP Camera",
  "type": {
    "id": "1",
    "uri": "http://api.lelylan.com/types/1"
  },
  "category": "cameras",
  "physical": null,
  "pending": false,
  "activated": true,
  "properties": [{
    "uri": "http://api.lelylan.com/properties/518ca487a2c03f238900001c",
    "id": "518ca487a2c03f238900001c",
    "value": "on",
    "expected": "on",
    "pending": false,
    "accepted": []
  }, {
    "uri": "http://api.lelylan.com/properties/518ca4d7a2c03f5ca8000015",
    "id": "518ca4d7a2c03f5ca8000015",
    "value": "off",
    "expected": "off",
    "pending": false,
    "accepted": []
  }, {
    "uri": "http://api.lelylan.com/properties/518ca4e6a2c03f238900001d",
    "id": "518ca4e6a2c03f238900001d",
    "value": "1",
    "expected": "1",
    "pending": false,
    "accepted": []
  }, {
    "uri": "http://api.lelylan.com/properties/518ca4f3a2c03fac5a000014",
    "id": "518ca4f3a2c03fac5a000014",
    "value": "0",
    "expected": "0",
    "pending": false,
    "accepted": []
  }, {
    "uri": "http://api.lelylan.com/properties/518ca514a2c03f238900001e",
    "id": "518ca514a2c03f238900001e",
    "value": "",
    "expected": "",
    "pending": false,
    "accepted": []
  }, {
    "uri": "http://api.lelylan.com/properties/518ca52da2c03f238900001f",
    "id": "518ca52da2c03f238900001f",
    "value": "",
    "expected": "",
    "pending": false,
    "accepted": []
  }, {
    "uri": "http://api.lelylan.com/properties/518ca540a2c03f2389000020",
    "id": "518ca540a2c03f2389000020",
    "value": "",
    "expected": "",
    "pending": false,
    "accepted": []
  }, {
    "uri": "http://api.lelylan.com/properties/518ca550a2c03fac5a000015",
    "id": "518ca550a2c03fac5a000015",
    "value": "",
    "expected": "",
    "pending": false,
    "accepted": []
  }],
  "owner": {
    "id": "5036227a4f1b030200000001",
    "uri": "http://api.lelylan.com/people/5036227a4f1b030200000001"
  },
  "maker": {
    "id": "5036227a4f1b030200000001",
    "uri": "http://api.lelylan.com/people/5036227a4f1b030200000001"
  },
  "created_at": "2014-10-16T15:52:00Z",
  "updated_at": "2014-10-16T15:52:00Z",
  "updated_from": "Andrea Reginato"
  }

  
  type = {
  "id": "1",
  "uri": "http://api.lelylan.com/types/1",
  "owner": {
    "id": "5036227a4f1b030200000001",
    "uri": "http://api.lelylan.com/people/5036227a4f1b030200000001"
  },
  "name": "IP Camera",
  "description": "Define a complete IP Camera structure.<br> \nIt gives you **snapshot**, **rotation**,  **zoom** and **light** functionalities.",
  "category": "cameras",
  "created_at": "2013-05-10T07:40:01Z",
  "updated_at": "2014-08-31T10:38:39Z",
  "properties": [{
    "id": "518ca487a2c03f238900001c",
    "uri": "http://api.lelylan.com/properties/518ca487a2c03f238900001c",
    "name": "Status",
    "type": "text",
    "default": "on",
    "accepted": [{
      "key": "on",
      "value": "On"
    }, {
      "key": "off",
      "value": "Off"
    }],
    "created_at": "2013-05-10T07:40:55Z",
    "updated_at": "2014-08-31T10:35:00Z"
  }, {
    "id": "518ca4d7a2c03f5ca8000015",
    "uri": "http://api.lelylan.com/properties/518ca4d7a2c03f5ca8000015",
    "name": "Light",
    "type": "text",
    "default": "off",
    "accepted": [{
      "key": "on",
      "value": "On"
    }, {
      "key": "off",
      "value": "Off"
    }],
    "created_at": "2013-05-10T07:42:15Z",
    "updated_at": "2014-08-31T10:35:40Z"
  }, {
    "id": "518ca4e6a2c03f238900001d",
    "uri": "http://api.lelylan.com/properties/518ca4e6a2c03f238900001d",
    "name": "Zoom",
    "type": "range",
    "default": "1",
    "accepted": null,
    "range": {
      "min": "0",
      "max": "10",
      "step": "1"
    },
    "created_at": "2013-05-10T07:42:30Z",
    "updated_at": "2013-05-10T07:42:30Z"
  }, {
    "id": "518ca4f3a2c03fac5a000014",
    "uri": "http://api.lelylan.com/properties/518ca4f3a2c03fac5a000014",
    "name": "Rotation",
    "type": "range",
    "default": "0",
    "accepted": null,
    "range": {
      "min": "0",
      "max": "360",
      "step": "1"
    },
    "created_at": "2013-05-10T07:42:43Z",
    "updated_at": "2013-05-10T07:42:43Z"
  }, {
    "id": "518ca514a2c03f238900001e",
    "uri": "http://api.lelylan.com/properties/518ca514a2c03f238900001e",
    "name": "Video URI",
    "type": "url",
    "default": null,
    "accepted": null,
    "created_at": "2013-05-10T07:43:16Z",
    "updated_at": "2013-05-10T07:43:16Z"
  }, {
    "id": "518ca52da2c03f238900001f",
    "uri": "http://api.lelylan.com/properties/518ca52da2c03f238900001f",
    "name": "Last Snapshot URI",
    "type": "url",
    "default": null,
    "accepted": null,
    "created_at": "2013-05-10T07:43:41Z",
    "updated_at": "2013-05-10T07:43:41Z"
  }, {
    "id": "518ca540a2c03f2389000020",
    "uri": "http://api.lelylan.com/properties/518ca540a2c03f2389000020",
    "name": "Username",
    "type": "text",
    "default": null,
    "accepted": null,
    "created_at": "2013-05-10T07:44:00Z",
    "updated_at": "2013-05-10T07:44:00Z"
  }, {
    "id": "518ca550a2c03fac5a000015",
    "uri": "http://api.lelylan.com/properties/518ca550a2c03fac5a000015",
    "name": "Password",
    "type": "password",
    "default": null,
    "accepted": null,
    "created_at": "2013-05-10T07:44:16Z",
    "updated_at": "2013-05-10T07:44:16Z"
  }],
  "functions": [{
    "id": "518ca578a2c03fac5a000016",
    "uri": "http://api.lelylan.com/functions/518ca578a2c03fac5a000016",
    "name": "Turn On",
    "properties": [{
      "id": "518ca487a2c03f238900001c",
      "uri": "http://api.lelylan.com/properties/518ca487a2c03f238900001c",
      "expected": "on",
      "pending": true
    }],
    "created_at": "2013-05-10T07:44:56Z",
    "updated_at": "2013-05-13T13:40:54Z"
  }, {
    "id": "518ca581a2c03f2389000021",
    "uri": "http://api.lelylan.com/functions/518ca581a2c03f2389000021",
    "name": "Turn Off",
    "properties": [{
      "id": "518ca487a2c03f238900001c",
      "uri": "http://api.lelylan.com/properties/518ca487a2c03f238900001c",
      "expected": "off",
      "pending": true
    }],
    "created_at": "2013-05-10T07:45:05Z",
    "updated_at": "2013-05-13T13:40:59Z"
  }, {
    "id": "518ca59aa2c03f5ca8000016",
    "uri": "http://api.lelylan.com/functions/518ca59aa2c03f5ca8000016",
    "name": "Lights On",
    "properties": [{
      "id": "518ca4d7a2c03f5ca8000015",
      "uri": "http://api.lelylan.com/properties/518ca4d7a2c03f5ca8000015",
      "expected": "on",
      "pending": true
    }],
    "created_at": "2013-05-10T07:45:30Z",
    "updated_at": "2013-05-13T13:41:05Z"
  }, {
    "id": "518ca5e6a2c03f5ca8000018",
    "uri": "http://api.lelylan.com/functions/518ca5e6a2c03f5ca8000018",
    "name": "Lights Off",
    "properties": [{
      "id": "518ca4d7a2c03f5ca8000015",
      "uri": "http://api.lelylan.com/properties/518ca4d7a2c03f5ca8000015",
      "expected": "off",
      "pending": true
    }],
    "created_at": "2013-05-10T07:46:46Z",
    "updated_at": "2013-05-13T13:41:12Z"
  }, {
    "id": "518ca5f9a2c03f2389000023",
    "uri": "http://api.lelylan.com/functions/518ca5f9a2c03f2389000023",
    "name": "Set Zoom",
    "properties": [{
      "id": "518ca4e6a2c03f238900001d",
      "uri": "http://api.lelylan.com/properties/518ca4e6a2c03f238900001d",
      "expected": null,
      "pending": true
    }],
    "created_at": "2013-05-10T07:47:05Z",
    "updated_at": "2013-05-10T07:47:05Z"
  }, {
    "id": "518ca60ba2c03f2389000025",
    "uri": "http://api.lelylan.com/functions/518ca60ba2c03f2389000025",
    "name": "Set Rotation",
    "properties": [{
      "id": "518ca4f3a2c03fac5a000014",
      "uri": "http://api.lelylan.com/properties/518ca4f3a2c03fac5a000014",
      "expected": null,
      "pending": true
    }],
    "created_at": "2013-05-10T07:47:23Z",
    "updated_at": "2013-05-10T07:47:23Z"
  }, {
    "id": "518ca65ba2c03fac5a000019",
    "uri": "http://api.lelylan.com/functions/518ca65ba2c03fac5a000019",
    "name": "Camera Settings",
    "properties": [{
      "id": "518ca514a2c03f238900001e",
      "uri": "http://api.lelylan.com/properties/518ca514a2c03f238900001e",
      "expected": null,
      "pending": true
    }, {
      "id": "518ca540a2c03f2389000020",
      "uri": "http://api.lelylan.com/properties/518ca540a2c03f2389000020",
      "expected": null,
      "pending": true
    }, {
      "id": "518ca550a2c03fac5a000015",
      "uri": "http://api.lelylan.com/properties/518ca550a2c03fac5a000015",
      "expected": null,
      "pending": true
    }],
    "created_at": "2013-05-10T07:48:43Z",
    "updated_at": "2013-05-10T07:48:43Z"
  }],
  "statuses": [{
    "id": "518ca66ba2c03fac5a00001d",
    "uri": "http://api.lelylan.com/statuses/518ca66ba2c03fac5a00001d",
    "name": "On",
    "function": {
      "id": "518ca581a2c03f2389000021",
      "uri": "http://api.lelylan.com/functions/518ca581a2c03f2389000021"
    },
    "properties": [{
      "id": "518ca487a2c03f238900001c",
      "uri": "http://api.lelylan.com/properties/518ca487a2c03f238900001c",
      "pending": false,
      "values": ["on"],
      "range": null
    }],
    "created_at": "2013-05-10T07:48:59Z",
    "updated_at": "2014-08-31T10:38:33Z"
  }, {
    "id": "518ca690a2c03f5ca800001a",
    "uri": "http://api.lelylan.com/statuses/518ca690a2c03f5ca800001a",
    "name": "Off",
    "function": {
      "id": "518ca578a2c03fac5a000016",
      "uri": "http://api.lelylan.com/functions/518ca578a2c03fac5a000016"
    },
    "properties": [{
      "id": "518ca487a2c03f238900001c",
      "uri": "http://api.lelylan.com/properties/518ca487a2c03f238900001c",
      "pending": false,
      "values": ["off"],
      "range": null
    }],
    "created_at": "2013-05-10T07:49:36Z",
    "updated_at": "2014-08-31T10:38:39Z"
  }]
  }

  
  privates = {
  "uri": "http://api.lelylan.com/devices/1",
  "id": "1",
  "name": "Device",
  "secret": "u/mNwcXQvAlIL5ICG6bfg/GniC5Xqds2",
  "activation_code": "6d35d29a5373321c5425e8a4b88e36341d9a547d"
  }


  $httpBackend.when('GET', /\/templates\//).passThrough();

  $httpBackend.whenGET('http://api.lelylan.com/devices/1').respond(device);
  $httpBackend.whenPUT('http://api.lelylan.com/devices/1')
    .respond(function(method, url, data, headers) { return [200, updateDevice(data), {}]; });
  $httpBackend.whenPUT('http://api.lelylan.com/devices/1/properties')
    .respond(function(method, url, data, headers) { return [200,  updateDeviceProperties(data), {}]; });
  $httpBackend.whenDELETE('http://api.lelylan.com/devices/1').respond(device);
  $httpBackend.whenGET('http://api.lelylan.com/types/1').respond(type);
  $httpBackend.whenGET('http://api.lelylan.com/devices/1/privates').respond(privates);

  var updateDevice = function(data) {
    data = angular.fromJson(data);
    device.updated_at   = new Date();
    device.name         = data.name;
    device.physical.uri = data.physical.uri;
    return device;
  }

  var updateDeviceProperties = function(data) {
    data = angular.fromJson(data);
    device.updated_at = new Date();
    _.each(data.properties, function(property) {
      var result = _.find(device.properties, function(_property) { return _property.id == property.id; } );
      result.expected = result.value = property.expected; });
    return device;
  }
});

// hack to make the anchor links work
app.run(function($rootScope, $location, $anchorScroll) {
  $rootScope.$on('$routeChangeSuccess', function(newRoute, oldRoute) { });
});

app.config(['$locationProvider', function ($locationProvider) {
  $locationProvider.html5Mode(true);
  $locationProvider.hashPrefix('!');
}]);

            
          
!
999px
Loading ..................

Console