Rift.IO OSM R1 Initial Submission

Signed-off-by: Jeremy Mordkoff <jeremy.mordkoff@riftio.com>
diff --git a/skyquake/framework/widgets/input-range-slider/input-range-slider.jsx b/skyquake/framework/widgets/input-range-slider/input-range-slider.jsx
new file mode 100644
index 0000000..d9e0544
--- /dev/null
+++ b/skyquake/framework/widgets/input-range-slider/input-range-slider.jsx
@@ -0,0 +1,72 @@
+
+/*
+ * 
+ *   Copyright 2016 RIFT.IO Inc
+ *
+ *   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.
+ *
+ */
+import React from 'react';
+// import Slider from 'react-rangeslider';
+// import Slider from './react-rangeslider.jsx';
+import './input-range-slider.scss';
+
+
+class RWslider extends React.Component {
+  constructor(props, context) {
+    super(props, context);
+    this.state = {...props}
+  }
+
+  handleChange = (value) => {
+    this.props.handleInputUpdate(value);
+    this.setState({
+      value: value
+    });
+  };
+
+  render() {
+    let state = this.state;
+      var className = "input-range-slider_" + this.props.orientation;
+    return (
+      <div className={className}>
+      <div className={className + '-info'}>
+      {state["min-value"]}
+      </div>
+      <Slider
+        displayValue={true}
+        value={state.value}
+        max={state["max-value"]}
+        min={state["min-value"]}
+        step={state["step-value"]}
+        onChange={this.handleChange}
+        className={className + '-info'} />
+      <div className={className + '-info'}>
+      {state["max-value"]}
+      </div>
+      </div>
+    );
+  }
+}
+
+RWslider.defaultProps = {
+    value: 10,
+    "min-value": 0,
+    "max-value":100,
+    "step-value":1,
+    "units": "%",
+    orientation: "horizontal"
+}
+
+export default RWslider
+
diff --git a/skyquake/framework/widgets/input-range-slider/input-range-slider.scss b/skyquake/framework/widgets/input-range-slider/input-range-slider.scss
new file mode 100644
index 0000000..803bd8b
--- /dev/null
+++ b/skyquake/framework/widgets/input-range-slider/input-range-slider.scss
@@ -0,0 +1,104 @@
+
+/*
+ * 
+ *   Copyright 2016 RIFT.IO Inc
+ *
+ *   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.
+ *
+ */
+@import '../../style/_colors.scss';
+
+  .input-range-slider_horizontal{
+    display:flex;
+    align-items:baseline;
+    &-info {
+      margin:0 0.5rem;
+    }
+  }
+
+
+.rangeslider {
+  margin: 20px 0;
+  position: relative;
+  background: #e6e6e6;
+  display:flex;
+  flex:1;
+  .rangeslider__fill, .rangeslider__handle {
+    position: absolute;
+  }
+  &, .rangeslider__fill {
+    display: block;
+    box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.3);
+  }
+  .rangeslider__handle {
+    background: #fff;
+    border: 1px solid #ccc;
+    cursor: pointer;
+    display: inline-block;
+    position: absolute;
+    &:active {
+      background: #999;
+    }
+  }
+}
+
+/**
+ * Rangeslider - Horizontal slider
+ */
+.rangeslider-horizontal {
+  height: 1rem;
+  margin-bottom: 2.5rem;
+  .rangeslider__fill {
+    height: 100%;
+    background: $light-blue;
+    top: 0;
+  }
+  .rangeslider__handle {
+    width: 0.5rem;
+    height: 2rem;
+    top: -0.5rem;
+    left:-10px;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    >div{
+          width: 5rem;
+          margin-top: 2rem;
+          text-align: center;
+    }
+  }
+}
+
+/**
+ * Rangeslider - Vertical slider
+ */
+.rangeslider-vertical {
+  margin: 20px auto;
+  height: 150px;
+  max-width: 10px;
+  background: none;
+  .rangeslider__fill {
+    width: 100%;
+    background: $light-blue;
+    box-shadow: none;
+    bottom: 0;
+  }
+  .rangeslider__handle {
+    width: 30px;
+    height: 10px;
+    left: -10px;
+    &:active {
+      box-shadow: none;
+    }
+  }
+}
diff --git a/skyquake/framework/widgets/input-range-slider/react-rangeslider.jsx b/skyquake/framework/widgets/input-range-slider/react-rangeslider.jsx
new file mode 100644
index 0000000..feff1eb
--- /dev/null
+++ b/skyquake/framework/widgets/input-range-slider/react-rangeslider.jsx
@@ -0,0 +1,251 @@
+
+/*
+ * 
+ *   Copyright 2016 RIFT.IO Inc
+ *
+ *   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.
+ *
+ */
+/**
+ * https://github.com/larsmaultsby/react-rangeslider
+ *
+ *
+ * Forked from: https://github.com/whoisandie/react-rangeslider
+ *
+ *
+    The MIT License (MIT)
+
+    Copyright (c) 2015 Bhargav Anand
+
+    Permission is hereby granted, free of charge, to any person obtaining a copy
+    of this software and associated documentation files (the "Software"), to deal
+    in the Software without restriction, including without limitation the rights
+    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+    copies of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be included in all
+    copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+    SOFTWARE.
+ *
+ */
+
+import React, { PropTypes, Component, findDOMNode } from 'react';
+import joinClasses from 'react/lib/joinClasses';
+
+function capitalize(str) {
+  return str.charAt(0).toUpperCase() + str.substr(1);
+}
+
+function maxmin(pos, min, max) {
+  if (pos < min) { return min; }
+  if (pos > max) { return max; }
+  return pos;
+}
+
+const constants = {
+  orientation: {
+    horizontal: {
+      dimension: 'width',
+      direction: 'left',
+      coordinate: 'x',
+    },
+    vertical: {
+      dimension: 'height',
+      direction: 'top',
+      coordinate: 'y',
+    }
+  }
+};
+
+class Slider extends Component {
+  static propTypes = {
+    min: PropTypes.number,
+    max: PropTypes.number,
+    step: PropTypes.number,
+    value: PropTypes.number,
+    orientation: PropTypes.string,
+    onChange: PropTypes.func,
+    className: PropTypes.string,
+  }
+
+  static defaultProps = {
+    min: 0,
+    max: 100,
+    step: 1,
+    value: 0,
+    orientation: 'horizontal',
+  }
+
+  state = {
+    limit: 0,
+    grab: 0
+  }
+
+  // Add window resize event listener here
+  componentDidMount() {
+    this.calculateDimensions();
+    window.addEventListener('resize', this.calculateDimensions);
+  }
+
+  componentWillUnmount() {
+    window.removeEventListener('resize', this.calculateDimensions);
+  }
+
+  handleSliderMouseDown = (e) => {
+    let value, { onChange } = this.props;
+    if (!onChange) return;
+    value = this.position(e);
+    onChange && onChange(value);
+  }
+
+  handleKnobMouseDown = () => {
+    document.addEventListener('mousemove', this.handleDrag);
+    document.addEventListener('mouseup', this.handleDragEnd);
+  }
+
+  handleDrag = (e) => {
+    let value, { onChange } = this.props;
+    if (!onChange) return;
+    value = this.position(e);
+    onChange && onChange(value);
+  }
+
+  handleDragEnd = () => {
+    document.removeEventListener('mousemove', this.handleDrag);
+    document.removeEventListener('mouseup', this.handleDragEnd);
+  }
+
+  handleNoop = (e) => {
+    e.stopPropagation();
+    e.preventDefault();
+  }
+
+  calculateDimensions = () => {
+    let { orientation } = this.props;
+    let dimension = capitalize(constants.orientation[orientation].dimension);
+    const sliderPos = findDOMNode(this.refs.slider)['offset' + dimension];
+    const handlePos = findDOMNode(this.refs.handle)['offset' + dimension]
+    this.setState({
+      limit: sliderPos - handlePos,
+      grab: handlePos / 2,
+    });
+  }
+  getPositionFromValue = (value) => {
+    let percentage, pos;
+    let { limit } = this.state;
+    let { min, max } = this.props;
+    percentage = (value - min) / (max - min);
+    pos = Math.round(percentage * limit);
+
+    return pos;
+  }
+
+  getValueFromPosition = (pos) => {
+    let percentage, value;
+    let { limit } = this.state;
+    let { orientation, min, max, step } = this.props;
+    percentage = (maxmin(pos, 0, limit) / (limit || 1));
+
+    if (orientation === 'horizontal') {
+      value = step * Math.round(percentage * (max - min) / step) + min;
+    } else {
+      value = max - (step * Math.round(percentage * (max - min) / step) + min);
+    }
+
+    return value;
+  }
+
+  position = (e) => {
+    let pos, value, { grab } = this.state;
+    let { orientation } = this.props;
+    const node = findDOMNode(this.refs.slider);
+    const coordinateStyle = constants.orientation[orientation].coordinate;
+    const directionStyle = constants.orientation[orientation].direction;
+    const coordinate = e['client' + capitalize(coordinateStyle)];
+    const direction = node.getBoundingClientRect()[directionStyle];
+
+    pos = coordinate - direction - grab;
+    value = this.getValueFromPosition(pos);
+
+    return value;
+  }
+
+  coordinates = (pos) => {
+    let value, fillPos, handlePos;
+    let { limit, grab } = this.state;
+    let { orientation } = this.props;
+    value = this.getValueFromPosition(pos);
+    handlePos = this.getPositionFromValue(value);
+
+    if (orientation === 'horizontal') {
+      fillPos = handlePos + grab;
+    } else {
+      fillPos = limit - handlePos + grab;
+    }
+
+    return {
+      fill: fillPos,
+      handle: handlePos,
+    };
+  }
+
+  render() {
+    let dimension, direction, position, coords, fillStyle, handleStyle, displayValue;
+    let { value, orientation, className } = this.props;
+
+    dimension = constants.orientation[orientation].dimension;
+    direction = constants.orientation[orientation].direction;
+
+    position = this.getPositionFromValue(value);
+    coords = this.coordinates(position);
+
+    fillStyle = {[dimension]: `${coords.fill}px`};
+    handleStyle = {[direction]: `${coords.handle}px`};
+
+    if(this.props.displayValue) {
+      displayValue = <div>{this.props.value}</div>;
+    }
+
+    return (
+      <div
+        ref="slider"
+        className={joinClasses('rangeslider ', 'rangeslider-' + orientation, className)}
+        onMouseDown={this.handleSliderMouseDown}
+        onClick={this.handleNoop}
+        style={{display:'flex'}}>
+        <div
+          ref="fill"
+          className="rangeslider__fill"
+          style={fillStyle} />
+        <div
+          ref="handle"
+          className="rangeslider__handle"
+          onMouseDown={this.handleKnobMouseDown}
+          onClick={this.handleNoop}
+          style={handleStyle}>
+            {displayValue}
+          </div>
+      </div>
+    );
+  }
+}
+
+export default Slider;