1
0
mirror of https://github.com/chatopera/cosin.git synced 2025-08-01 16:38:02 +08:00

#60 更新联系人笔记

This commit is contained in:
Kyle 2018-09-13 19:36:50 +08:00
parent dfb1e0b12c
commit 15683089e6
No known key found for this signature in database
GPG Key ID: E9D96D736A2D3F3C
6 changed files with 173 additions and 1643 deletions

View File

@ -12,8 +12,8 @@ const MINIO_END_POINT = 'localhost';
const MINIO_ACCESS_KEY = 'key';
const MINIO_SECRET_KEY = 'secret';
const REDIS_HOST = 'localhost';
const REDIS_PORT = 6379;
const REDIS_HOST = '192.168.2.217';
const REDIS_PORT = 9763;
const config = {
PBX_CHANNEL_ID,

View File

@ -30,11 +30,11 @@ test.only("Redis Test # 外呼接通", async(t) => {
let payload = {
"uuid": "9a0cbc81-ccae-425e-8d3d-369b872a6481",
"to": "15801213121",
"from": "1002",
"to": "13213213213",
"from": "1003",
"type": "callout",
"channel": "bxzq",
"dialplan": "4028825165a9b23a0165a9b4de1c04c9",
"dialplan": "4028827365b2acec0165b307afe405de",
"createtime": now.valueOf(),
"ops": "answer"
}

View File

@ -26,11 +26,11 @@ test.only("Redis Test # 外呼挂断", async(t) => {
console.log("挂断时间:", now);
let payload = {
"uuid": "9a0cbc81-ccae-425e-8d3d-369b872a6481",
"to": "15801213121",
"from": "1002",
"to": "13213213213",
"from": "1003",
"type": "callout",
"channel": "bxzq",
"dialplan": "4028825165a9b23a0165a9b4de1c04c9",
"dialplan": "4028827365b2acec0165b307afe405de",
"createtime": now.valueOf(),
"ops": "hangup",
"record":"chatopera/376bf70a-9449-46c8-ad3e-03ac41953946.wav"

View File

@ -1,643 +0,0 @@
/* ---------------- *\
GLOBAL VARIABLES
\* ---------------- */
/* 1 / sqrt(3) */
/* ------ *\
MIXINS
\* ------ */
/* -------------- *\
MAIN CONTAINER
\* -------------- */
.timeline-me-container {
position: relative;
color: #666666;
}
.timeline-me-container.no-scroll .timeline-me-wrapper {
overflow: hidden;
}
.timeline-me-container.no-x-scroll .timeline-me-wrapper {
overflow-x: hidden;
}
.timeline-me-container.no-y-scroll .timeline-me-wrapper {
overflow-y: hidden;
}
.timeline-me-track {
position: relative;
overflow: hidden;
}
.timeline-me-track:before {
content: '';
position: absolute;
background: #555555;
}
.timeline-me-vertical .timeline-me-track:before {
top: 0;
left: 50%;
height: 100%;
width: 4px;
margin-left: -2px;
}
.timeline-me-horizontal .timeline-me-track {
display: table;
}
.timeline-me-horizontal .timeline-me-track:before {
top: 50%;
left: 0;
height: 4px;
width: 100%;
margin-top: -2px;
}
.timeline-me-leftscroll,
.timeline-me-rightscroll {
width: 50px;
position: absolute;
z-index: 1001;
top: 0;
bottom: 0;
}
.timeline-me-leftscroll {
left: 0;
background-image: -webkit-gradient(linear, left top, right top, from(#8dd8dd), to(rgba(141, 216, 221, 0)));
background-image: -webkit-linear-gradient(left, #8dd8dd, rgba(141, 216, 221, 0));
background-image: -moz-linear-gradient(left, #8dd8dd, rgba(141, 216, 221, 0));
background-image: -ms-linear-gradient(left, #8dd8dd, rgba(141, 216, 221, 0));
background-image: -o-linear-gradient(left, #8dd8dd, rgba(141, 216, 221, 0));
background-image: linear-gradient(left, #8dd8dd, rgba(141, 216, 221, 0));
filter: progid:DXImageTransform.Microsoft.gradient(start-colourStr='#8dd8dd', end-colourStr='rgba(141, 216, 221, 0)', gradientType='1');
}
.timeline-me-rightscroll {
right: 0;
background-image: -webkit-gradient(linear, left top, right top, from(rgba(141, 216, 221, 0)), to(#8dd8dd));
background-image: -webkit-linear-gradient(left, rgba(141, 216, 221, 0), #8dd8dd);
background-image: -moz-linear-gradient(left, rgba(141, 216, 221, 0), #8dd8dd);
background-image: -ms-linear-gradient(left, rgba(141, 216, 221, 0), #8dd8dd);
background-image: -o-linear-gradient(left, rgba(141, 216, 221, 0), #8dd8dd);
background-image: linear-gradient(left, rgba(141, 216, 221, 0), #8dd8dd);
filter: progid:DXImageTransform.Microsoft.gradient(start-colourStr='rgba(141, 216, 221, 0)', end-colourStr='#8dd8dd', gradientType='1');
}
.timeline-me-leftarrow,
.timeline-me-rightarrow {
position: absolute;
top: 50%;
z-index: 1002;
transform: translate(0, -50%);
}
.timeline-me-leftarrow:after,
.timeline-me-rightarrow:after {
content: '';
width: 0;
height: 0;
position: absolute;
transform: translate(0, -50%);
border-style: solid;
}
.timeline-me-leftarrow {
left: 0;
}
.timeline-me-leftarrow:after {
left: 0;
border-width: 11.54px 20px 11.54px 0;
border-color: transparent #ffffff transparent transparent;
}
.timeline-me-rightarrow {
right: 0;
}
.timeline-me-rightarrow:after {
right: 0;
border-width: 11.54px 0 11.54px 20px;
border-color: transparent transparent transparent #ffffff;
}
/* -------------------- *\
GENERIC ITEM'S STYLE
\* -------------------- */
.timeline-me-horizontal {
/*.timeline-me-label { bottom: - ($label-picto-total-size - $timeline-thickness) / 2; }*/
}
.timeline-me-horizontal .timeline-me-wrapper {
overflow-x: scroll;
}
.timeline-me-horizontal .timeline-me-item {
display: table-cell;
vertical-align: middle;
padding-left: 10px;
padding-right: 10px;
}
.timeline-me-horizontal .timeline-me-fix-height .timeline-me-label {
height: 150px;
}
.timeline-me-horizontal .timeline-me-fix-height .timeline-me-content {
height: 300px;
}
.timeline-me-horizontal .timeline-me-fix-width .timeline-me-label {
width: 200px;
}
.timeline-me-horizontal .timeline-me-fix-width .timeline-me-content {
width: 400px;
}
.timeline-me-vertical .timeline-me-item {
padding-top: 5px;
padding-bottom: 5px;
}
.timeline-me-item,
.timeline-me-item:first-child {
position: relative;
margin: 20px 0px;
}
.timeline-me-label .timeline-me-same-position {
border-left: 2px solid #666666;
margin-left: 10px;
padding-left: 10px;
}
.timeline-me-content {
position: relative;
-moz-transform-style: preserve-3d;
-webkit-transform-style: preserve-3d;
-ms-transform-style: preserve-3d;
-o-transform-style: preserve-3d;
transform-style: preserve-3d;
}
.timeline-me-shortcontent,
.timeline-me-fullcontent {
position: relative;
box-sizing: border-box;
width: 100%;
background-color: #ffffff;
line-height: 20px;
border-radius: 2px;
}
.timeline-me-showmore {
display: block;
}
.timeline-me-showless {
display: none;
}
.timeline-me-hidden {
position: relative;
z-index: -1000;
}
/* ----------------- *\
MILESTONE ELEMENT
\* ----------------- */
.timeline-me-milestone {
/* Specific for 'right' position item - default one */
/* Specific for 'top' position item */
/* Specific for 'left' position item */
/* Specific for 'bottom' position item */
}
.timeline-me-milestone .timeline-me-label {
position: relative;
line-height: 36px;
}
.timeline-me-milestone .timeline-me-label .timeline-me-picto {
position: absolute;
box-sizing: content-box;
height: 30px;
width: 30px;
background-color: #ffffff;
border-radius: 50%;
border: 3px solid #555555;
z-index: 100;
text-align: center;
overflow: hidden;
}
.timeline-me-milestone.timeline-me-right, .timeline-me-milestone:not(.timeline-me-top):not(.timeline-me-left):not(.timeline-me-bottom) {
width: 50%;
margin-left: 50%;
}
.timeline-me-milestone.timeline-me-right .timeline-me-label, .timeline-me-milestone:not(.timeline-me-top):not(.timeline-me-left):not(.timeline-me-bottom) .timeline-me-label {
height: 36px;
padding-left: 39px;
margin-left: -18px;
}
.timeline-me-milestone.timeline-me-right .timeline-me-label .timeline-me-picto, .timeline-me-milestone:not(.timeline-me-top):not(.timeline-me-left):not(.timeline-me-bottom) .timeline-me-label .timeline-me-picto {
top: 0;
left: 0;
}
.timeline-me-milestone.timeline-me-top {
margin: auto;
text-align: center;
}
.timeline-me-milestone.timeline-me-top .timeline-me-label {
padding-bottom: 39px;
}
.timeline-me-milestone.timeline-me-top .timeline-me-label .timeline-me-picto {
bottom: 0px;
left: 50%;
margin-left: -18px;
}
.timeline-me-milestone.timeline-me-left {
width: 50%;
margin-right: 50%;
text-align: right;
}
.timeline-me-milestone.timeline-me-left .timeline-me-label {
height: 36px;
padding-right: 39px;
margin-right: -18px;
}
.timeline-me-milestone.timeline-me-left .timeline-me-label .timeline-me-picto {
top: 0;
right: 0px;
}
.timeline-me-milestone.timeline-me-bottom {
margin: auto;
text-align: center;
}
.timeline-me-milestone.timeline-me-bottom .timeline-me-label {
padding-top: 39px;
}
.timeline-me-milestone.timeline-me-bottom .timeline-me-label .timeline-me-picto {
top: 0px;
left: 50%;
margin-left: -18px;
}
.timeline-me-milestone .timeline-me-shortcontent {
display: none;
}
.timeline-me-horizontal .timeline-me-milestone.timeline-me-top .timeline-me-label-wrapper {
padding-bottom: 36px;
}
.timeline-me-horizontal .timeline-me-milestone.timeline-me-bottom .timeline-me-label-wrapper {
padding-top: 36px;
}
/* ----------------- *\
SMALLITEM ELEMENT
\* ----------------- */
.timeline-me-smallitem {
/* Specific for 'left' position item - default one */
/* Specific for 'top' position item */
/* Specific for 'right' position item */
/* Specific for 'bottom' position item */
}
.timeline-me-smallitem .timeline-me-item-wrapper {
position: relative;
}
.timeline-me-smallitem .timeline-me-label {
position: absolute;
line-height: 36px;
}
.timeline-me-smallitem .timeline-me-label .timeline-me-picto {
position: absolute;
box-sizing: content-box;
height: 30px;
width: 30px;
background-color: #ffffff;
border-radius: 50%;
border: 3px solid #555555;
z-index: 100;
text-align: center;
overflow: hidden;
}
.timeline-me-smallitem.timeline-me-left .timeline-me-label, .timeline-me-smallitem:not(.timeline-me-top):not(.timeline-me-right):not(.timeline-me-bottom) .timeline-me-label {
left: 50%;
top: 0;
margin-left: -18px;
text-align: left;
padding-left: 39px;
}
.timeline-me-smallitem.timeline-me-left .timeline-me-label .timeline-me-picto, .timeline-me-smallitem:not(.timeline-me-top):not(.timeline-me-right):not(.timeline-me-bottom) .timeline-me-label .timeline-me-picto {
top: 0;
left: 0px;
}
.timeline-me-smallitem.timeline-me-left .timeline-me-content-container, .timeline-me-smallitem:not(.timeline-me-top):not(.timeline-me-right):not(.timeline-me-bottom) .timeline-me-content-container {
width: 40%;
margin-left: 10%;
padding-right: 25px;
}
.timeline-me-smallitem.timeline-me-left .timeline-me-fullcontent:before,
.timeline-me-smallitem.timeline-me-left .timeline-me-shortcontent:before, .timeline-me-smallitem:not(.timeline-me-top):not(.timeline-me-right):not(.timeline-me-bottom) .timeline-me-fullcontent:before,
.timeline-me-smallitem:not(.timeline-me-top):not(.timeline-me-right):not(.timeline-me-bottom) .timeline-me-shortcontent:before {
background-color: inherit;
-webkit-transform: translateY(-50%) rotate(45deg);
-moz-transform: translateY(-50%) rotate(45deg);
-ms-transform: translateY(-50%) rotate(45deg);
-o-transform: translateY(-50%) rotate(45deg);
transform: translateY(-50%) rotate(45deg);
height: 10px;
width: 10px;
right: -5px;
top: 18px;
}
.timeline-me-smallitem.timeline-me-top .timeline-me-label {
top: 50%;
left: 0;
margin-top: -18px;
text-align: left;
padding-top: 39px;
}
.timeline-me-smallitem.timeline-me-top .timeline-me-label .timeline-me-picto {
top: 0px;
}
.timeline-me-smallitem.timeline-me-top .timeline-me-content-wrapper:not(.timeline-me-hidden) {
margin-bottom: 50px;
}
.timeline-me-smallitem.timeline-me-top .timeline-me-fullcontent:before,
.timeline-me-smallitem.timeline-me-top .timeline-me-shortcontent:before {
background-color: inherit;
-webkit-transform: translateX(-50%) rotate(45deg);
-moz-transform: translateX(-50%) rotate(45deg);
-ms-transform: translateX(-50%) rotate(45deg);
-o-transform: translateX(-50%) rotate(45deg);
transform: translateX(-50%) rotate(45deg);
height: 10px;
width: 10px;
bottom: -5px;
left: 18px;
}
.timeline-me-smallitem.timeline-me-right .timeline-me-label {
right: 50%;
top: 0;
margin-right: -18px;
text-align: right;
padding-right: 39px;
}
.timeline-me-smallitem.timeline-me-right .timeline-me-label .timeline-me-picto {
top: 0;
right: 0px;
}
.timeline-me-smallitem.timeline-me-right .timeline-me-content-container {
width: 40%;
margin-left: 50%;
padding-left: 25px;
}
.timeline-me-smallitem.timeline-me-right .timeline-me-fullcontent:before,
.timeline-me-smallitem.timeline-me-right .timeline-me-shortcontent:before {
background-color: inherit;
-webkit-transform: translateY(-50%) rotate(45deg);
-moz-transform: translateY(-50%) rotate(45deg);
-ms-transform: translateY(-50%) rotate(45deg);
-o-transform: translateY(-50%) rotate(45deg);
transform: translateY(-50%) rotate(45deg);
height: 10px;
width: 10px;
left: -5px;
top: 18px;
}
.timeline-me-smallitem.timeline-me-bottom .timeline-me-label {
bottom: 50%;
left: 0;
margin-bottom: -18px;
text-align: left;
padding-bottom: 39px;
}
.timeline-me-smallitem.timeline-me-bottom .timeline-me-label .timeline-me-picto {
bottom: 0px;
}
.timeline-me-smallitem.timeline-me-bottom .timeline-me-content-wrapper:not(.timeline-me-hidden) {
margin-top: 50px;
}
.timeline-me-smallitem.timeline-me-bottom .timeline-me-fullcontent:before,
.timeline-me-smallitem.timeline-me-bottom .timeline-me-shortcontent:before {
background-color: inherit;
-webkit-transform: translateX(-50%) rotate(45deg);
-moz-transform: translateX(-50%) rotate(45deg);
-ms-transform: translateX(-50%) rotate(45deg);
-o-transform: translateX(-50%) rotate(45deg);
transform: translateX(-50%) rotate(45deg);
height: 10px;
width: 10px;
top: -5px;
left: 18px;
}
.timeline-me-smallitem .timeline-me-content-container {
box-sizing: border-box;
}
.timeline-me-smallitem .timeline-me-fullcontent,
.timeline-me-smallitem .timeline-me-shortcontent {
padding: 10px;
min-height: 18px;
min-width: 18px;
}
.timeline-me-smallitem .timeline-me-fullcontent .timeline-me-same-position,
.timeline-me-smallitem .timeline-me-shortcontent .timeline-me-same-position {
display: block;
border-top: 2px solid #666666;
margin-top: 10px;
padding-top: 10px;
}
.timeline-me-smallitem .timeline-me-fullcontent:before,
.timeline-me-smallitem .timeline-me-shortcontent:before {
content: '';
position: absolute;
}
.timeline-me-smallitem .timeline-me-displayfull .timeline-me-shortcontent, .timeline-me-smallitem.timeline-me-displayfull .timeline-me-shortcontent {
display: none;
}
.timeline-me-smallitem .timeline-me-displayfull .timeline-me-fullcontent, .timeline-me-smallitem.timeline-me-displayfull .timeline-me-fullcontent {
display: block;
}
.timeline-me-smallitem .timeline-me-displayfull .timeline-me-showmore, .timeline-me-smallitem.timeline-me-displayfull .timeline-me-showmore {
display: none;
}
.timeline-me-smallitem .timeline-me-displayfull .timeline-me-showless, .timeline-me-smallitem.timeline-me-displayfull .timeline-me-showless {
display: block;
}
.timeline-me-smallitem .timeline-me-shortcontent {
display: block;
}
.timeline-me-smallitem .timeline-me-fullcontent {
display: none;
}
/* --------------- *\
BIGITEM ELEMENT
\* --------------- */
.timeline-me-bigitem {
/* to avoid "not clickable" effect on flipped element */
}
.timeline-me-bigitem .timeline-me-label {
position: relative;
margin-bottom: 10px;
text-align: center;
padding-bottom: 39px;
line-height: 36px;
}
.timeline-me-bigitem .timeline-me-label .timeline-me-picto {
position: absolute;
box-sizing: content-box;
height: 30px;
width: 30px;
background-color: #ffffff;
border-radius: 50%;
border: 3px solid #555555;
z-index: 100;
bottom: 0px;
left: 50%;
margin-left: -18px;
text-align: center;
overflow: hidden;
}
.timeline-me-bigitem .timeline-me-content-wrapper {
position: relative;
z-index: 1000;
}
.timeline-me-bigitem .timeline-me-content-container {
width: 80%;
margin: auto;
}
.timeline-me-bigitem .timeline-me-fullcontent,
.timeline-me-bigitem .timeline-me-shortcontent {
padding: 20px;
}
.timeline-me-bigitem .timeline-me-fullcontent .timeline-me-same-position,
.timeline-me-bigitem .timeline-me-shortcontent .timeline-me-same-position {
display: block;
border-top: 2px solid #666666;
margin-top: 10px;
padding-top: 10px;
}
.timeline-me-bigitem .timeline-me-flipped .timeline-me-showmore, .timeline-me-bigitem.timeline-me-flipped .timeline-me-showmore {
display: none;
}
.timeline-me-bigitem .timeline-me-flipped .timeline-me-showless, .timeline-me-bigitem.timeline-me-flipped .timeline-me-showless {
display: block;
}
.timeline-me-horizontal .timeline-me-bigitem .timeline-me-item-wrapper {
display: table;
}
.timeline-me-horizontal .timeline-me-bigitem .timeline-me-item-wrapper .timeline-me-label-wrapper,
.timeline-me-horizontal .timeline-me-bigitem .timeline-me-item-wrapper .timeline-me-content-wrapper {
display: table-cell;
vertical-align: middle;
}
.timeline-me-horizontal .timeline-me-bigitem .timeline-me-label {
margin-bottom: 0;
bottom: 18px;
}
.timeline-me-horizontal .timeline-me-bigitem .timeline-me-content-container {
width: 100%;
margin: 0;
}
/* --------------- *\
BIGITEM 3D-FLIP
\* --------------- */
/* entire container, keeps perspective */
.timeline-me-bigitem {
-moz-perspective: 1500px;
-webkit-perspective: 1500px;
-ms-perspective: 1500px;
-o-perspective: 1500px;
perspective: 1500px;
-moz-transform-style: preserve-3d;
-webkit-transform-style: preserve-3d;
-ms-transform-style: preserve-3d;
-o-transform-style: preserve-3d;
transform-style: preserve-3d;
/* flip the pane when hovered */
/* Configure flip speed */
/* Hide back of pane during flip */
/* Front pane, placed above back */
/* Back pane, initially hidden pane */
}
.timeline-me-bigitem.timeline-me-flipped .timeline-me-content-container {
-moz-transform: rotateY(180deg);
-webkit-transform: rotateY(180deg);
-ms-transform: rotateY(180deg);
-o-transform: rotateY(180deg);
transform: rotateY(180deg);
}
.timeline-me-bigitem .timeline-me-content-container {
-webkit-transition: 0.6s;
-moz-transition: 0.6s;
-ms-transition: 0.6s;
-o-transition: 0.6s;
transition: 0.6s;
-moz-transform-style: preserve-3d;
-webkit-transform-style: preserve-3d;
-ms-transform-style: preserve-3d;
-o-transform-style: preserve-3d;
transform-style: preserve-3d;
position: relative;
}
.timeline-me-bigitem .timeline-me-shortcontent, .timeline-me-bigitem .timeline-me-fullcontent {
-moz-backface-visibility: hidden;
-webkit-backface-visibility: hidden;
-ms-backface-visibility: hidden;
-o-backface-visibility: hidden;
backface-visibility: hidden;
-moz-transform-style: preserve-3d;
-webkit-transform-style: preserve-3d;
-ms-transform-style: preserve-3d;
-o-transform-style: preserve-3d;
transform-style: preserve-3d;
position: absolute;
top: 0;
left: 0;
}
.timeline-me-bigitem .timeline-me-shortcontent {
z-index: 2;
-moz-transform: rotateY(0deg);
-webkit-transform: rotateY(0deg);
-ms-transform: rotateY(0deg);
-o-transform: rotateY(0deg);
transform: rotateY(0deg);
}
.timeline-me-bigitem .timeline-me-fullcontent {
-moz-transform: rotateY(180deg);
-webkit-transform: rotateY(180deg);
-ms-transform: rotateY(180deg);
-o-transform: rotateY(180deg);
transform: rotateY(180deg);
}
/* -------------- *\
COLLAPSED MODE
\* -------------- */
.timeline-me-collapsed .timeline-me-smallitem .timeline-me-content-wrapper,
.timeline-me-collapsed .timeline-me-bigitem .timeline-me-content-wrapper, .timeline-me-collapsed.timeline-me-horizontal .timeline-me-smallitem .timeline-me-content-wrapper, .timeline-me-collapsed.timeline-me-horizontal .timeline-me-bigitem .timeline-me-content-wrapper {
display: none;
}
.timeline-me-collapsed .timeline-me-smallitem .timeline-me-label,
.timeline-me-collapsed .timeline-me-bigitem .timeline-me-label {
position: relative;
margin: 0;
}
.timeline-me-collapsed.timeline-me-vertical .timeline-me-bigitem .timeline-me-label {
padding-bottom: 0;
left: 50%;
top: 0;
margin-left: -18px;
text-align: left;
padding-left: 39px;
}
.timeline-me-collapsed.timeline-me-vertical .timeline-me-bigitem .timeline-me-label .timeline-me-picto {
top: 0;
left: 0px;
margin-left: 0;
}
.timeline-me-collapsed.timeline-me-horizontal .timeline-me-smallitem .timeline-me-label {
text-align: center;
}
.timeline-me-collapsed.timeline-me-horizontal .timeline-me-smallitem .timeline-me-label .timeline-me-picto {
left: 50%;
margin-left: -18px;
}
.timeline-me-collapsed.timeline-me-horizontal .timeline-me-smallitem.timeline-me-top .timeline-me-label-wrapper {
padding-top: 36px;
}
.timeline-me-collapsed.timeline-me-horizontal .timeline-me-smallitem.timeline-me-bottom .timeline-me-label-wrapper {
padding-bottom: 36px;
}

View File

@ -1,891 +0,0 @@
/*--------------------------------------------------------------------
*JAVASCRIPT "timelineMe.js"
*Version: 0.1.9 - 2016
*author: Mickaël Roy
*website: http://www.mickaelroy.com
*Licensed MIT
-----------------------------------------------------------------------*/
(function ($){
var pluginName = 'timelineMe';
/**
* The plugin constructor
* @param {DOM Element} element The DOM element where plugin is applied
* @param {Object} options Options passed to the constructor
*/
function TimelineMe(element, options) {
// Store a reference to the source element
this.el = element;
// Store a jQuery reference to the source element
this.$el = $(element);
// Set a random (and normally unique) id for the object
this.instanceId = Math.round(new Date().getTime() + (Math.random() * 100));
// Set the instance options extending the plugin defaults and
// the options passed by the user
this.settings = $.extend({}, $.fn[pluginName].defaults, options);
// Initialize the plugin instance
this.init();
}
/**
* Set up your Plugin protptype with desired methods.
* It is a good practice to implement 'init' and 'destroy' methods.
*/
TimelineMe.prototype = {
/**
* Initialize the plugin instance.
* Set any other attribtes, store any other element reference, register
* listeners, etc
*
* When bind listerners remember to name tag it with your plugin's name.
* Elements can have more than one listener attached to the same event
* so you need to tag it to unbind the appropriate listener on destroy:
*
* @example
* this.$someSubElement.on('click.' + pluginName, function() {
* // Do something
* });
*
*/
init: function() {
this.$el.addClass('timeline-me-container');
if(this.settings.orientation == 'horizontal') {
this.$el.addClass('timeline-me-horizontal');
if(this.settings.scrollBar == false)
this.$el.addClass('no-x-scroll');
} else {
this.$el.addClass('timeline-me-vertical');
if(this.settings.scrollBar == false)
this.$el.addClass('no-y-scroll');
}
var timelineWrapper = $('<div class="timeline-me-wrapper">');
this.$el.append(timelineWrapper);
var track = $('<div class="timeline-me-track">');
timelineWrapper.append(track);
if(this.settings.scrollZones == true) {
var leftScroll = $('<div class="timeline-me-leftscroll">');
var rightScroll = $('<div class="timeline-me-rightscroll">');
timelineWrapper.before(leftScroll);
timelineWrapper.after(rightScroll);
bindScrollTo(leftScroll, rightScroll, timelineWrapper);
}
if(this.settings.scrollArrows == true) {
var leftScroll;
var rightScroll;
if(this.settings.leftArrowElm == undefined) {
leftScroll = $('<span class="timeline-me-leftarrow">');
} else {
leftScroll = $('<span class="timeline-me-leftarrow-c">');
leftScroll.html(this.settings.leftArrowElm);
}
if(this.settings.rightArrowElm == undefined) {
rightScroll = $('<span class="timeline-me-rightarrow">');
} else {
rightScroll = $('<span class="timeline-me-rightarrow-c">');
rightScroll.html(this.settings.rightArrowElm);
}
timelineWrapper.before(leftScroll);
timelineWrapper.after(rightScroll);
bindScrollTo(leftScroll, rightScroll, timelineWrapper);
}
if(this.settings.items && this.settings.items.length > 0) {
this.content = this.settings.items;
if(hasNumberProperty(this.content, 'relativePosition')) {
this._sortItemsPosition(this.content);
this._mergeSamePositionItems(this.content);
this._calcDiffWithNextItem(this.content);
}
this._sortItemsPosition(this.content);
this._fillItemsPosition(this.content);
for(var i = 0; i < this.content.length; i++) {
track.append(this._createItemElement(this.content[i]));
if(this.settings.orientation == 'horizontal') {
resolveContainerWidth(track);
}
}
// once items' DOM elements have been built, we can reposition them
if(hasNumberProperty(this.content, 'relativePosition')) {
this._calcRelativePosRatio(this.content);
this.maxRelativeRatio = getMaxPropertyValue(this.content, 'relativePosRatio');
this._calcRelativeHeight(this.content, this.maxRelativeRatio);
this._addMissingRelativeHeight(this.content);
}
}
},
/**
* The 'destroy' method is were you free the resources used by your plugin:
* references, unregister listeners, etc.
*
* Remember to unbind for your event:
*
* @example
* this.$someSubElement.off('.' + pluginName);
*
* Above example will remove any listener from your plugin for on the given
* element.
*/
destroy: function() {
// Remove classes added by the plugin
this.$el.removeClass('timeline-me-container');
this.$el.removeClass('timeline-me-horizontal');
this.$el.removeClass('timeline-me-vertical');
this.$el.removeClass('no-x-scroll');
this.$el.removeClass('no-y-scroll');
// Remove child nodes
this.$el.empty();
// Remove any attached data from your plugin
this.$el.removeData();
},
// To call a call a pseudo private method:
//this._pseudoPrivateMethod();
// To call a real private method from those public methods, you need to use 'call' or 'apply':
//privateMethod.call(this);
/**
* getItem method
*
* @example
* $('#element').pluginName('getItem', index);
*
* @return {item} corresponding to the specified index
*/
getItem: function(index) {
return this.content[index];
},
/**
* getItemIndex method
*
* @example
* $('#element').pluginName('getItemIndex', item);
*
* @return {index} of the corresponding item
*/
getItemIndex: function(item) {
return this.settings.items.indexOf(item);
},
/**
* collapse method
*
* @example
* $('#element').pluginName('collapse', 'show');
*/
collapse: function(method) {
switch(method) {
case 'toggle':
this.$el.toggleClass('timeline-me-collapsed');
break;
case 'show':
this.$el.removeClass('timeline-me-collapsed');
break;
case 'hide':
default:
this.$el.addClass('timeline-me-collapsed');
break;
}
},
/**
* load method
*
* @example
* $('#element').pluginName('load', itemsToAdd);
*/
load: function(items) {
if(!items) return;
if(!this.settings.items) this.settings.items = [];
if(!$.isArray(items)) items = [items];
this.settings.items = this.settings.items.concat(items);
this.destroy();
this.init();
},
/**
* You can use the name convention functions started with underscore are
* private. Really calls to functions starting with underscore are
* filtered, for example:
*
* @example
* $('#element').jqueryPlugin('_pseudoPrivateMethod'); // Will not work
*/
// Method that create ids for items that does not have one
_fillItemsId: function(items) {
if(!items)
return;
var existingIds = [];
var lastId;
for(var i = 0; i < items.length; i++) {
if(items[i].id) {
existingIds.push(items[i].id);
if(!lastId)
lastId = items[i].id;
else
lastId = Math.max(lastId, items[i].id);
}
}
for(var j = 0; j < items.length; j++) {
if(!items[j].id) {
lastId++;
items[j].id = lastId;
}
}
return items;
},
// Method that sort items, depending on their relativePosition
_sortItemsPosition: function(items) {
var hasRelativePositioning = hasNumberProperty(items, 'relativePosition');
if(hasRelativePositioning) {
items.sort(function(a, b) {
return a.relativePosition - b.relativePosition;
});
}
},
// Method that merges items with same relativePosition
// !!! When 2 (or more) items have the same relativePosition, only the first item's type & picto will be taken into account !!!
_mergeSamePositionItems: function(items) {
var n = items.length - 1;
for(var i = 0; i < n; i++) {
if(items[i].relativePosition == items[i + 1].relativePosition) {
items[i].label = items[i].label + '<span class="timeline-me-same-position">' + items[i + 1].label + '</span>';
items[i].shortContent = items[i].shortContent + '<span class="timeline-me-same-position">' + items[i + 1].shortContent + '</span>';
items[i].fullContent = items[i].fullContent + '<span class="timeline-me-same-position">' + items[i + 1].fullContent + '</span>';
items.splice(i + 1, 1);
n--;
}
}
},
// Method that calculates relativePosition difference with next item
_calcDiffWithNextItem: function(items) {
var hasRelativePositioning = hasNumberProperty(items, 'relativePosition');
for(var i = 0; i < items.length; i++) {
if(i == items.length - 1)
items[i].diffWithNextRelativePos = undefined;
else
items[i].diffWithNextRelativePos = items[i + 1].relativePosition - items[i].relativePosition;
}
},
// Method that calculates ratio: item's height / relative pos diff with next item
_calcRelativePosRatio: function(items) {
for(var i = 0; i < items.length; i++) {
var height = items[i].element[0].getBoundingClientRect().height;
var relPosDiff = items[i].diffWithNextRelativePos;
var ratio = 0;
if(!isNaN(height) && !isNaN(relPosDiff))
ratio = height / relPosDiff;
items[i].relativePosRatio = ratio;
}
},
// Method that calculates items' "virtual" height depending on relative positioning
_calcRelativeHeight: function(items, heightRatio) {
if(!heightRatio) return;
for(var i = 0; i < items.length; i++) {
items[i].relativeHeight = heightRatio * (items[i].diffWithNextRelativePos ? items[i].diffWithNextRelativePos : 0);
}
},
// Method that calculates items' "virtual" height depending on relative positioning
_addMissingRelativeHeight: function(items) {
for(var i = 0; i < items.length; i++) {
var heightToAdd = 0;
if(!isNaN(items[i].relativeHeight)) {
var height = items[i].element[0].getBoundingClientRect().height;
heightToAdd = items[i].relativeHeight - height;
var marginBottom = parseInt(items[i].element.css('margin-bottom')) + (heightToAdd > 0 ? heightToAdd : 0);
items[i].element.css('margin-bottom', marginBottom + 'px');
}
}
},
// Method that fill 'position' field, depending on item's forcePosition option, on item's type and on position of the previous item
_fillItemsPosition: function(items) {
if(!items)
return;
var positions;
if(this.settings.orientation == 'horizontal')
positions = ['top', 'bottom'];
else
positions = ['left', 'right'];
for(var i = 0; i < this.content.length; i++) {
if(this.content[i].forcePosition && positions.indexOf(this.content[i].forcePosition) >= 0) {
this.content[i].position = this.content[i].forcePosition;
} else if(!this.content[i].position) {
switch(this.content[i].type) {
case 'milestone':
if(this.settings.orientation == 'horizontal')
this.content[i].position = 'top';
else
this.content[i].position = 'right';
break;
case 'smallItem':
if(i == 0)
this.content[i].position = this.settings.orientation == 'horizontal' ? 'top' : 'left';
else if(this.settings.orientation == 'horizontal' && this.content[i - 1].position == 'top')
this.content[i].position = 'bottom';
else if(this.settings.orientation == 'horizontal' && this.content[i - 1].position == 'bottom')
this.content[i].position = 'top';
else if(this.settings.orientation != 'horizontal' && this.content[i - 1].position == 'left')
this.content[i].position = 'right';
else if(this.settings.orientation != 'horizontal' && this.content[i - 1].position == 'right')
this.content[i].position = 'left';
else
this.content[i].position = this.settings.orientation == 'horizontal' ? 'top' : 'left';
break;
case 'bigItem':
break;
}
}
}
return items;
},
// Method that refresh item's class depending on its 'position' property
_refreshItemPosition: function(item) {
if(!item || (item && !item.element) || (item && !item.position))
return;
switch(item.position) {
case 'left':
item.element
.removeClass('timeline-me-top')
.removeClass('timeline-me-right')
.removeClass('timeline-me-bottom')
.addClass('timeline-me-left');
break;
case 'top':
item.element
.removeClass('timeline-me-left')
.removeClass('timeline-me-right')
.removeClass('timeline-me-bottom')
.addClass('timeline-me-top');
break;
case 'right':
item.element
.removeClass('timeline-me-top')
.removeClass('timeline-me-left')
.removeClass('timeline-me-bottom')
.addClass('timeline-me-right');
break;
case 'bottom':
item.element
.removeClass('timeline-me-top')
.removeClass('timeline-me-right')
.removeClass('timeline-me-left')
.addClass('timeline-me-bottom');
break;
}
return item;
},
// Method that create the item's html structure and fill it
_createItemElement: function(item) {
var itemElm;
switch(item.type) {
case 'milestone':
itemElm = this._buildMilestoneElement(item);
break;
case 'smallItem':
itemElm = this._buildSmallItemElement(item);
break;
case 'bigItem':
itemElm = this._buildBigItemElement(item);
break;
}
item.element = itemElm;
this._refreshItemPosition(item);
item.element.on('timelineMe.itemHeightChanged', function(event) {
// Do some stuff
});
item.element.on('timelineMe.smallItem.displayfull', function(event) {
// Do some stuff
});
item.element.on('timelineMe.bigItem.flipped', function(event) {
var container = item.element.find('.timeline-me-content-container');
if(item.element.hasClass('timeline-me-flipped')) {
container.height(item.fullContentElement.outerHeight());
} else {
container.height(item.shortContentElement.outerHeight());
}
});
this._buildItemContent(item);
this._fillItem(item);
return itemElm;
},
// Method that build html element for corresponding item
_buildMilestoneElement: function(item) {
var milestoneElm = $('<div class="timeline-me-item timeline-me-milestone">');
return milestoneElm;
},
// Method that build html element for corresponding item
_buildSmallItemElement: function(item) {
var smallItemElm = $('<div class="timeline-me-item timeline-me-smallitem">');
return smallItemElm;
},
// Method that build html element for corresponding item
_buildBigItemElement: function(item) {
var bigItemElm = $('<div class="timeline-me-item timeline-me-bigitem">');
return bigItemElm;
},
// Method that fills the item's element with some useful html structure
_buildItemContent: function(item) {
if(!item || !item.element)
return;
var pixelsRegex = /[0-9]+px$/;
// Following wrapper are only used in horizontal mode, in order to correctly display bigItems (with table display)
var itemWrapper = $('<div class="timeline-me-item-wrapper">');
var labelWrapper = $('<div class="timeline-me-label-wrapper">');
var contentWrapper = $('<div class="timeline-me-content-wrapper">');
var labelElm = $('<div class="timeline-me-label">');
item.labelElement = labelElm;
if(this.settings.orientation == 'horizontal' && this.settings.labelDimensionValue && pixelsRegex.test(this.settings.labelDimensionValue)) {
labelElm.css('width', this.settings.labelDimensionValue);
}
var pictoElm = $('<div class="timeline-me-picto">');
item.pictoElement = pictoElm;
labelWrapper.append(labelElm);
itemWrapper.append(labelWrapper);
if(item.type == 'smallItem' || item.type == 'bigItem') {
var contentContainer = $('<div class="timeline-me-content-container">');
var contentElm = $('<div class="timeline-me-content"></div>');
contentContainer.append(contentElm);
if(this.settings.orientation == 'horizontal' && this.settings.contentDimensionValue && pixelsRegex.test(this.settings.contentDimensionValue)) {
contentElm.css('width', this.settings.contentDimensionValue);
}
var shortContentElm = $('<div class="timeline-me-shortcontent">');
contentElm.append(shortContentElm);
item.shortContentElement = shortContentElm;
var fullContentElm = $('<div class="timeline-me-fullcontent">');
contentElm.append(fullContentElm);
item.fullContentElement = fullContentElm;
var showMoreElm = $('<div class="timeline-me-showmore">');
item.showMoreElement = showMoreElm;
var showLessElm = $('<div class="timeline-me-showless">');
item.showLessElement = showLessElm;
contentWrapper.append(contentContainer);
itemWrapper.append(contentWrapper);
}
item.element.append(itemWrapper);
item.itemWrapperElement = itemWrapper;
item.labelWrapperElement = labelWrapper;
item.contentWrapperElement = contentWrapper;
},
// Method that fills the item's element with content passed through the options
_fillItem: function(item) {
if((item.label || item.labelTemplate) && item.labelElement) {
item.label ? item.labelElement.html(item.label) : item.labelElement.html(loadHTMLFromFile(item.labelTemplate));
item.labelElement.append(item.pictoElement);
}
if(this.settings.labelClass && item.labelElement) {
item.labelElement.addClass(this.settings.labelClass);
}
if(item.picto || item.pictoTemplate) {
item.picto ? item.pictoElement.html(item.picto) : item.pictoElement.html(loadHTMLFromFile(item.pictoTemplate));
}
if((item.shortContent || item.shortContentTemplate) && item.shortContentElement) {
item.shortContent ? item.shortContentElement.html(item.shortContent) : item.shortContentElement.html(loadHTMLFromFile(item.shortContentTemplate));
/*
// Two solutions to get element's height:
resolveElementHeight(item.shortContentElement).then(function(data) {
console.log('resolved');
console.log(data);
});
eventElementHeight(item.shortContentElement, {eventName: 'timelineMe.heightChanged'});
*/
if(item.type == 'bigItem') {
// Use height watcher:
heightWatcher(item.shortContentElement, function(data) {
if(data && data.element) {
var container = data.element.closest('.timeline-me-content-container');
if(!data.oldVal && data.newVal && !data.element.hasClass('timeline-me-displayfull'))
container.height(data.newVal);
}
});
heightWatcher(item.fullContentElement, function(data) {
if(data && data.element) {
var container = data.element.closest('.timeline-me-content-container');
if(data.newVal && !data.oldVal && data.element.hasClass('timeline-me-displayfull'))
container.height(data.newVal);
}
});
}
}
if(this.settings.shortContentClass && item.shortContentElement) {
item.shortContentElement.addClass(this.settings.shortContentClass);
}
if((item.fullContent || item.fullContentTemplate) && item.fullContentElement) {
item.fullContent ? item.fullContentElement.html(item.fullContent) : item.fullContentElement.html(loadHTMLFromFile(item.fullContentTemplate));
}
if(this.settings.fullContentClass && item.fullContentElement) {
item.fullContentElement.addClass(this.settings.fullContentClass);
}
if((item.showMore || item.showMoreTemplate) && item.showMoreElement) {
item.showMore ? item.showMoreElement.html(item.showMore) : item.showMoreElement.html(loadHTMLFromFile(item.showMoreTemplate));
if(item.shortContentElement)
item.shortContentElement.append(item.showMoreElement);
if(item.type == 'smallItem') {
item.showMoreElement.on('click', function() {
item.element.toggleClass('timeline-me-displayfull');
item.element.trigger('timelineMe.smallItem.displayfull');
});
} else if(item.type == 'bigItem') {
item.showMoreElement.on('click', function() {
item.element.toggleClass('timeline-me-flipped');
item.element.trigger('timelineMe.bigItem.flipped');
});
}
}
if(this.settings.showMoreClass && item.showMoreElement) {
item.showMoreElement.addClass(this.settings.showMoreClass);
}
if((item.showLess || item.showLessTemplate) && item.showLessElement) {
item.showLess ? item.showLessElement.html(item.showLess) : item.showLessElement.html(loadHTMLFromFile(item.showLessTemplate));
if(item.fullContentElement)
item.fullContentElement.append(item.showLessElement);
if(item.type == 'smallItem') {
item.showLessElement.on('click', function() {
item.element.toggleClass('timeline-me-displayfull');
item.element.trigger('timelineMe.smallItem.displayfull');
});
} else if(item.type == 'bigItem') {
item.showLessElement.on('click', function() {
item.element.toggleClass('timeline-me-flipped');
item.element.trigger('timelineMe.bigItem.flipped');
});
}
}
if(this.settings.showLessClass && item.showLessElement) {
item.showLessElement.addClass(this.settings.showLessClass);
}
// if the timeline is in horizontal mode, we create a cloned version of the contentWrapper element, that will be hidden,
// in order to place it correctly above/below the timeline (will simulate a margin-bottom/margin-top equals to the height of the contentWrapper)
if(item.type == 'smallItem' && this.settings.orientation == 'horizontal') {
if(item.contentWrapperClone)
delete item.contentWrapperClone;
var contentWrapperClone = item.contentWrapperElement.clone();
if(item.position == 'top')
item.contentWrapperElement.after(contentWrapperClone);
if(item.position == 'bottom')
item.contentWrapperElement.before(contentWrapperClone);
contentWrapperClone.addClass('timeline-me-hidden');
item.contentWrapperClone = contentWrapperClone;
}
},
// Method that will refresh items' elements (still in progress)
_refreshItems: function() {
for(var i = 0; i < this.content.length; i++) {
if(!this.content[i].element || !this._isItemClassCorrespondingToType(this.content[i])) {
this._createItemElement(this.content[i])
}
this._fillItem(this.content[i]);
}
},
// Method that checks if an item's element has the class defined by his type
_isItemClassCorrespondingToType: function(item) {
if(!item || !item.type || !item.element)
return false;
switch(item.type) {
case 'milestone':
return (item.element.hasClass('timeline-milestone'));
break;
case 'smallItem':
return (item.element.hasClass('timeline-smallitem'));
break;
case 'bigItem':
return (item.element.hasClass('timeline-bigitem'));
break;
default:
return false;
break;
}
}
};
/**
* These are real private methods. A plugin instance has access to them
* @return {[type]}
*/
// Method that bind left & right scroll to any scrollable element
var bindScrollTo = function(leftScrollElm, rightScrollElm, elmToScroll, scrollSpeed) {
var scrollSpeed = scrollSpeed ? scrollSpeed : 5;
leftScrollElm.on('mouseenter', function() {
var timer = setInterval(function() {
elmToScroll.scrollLeft(elmToScroll.scrollLeft() - scrollSpeed);
}, 20);
leftScrollElm.on('mouseleave', function() {
clearInterval(timer);
});
});
rightScrollElm.on('mouseenter', function() {
var timer = setInterval(function() {
elmToScroll.scrollLeft(elmToScroll.scrollLeft() + scrollSpeed);
}, 20);
rightScrollElm.on('mouseleave', function() {
clearInterval(timer);
});
});
}
// Method that return container width depending on children's width
var resolveContainerWidth = function(element) {
if(!element) return;
var children = element.children();
if(children.length <= 0) return;
var totalWidth = 0;
for(var i = 0; i < children.length; i++) {
totalWidth += $(children[i]).width();
}
element.width(totalWidth);
};
// Method that can return an element's height through a promise (so it'll be resolve only when the element will have a positive height)
var resolveElementHeight = function(element, args) {
if(!args)
args = {};
var refreshDelay = args.refreshDelay ? args.refreshDelay : 500;
var ret = new $.Deferred();
var elmHeight;
if(element)
elmHeight = element.height();
if(elmHeight && elmHeight > 0) {
ret.resolve(elmHeight);
} else {
setTimeout(function () {
resolveElementHeight(element, args).then(function(heightData) {
ret.resolve(heightData);
});
}, refreshDelay);
}
return ret;
}
// Method that can return an element's height through an event (event will be fired only when the element will have a positive height)
var eventElementHeight = function(element, args) {
if(!args)
args = {};
var eventName = args.eventName ? args.eventName : 'onElementHeight';
var refreshDelay = args.refreshDelay ? args.refreshDelay : 500;
var elmHeight;
if(element)
elmHeight = element.height();
if(elmHeight && elmHeight > 0) {
var e = jQuery.Event(eventName, {elementHeight: elmHeight});
element.trigger(e);
} else {
setTimeout(function () {
eventElementHeight(element, args);
}, refreshDelay);
}
}
// Method that can return an element's height when it's changing
var resolveElementHeightChange = function(element, args) {
if(!args)
args = {};
var refreshDelay = args.refreshDelay ? args.refreshDelay : 500;
var previousHeight = args.previousHeight;
var level = args.level ? args.level : 0;
var ret = new $.Deferred();
var elmHeight;
if(element)
elmHeight = element[0].getBoundingClientRect().height;
if(elmHeight && (!previousHeight || previousHeight != elmHeight)) {
ret.resolve(elmHeight, previousHeight, level);
} else {
args.previousHeight = elmHeight;
setTimeout(function () {
resolveElementHeightChange(element, {previousHeight: elmHeight, refreshDelay: refreshDelay, level: (level + 1)}).then(function(newHeightVal, previousHeightVal, levelVal) {
ret.resolve(newHeightVal, previousHeightVal, level);
});
}, refreshDelay);
}
return ret;
}
// Method that can watch for an element's height change (uses the resolveElementHeightChange function)
var heightWatcher = function(element, callback) {
resolveElementHeightChange(element).then(function(newVal, oldVal) {
var heightEvent = jQuery.Event('timelineMe.itemHeightChanged', {elementHeight: newVal, previousHeight: oldVal});
element.trigger(heightEvent);
});
element.on('timelineMe.itemHeightChanged', function(e) {
callback({element: element, newVal: e.elementHeight, oldVal: e.previousHeight});
resolveElementHeightChange($(e.target), {previousHeight: e.elementHeight}).then(function(newVal, oldVal) {
var heightEvent = jQuery.Event('timelineMe.itemHeightChanged', {elementHeight: newVal, previousHeight: oldVal});
$(e.target).trigger(heightEvent);
});
});
}
// Method checking if all items of an array have a specific number property
var hasNumberProperty = function(items, propertyName) {
if(!items) return false;
var hasProperty = true;
for(var i = 0; i < items.length; i++) {
if(isNaN(items[i][propertyName]))
hasProperty = false;
}
return hasProperty;
}
// Method returning max value of specific array's property
var getMaxPropertyValue = function(items, propertyName) {
if(!items) return false;
var maxProperty;
for(var i = 0; i < items.length; i++) {
if(maxProperty == undefined)
maxProperty = items[i][propertyName];
else if(items[i][propertyName] > maxProperty)
maxProperty = items[i][propertyName];
}
return maxProperty;
}
//
var loadHTMLFromFile = function(url) {
var html;
$.ajax({
url: url,
success: function (result) {
html = result;
},
async: false
});
return html;
}
$.fn[pluginName] = function(options) {
var args = arguments;
if (options === undefined || typeof options === 'object') {
// Creates a new plugin instance, for each selected element, and
// stores a reference withint the element's data
return this.each(function() {
if (!$.data(this, 'plugin_' + pluginName)) {
$.data(this, 'plugin_' + pluginName, new TimelineMe(this, options));
}
});
} else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {
// Call a public plugin method (not starting with an underscore) for each
// selected element.
if ($.inArray(options, $.fn[pluginName].getters) !== -1) {
// If the user does not pass any arguments and the method allows to
// work as a getter then break the chainability so we can return a value
// instead the element reference.
var instance = $.data(this[0], 'plugin_' + pluginName);
return instance[options].apply(instance, Array.prototype.slice.call(args, 1));
} else {
// Invoke the speficied method on each selected element
return this.each(function() {
var instance = $.data(this, 'plugin_' + pluginName);
if (instance instanceof TimelineMe && typeof instance[options] === 'function') {
instance[options].apply(instance, Array.prototype.slice.call(args, 1));
}
});
}
}
};
/**
* Names of the pluguin methods that can act as a getter method.
* @type {Array}
*/
$.fn[pluginName].getters = ['getItem', 'getItemIndex'];
/**
* Default options
*/
$.fn[pluginName].defaults = {
orientation : 'vertical',
items : [],
// horizontal-orientation specific options
contentDimensionValue : '400px',
labelDimensionValue : '200px',
scrollBar : true,
scrollZones : false,
scrollArrows : false,
leftArrowElm : undefined,
rightArrowElm : undefined
};
}(jQuery));

View File

@ -14,6 +14,72 @@
</span>
</h1>
<div class="row">
<div class="col-lg-6" style="padding-right: 10px;">
<div class="box-body ukefu-im-theme">
<div class="uk-layui-form">
<div class="layui-collapse">
<div class="layui-colla-item">
<h2 class="layui-colla-title">标签</h2>
<div class="layui-colla-content layui-show">
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label" style="text-align: left;">已有标签:</label>
<div id="contactTags" class="layui-input-inline" style="width: 100%;">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="box-body ukefu-im-theme">
<div class="uk-layui-form">
<div class="layui-collapse">
<div class="layui-colla-item">
<h2 class="layui-colla-title">笔记</h2>
<div class="layui-colla-content layui-show">
<div class="layui-form-item">
<label class="layui-form-label">笔记分类:</label>
<div class="layui-input-inline">
<select id="notesCategory" name="notesCategory" lay-filter="category" required lay-verify="required" style="display: inline">
<option value="callout">外呼</option>
<option value="callin">呼入</option>
<option value="webim">网页</option>
</select>
</div>
</div>
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">内容:</label>
<div class="layui-input-inline">
<textarea id="notesContent" name="notes" class="layui-textarea" ></textarea>
</div>
</div>
<div class="layui-button-block">
<button class="layui-btn" lay-submit lay-filter="notesbtn" id="notesAddBtn">立即添加</button>
</div>
</div>
<!--<div class="layui-form-item">-->
<!--<div class="layui-button-inline" style="float: right;">-->
<!--<button class="layui-btn" lay-submit lay-filter="notesbtn" id="notesAddBtn">立即添加</button>-->
<!--</div>-->
<!--</div>-->
</div>
</div>
</div>
</div>
</div>
<div class="layui-collapse">
<div class="layui-colla-item">
<h2 class="layui-colla-title">往来历史</h2>
<div class="layui-colla-content layui-show">
<div id="timeline" class="timeline-container" type="text" style="height: 617px; overflow-y: auto;">
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="box-body ukefu-im-theme">
<div class="uk-layui-form">
@ -141,77 +207,76 @@
</div>
</div>
</div>
<div class="box-body ukefu-im-theme" style="margin-top: 20px;">
<div class="uk-layui-form">
<div class="layui-collapse">
<div class="layui-colla-item">
<h2 class="layui-colla-title">标签</h2>
<div class="layui-colla-content layui-show">
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label" style="text-align: left;">已有标签:</label>
<div id="contactTags" class="layui-input-inline" style="width: 100%;">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="box-body ukefu-im-theme" style="margin-top: 20px;">
<div class="uk-layui-form">
<div class="layui-collapse">
<div class="layui-colla-item">
<h2 class="layui-colla-title">笔记</h2>
<div class="layui-colla-content layui-show">
<div class="layui-form-item">
<label class="layui-form-label" style="text-align: left;">笔记分类:</label>
<div class="layui-input-inline">
<select id="notesCategory" name="notesCategory" lay-filter="category" required lay-verify="required" style="display: inline">
<option value="callout">外呼</option>
<option value="callin">呼入</option>
<option value="webim">网页</option>
</select>
</div>
</div>
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label" style="text-align: left;">内容:</label>
<div class="layui-input-inline" style="width: 149%;">
<textarea id="notesContent" name="notes" class="layui-textarea" ></textarea>
</div>
</div>
</div>
<div class="layui-form-item">
<div class="layui-button-inline" style="float: right;">
<button class="layui-btn" lay-submit lay-filter="notesbtn" id="notesAddBtn">立即添加</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-6" style="padding-left: 10px;">
<div class="layui-collapse">
<div class="layui-colla-item">
<h2 class="layui-colla-title">往来历史</h2>
<div class="layui-colla-content layui-show">
<div id="timeline" class="timeline-container" type="text" style="height: 1243px; overflow-y: auto;">
</div>
</div>
</div>
</div>
<!--<div class="box-body ukefu-im-theme" style="margin-top: 20px;">-->
<!--<div class="uk-layui-form">-->
<!--<div class="layui-collapse">-->
<!--<div class="layui-colla-item">-->
<!--<h2 class="layui-colla-title">标签</h2>-->
<!--<div class="layui-colla-content layui-show">-->
<!--<div class="layui-form-item">-->
<!--<div class="layui-inline">-->
<!--<label class="layui-form-label" style="text-align: left;">已有标签:</label>-->
<!--<div id="contactTags" class="layui-input-inline" style="width: 100%;">-->
<!--</div>-->
<!--</div>-->
<!--</div>-->
<!--</div>-->
<!--</div>-->
<!--</div>-->
<!--</div>-->
<!--</div>-->
<!--<div class="box-body ukefu-im-theme" style="margin-top: 20px;">-->
<!--<div class="uk-layui-form">-->
<!--<div class="layui-collapse">-->
<!--<div class="layui-colla-item">-->
<!--<h2 class="layui-colla-title">笔记</h2>-->
<!--<div class="layui-colla-content layui-show">-->
<!--<div class="layui-form-item">-->
<!--<label class="layui-form-label" style="text-align: left;">笔记分类:</label>-->
<!--<div class="layui-input-inline">-->
<!--<select id="notesCategory" name="notesCategory" lay-filter="category" required lay-verify="required" style="display: inline">-->
<!--<option value="callout">外呼</option>-->
<!--<option value="callin">呼入</option>-->
<!--<option value="webim">网页</option>-->
<!--</select>-->
<!--</div>-->
<!--</div>-->
<!--<div class="layui-form-item">-->
<!--<div class="layui-inline">-->
<!--<label class="layui-form-label" style="text-align: left;">内容:</label>-->
<!--<div class="layui-input-inline" style="width: 149%;">-->
<!--<textarea id="notesContent" name="notes" class="layui-textarea" ></textarea>-->
<!--</div>-->
<!--</div>-->
<!--</div>-->
<!--<div class="layui-form-item">-->
<!--<div class="layui-button-inline" style="float: right;">-->
<!--<button class="layui-btn" lay-submit lay-filter="notesbtn" id="notesAddBtn">立即添加</button>-->
<!--</div>-->
<!--</div>-->
<!--</div>-->
<!--</div>-->
<!--</div>-->
<!--</div>-->
<!--</div>-->
</div>
<!--<div class="col-lg-6" style="padding-left: 10px;">-->
<!--<div class="layui-collapse">-->
<!--<div class="layui-colla-item">-->
<!--<h2 class="layui-colla-title">往来历史</h2>-->
<!--<div class="layui-colla-content layui-show">-->
<!--<div id="timeline" class="timeline-container" type="text" style="height: 1243px; overflow-y: auto;">-->
<!--</div>-->
<!--</div>-->
<!--</div>-->
<!--</div>-->
<!--</div>-->
</div>
</div>
</div>
</div>
<link rel="stylesheet" href="/js/timelineMe/jquery.timelineMe.css" type="text/css">
<script type="text/javascript" src="/js/timelineMe/jquery.timelineMe.js"></script>
<script src="/js/lodash-4.17.4.min.js"></script>
<script src="/js/CSKeFu_Rest_Request.v1.js"></script>
<style>
.unselectedClass {
@ -230,6 +295,25 @@
border: solid 1px #CCCCCC;
}
</style>
<style>
.card {
/* Add shadows to create the "card" effect */
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
transition: 0.3s;
margin-top: 5px;
}
/* On mouse-over, add a deeper shadow */
.card:hover {
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
}
/* Add some padding inside the card container */
.container {
width: 100%;
padding: 2px 16px;
}
</style>
<script>
// save time line datas
var items = [];
@ -253,17 +337,16 @@
// var items = [];
if(data.totalElements > 0) {
for(var item of data.data){
items.push({
type: 'bigItem',
label: item.updatetime,
picto: '<div style="background-color: #3ac3e8;width: 100%;height: 100%;"></div>',
shortContent: '<div style="border: solid 1px #333333; line-height: 2em;font-weight: 300;word-break:break-all"><p>事件类型:' + item.category + '</p>' + '<p>创建者:' + item.creatername + '</p>' + '<div><label>内容:</label><span>' + item.content + '</span></div></div>',
forcePosition: 'right'
});
$("#timeline").append('<div class="card">' +
'<div class="container">' +
'<h4>时间:' + item.updatetime + '</h4>' +
'<h5>笔记者:' + item.creatername + '</h5>' +
'<h5>事件类型:' + item.category + '</h5>' +
'<div>笔记:' + item.content + '</div>' +
'</div>' +
'</div>');
}
}
// time line
makeTimeline()
} else {
}
}, function (err) {
@ -271,22 +354,6 @@
});
}
// make time line
function makeTimeline() {
var el = $('#timeline');
if(el){
$(el).empty();
$('#timeline').timelineMe({});
$('#timeline').timelineMe('load',
items
);
} else {
$('#timeline').timelineMe({
items: items,
});
}
}
// unselect tag
function unOrselectTag(id) {
var el = $("#tag_" + id);
@ -382,7 +449,6 @@
});
}
layui.use('layer', function() {
layer = layui.layer;
@ -420,16 +486,14 @@
if(data.rc === 0){
var item = data.data;
// get all notes by contact id.
items.unshift({
type: 'bigItem',
label: item.updatetime,
picto: '<div style="background-color: #3ac3e8;width: 100%;height: 100%;"></div>',
shortContent: '<div style="border: solid 1px #333333; line-height: 2em;font-weight: 300;word-break:break-all"><p>事件类型:' + category + '</p>' + '<p>创建者:' + item.creatername + '</p>' + '<div><label>内容:</label><span>' + content + '</span></div></div>',
forcePosition: 'right'
});
// time line
makeTimeline()
$("#timeline").prepend('<div class="card">' +
'<div class="container">' +
'<h4><b>时间:' + item.updatetime + '</b></h4>' +
'<h5>笔记者:' + item.creatername + '</h5>' +
'<h5>事件类型:' + category + '</h5>' +
'<div>笔记:' + content + '</div>' +
'</div>' +
'</div>');
} else {
}
}, function (err) {