added GitHub API support (show release info)
new dialog (show release info) fixed caching timings (main ajax update trigger calls) fixed footer "current year" number
This commit is contained in:
82
app/main/controller/api/github.php
Normal file
82
app/main/controller/api/github.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodus4d
|
||||
* Date: 16.01.16
|
||||
* Time: 03:34
|
||||
*/
|
||||
|
||||
namespace Controller\Api;
|
||||
use Model;
|
||||
use Controller;
|
||||
|
||||
|
||||
/**
|
||||
* Github controller
|
||||
* Class Route
|
||||
* @package Controller\Api
|
||||
*/
|
||||
class GitHub extends Controller\Controller {
|
||||
|
||||
/**
|
||||
* get HTTP request options for API (curl) request
|
||||
* @return array
|
||||
*/
|
||||
protected function getRequestOptions(){
|
||||
$requestOptions = [
|
||||
'timeout' => 8,
|
||||
'method' => 'GET',
|
||||
'user_agent' => $this->getUserAgent(),
|
||||
'follow_location' => false // otherwise CURLOPT_FOLLOWLOCATION will fail
|
||||
];
|
||||
|
||||
return $requestOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* get release information from GitHub
|
||||
* @param $f3
|
||||
*/
|
||||
public function releases($f3){
|
||||
$cacheKey = 'CACHE_GITHUB_RELEASES';
|
||||
$ttl = 60 * 30; // 30min
|
||||
$releaseCount = 5;
|
||||
|
||||
if( !$f3->exists($cacheKey) ){
|
||||
$apiPath = $this->getF3()->get('PATHFINDER.API.GIT_HUB') . '/repos/exodus4d/pathfinder/releases';
|
||||
|
||||
// build request URL
|
||||
$options = $this->getRequestOptions();
|
||||
$apiResponse = \Web::instance()->request($apiPath, $options );
|
||||
|
||||
if($apiResponse['body']){
|
||||
// request succeeded -> format "Markdown" to "HTML"
|
||||
// result is JSON formed
|
||||
$releasesData = (array)json_decode($apiResponse['body']);
|
||||
|
||||
// check max release count
|
||||
if(count($releasesData) > $releaseCount){
|
||||
$releasesData = array_slice($releasesData, 0, $releaseCount);
|
||||
}
|
||||
|
||||
$md = \Markdown::instance();
|
||||
foreach($releasesData as &$releaseData){
|
||||
if(isset($releaseData->body)){
|
||||
$releaseData->body = $md->convert( $releaseData->body );
|
||||
}
|
||||
}
|
||||
$f3->set($cacheKey, $releasesData, $ttl);
|
||||
}else{
|
||||
// request failed -> cache failed result (respect API request limit)
|
||||
$f3->set($cacheKey, false, 60 * 5);
|
||||
}
|
||||
}
|
||||
|
||||
// set 503 if service unavailable or temp cached data = false
|
||||
if( !$f3->get($cacheKey) ){
|
||||
$f3->status(503);
|
||||
}
|
||||
|
||||
echo json_encode($f3->get($cacheKey));
|
||||
}
|
||||
}
|
||||
@@ -465,7 +465,7 @@ class Map extends \Controller\AccessController {
|
||||
$responseTTL = $f3->get('PATHFINDER.TIMER.UPDATE_SERVER_MAP.DELAY') / 1000;
|
||||
$mapData = (array)$f3->get('POST.mapData');
|
||||
|
||||
$user = $this->_getUser(0);
|
||||
$user = $this->_getUser();
|
||||
$return = (object) [];
|
||||
$return->error = [];
|
||||
|
||||
@@ -628,7 +628,7 @@ class Map extends \Controller\AccessController {
|
||||
$return = (object) [];
|
||||
$return->error = [];
|
||||
|
||||
$user = $this->_getUser(0);
|
||||
$user = $this->_getUser();
|
||||
|
||||
if($user){
|
||||
|
||||
|
||||
@@ -16,22 +16,6 @@ use Model;
|
||||
*/
|
||||
class CcpApiController extends Controller{
|
||||
|
||||
/**
|
||||
* get a custom userAgent string for API calls
|
||||
* (recommended by CCP)
|
||||
* @return string
|
||||
*/
|
||||
protected function getUserAgent(){
|
||||
$userAgent = '';
|
||||
|
||||
$userAgent .= $this->getF3()->get('PATHFINDER.NAME');
|
||||
$userAgent .= ' - ' . $this->getF3()->get('PATHFINDER.VERSION');
|
||||
$userAgent .= ' | ' . $this->getF3()->get('PATHFINDER.CONTACT');
|
||||
$userAgent .= ' (' . $_SERVER['SERVER_NAME'] . ')';
|
||||
|
||||
return $userAgent;
|
||||
}
|
||||
|
||||
/**
|
||||
* get HTTP request options for API (curl) request
|
||||
* @return array
|
||||
|
||||
@@ -434,6 +434,21 @@ class Controller {
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* get a custom userAgent string for API calls
|
||||
* @return string
|
||||
*/
|
||||
protected function getUserAgent(){
|
||||
$userAgent = '';
|
||||
|
||||
$userAgent .= $this->getF3()->get('PATHFINDER.NAME');
|
||||
$userAgent .= ' - ' . $this->getF3()->get('PATHFINDER.VERSION');
|
||||
$userAgent .= ' | ' . $this->getF3()->get('PATHFINDER.CONTACT');
|
||||
$userAgent .= ' (' . $_SERVER['SERVER_NAME'] . ')';
|
||||
|
||||
return $userAgent;
|
||||
}
|
||||
|
||||
/**
|
||||
* onError() callback function
|
||||
* -> on AJAX request -> return JSON with error information
|
||||
@@ -475,6 +490,7 @@ class Controller {
|
||||
if($f3->get('AJAX')){
|
||||
header('Content-type: application/json');
|
||||
echo json_encode($return);
|
||||
die();
|
||||
}else{
|
||||
// set error data for template rendering
|
||||
$error->redirectUrl = $this->getRouteUrl();
|
||||
|
||||
@@ -104,3 +104,4 @@ DELETE_ACCOUNT = delete_account
|
||||
[PATHFINDER.API]
|
||||
; Path for CCPs XML APIv2
|
||||
CCP_XML = https://api.eveonline.com
|
||||
GIT_HUB = https://api.github.com
|
||||
|
||||
@@ -43,7 +43,9 @@ define(['jquery'], function($) {
|
||||
saveSignatureData: 'api/signature/save', // ajax URL - save signature data for system
|
||||
deleteSignatureData: 'api/signature/delete', // ajax URL - delete signature data for system
|
||||
// route API
|
||||
searchRoute: 'api/route/search' // ajax URL - search system routes
|
||||
searchRoute: 'api/route/search', // ajax URL - search system routes
|
||||
// GitHub API
|
||||
gitHubReleases: 'api/github/releases' // ajax URL - get release info from GitHub
|
||||
},
|
||||
url: {
|
||||
ccpImageServer: 'https://image.eveonline.com/', // CCP image Server
|
||||
|
||||
@@ -17,6 +17,7 @@ define([
|
||||
'dialog/account_settings',
|
||||
'dialog/notification',
|
||||
'dialog/manual',
|
||||
'dialog/releases',
|
||||
'dialog/credit'
|
||||
], function($, Init, Util, Render, CCP, Gallery, bootbox) {
|
||||
|
||||
@@ -39,7 +40,8 @@ define([
|
||||
// navigation
|
||||
navigationElementId: 'pf-navbar', // id for navbar element
|
||||
navigationLinkManualClass: 'pf-navbar-manual', // class for "manual" trigger link
|
||||
navigationLinkLicenseClass : 'pf-navbar-license', // class for "license" trigger link
|
||||
navigationLinkLicenseClass: 'pf-navbar-license', // class for "license" trigger link
|
||||
navigationVersionLinkClass: 'pf-navbar-version-info', // class for "version information"
|
||||
|
||||
// login form
|
||||
loginFormId: 'pf-login-form', // id for login form
|
||||
@@ -130,6 +132,11 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
// releases -----------------------------------------------------
|
||||
$('.' + config.navigationVersionLinkClass).on('click', function(e){
|
||||
$.fn.releasesDialog();
|
||||
});
|
||||
|
||||
// manual -------------------------------------------------------
|
||||
$('.' + config.navigationLinkManualClass).on('click', function(e){
|
||||
e.preventDefault();
|
||||
|
||||
@@ -481,7 +481,8 @@ define([
|
||||
|
||||
var moduleData = {
|
||||
id: config.pageFooterId,
|
||||
footerLicenceLinkClass: config.footerLicenceLinkClass
|
||||
footerLicenceLinkClass: config.footerLicenceLinkClass,
|
||||
currentYear: new Date().getFullYear()
|
||||
};
|
||||
|
||||
var headRendered = Mustache.render(TplFooter, moduleData);
|
||||
|
||||
80
js/app/ui/dialog/releases.js
Normal file
80
js/app/ui/dialog/releases.js
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* releases dialog (GitHub API repository information)
|
||||
*/
|
||||
|
||||
define([
|
||||
'jquery',
|
||||
'app/init',
|
||||
'app/util',
|
||||
'app/render',
|
||||
'bootbox'
|
||||
], function($, Init, Util, Render, bootbox) {
|
||||
'use strict';
|
||||
|
||||
var config = {
|
||||
releasesDialogClass: 'pf-releases-dialog' // class for "Releases" dialog
|
||||
};
|
||||
|
||||
/**
|
||||
* load release information in dialog
|
||||
* @param releasesDialog
|
||||
*/
|
||||
var loadDialogData = function(releasesDialog){
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: Init.path.gitHubReleases,
|
||||
// data: updatedMapData,
|
||||
dataType: 'json'
|
||||
}).done(function(releasesData){
|
||||
requirejs(['text!templates/ui/timeline_element.html', 'mustache'], function(template, Mustache) {
|
||||
for(var i = 0; i < releasesData.length; i++){
|
||||
var releaseData = releasesData[i];
|
||||
|
||||
// template vars
|
||||
var data = {
|
||||
isFirst: (i === 0),
|
||||
isOdd: (i % 2 !== 0),
|
||||
releaseDate: releaseData.published_at.substr(0, 10),
|
||||
releaseData: releaseData
|
||||
};
|
||||
|
||||
var content = Mustache.render(template, data);
|
||||
releasesDialog.find('ul.timeline').append(content);
|
||||
}
|
||||
});
|
||||
}).fail(function( jqXHR, status, error) {
|
||||
var reason = status + ' ' + jqXHR.status + ': ' + error;
|
||||
Util.showNotify({title: jqXHR.status + ': login', text: reason, type: 'error'});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* show releases dialog
|
||||
*/
|
||||
$.fn.releasesDialog = function(){
|
||||
|
||||
requirejs(['text!templates/dialog/releases.html', 'mustache'], function(template, Mustache) {
|
||||
|
||||
var data = {
|
||||
test: 'blaBla'
|
||||
};
|
||||
|
||||
var content = Mustache.render(template, data);
|
||||
|
||||
var releasesDialog = bootbox.dialog({
|
||||
className: config.releasesDialogClass,
|
||||
title: 'Releases',
|
||||
size: 'large',
|
||||
message: content
|
||||
});
|
||||
|
||||
// after modal is shown =======================================================================
|
||||
releasesDialog.on('shown.bs.modal', function(e) {
|
||||
|
||||
loadDialogData(releasesDialog);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
});
|
||||
@@ -72,7 +72,7 @@ define([
|
||||
}
|
||||
},
|
||||
trust: {
|
||||
label: '<i class="fa fa-fw fa-lock"></i> trust set',
|
||||
label: '<i class="fa fa-fw fa-lock"></i> set "Trust"',
|
||||
className: 'btn-primary',
|
||||
callback: function(){
|
||||
var dialog = $(this);
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -96,6 +96,7 @@ app/ui/demo_map.js
|
||||
app/ui/dialog/account_settings.js
|
||||
app/ui/dialog/notification.js
|
||||
app/ui/dialog/manual.js
|
||||
app/ui/dialog/releases.js
|
||||
app/ui/dialog/credit.js
|
||||
app/landingpage.js
|
||||
lib/requirejs/text.js
|
||||
|
||||
2
public/templates/dialog/releases.html
Normal file
2
public/templates/dialog/releases.html
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
<ul class="timeline"></ul>
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="navbar-header pull-left">
|
||||
|
||||
<p class="navbar-text txt-color txt-color-gray"><i class="fa fa-copyright"></i>
|
||||
2015 <a class="{{footerLicenceLinkClass}}" href="javascript:void(0)" target="_blank">Licence</a>
|
||||
{{ currentYear }} <a class="{{footerLicenceLinkClass}}" href="javascript:void(0)" target="_blank">Licence</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="navbar-header pull-right">
|
||||
|
||||
19
public/templates/ui/timeline_element.html
Normal file
19
public/templates/ui/timeline_element.html
Normal file
@@ -0,0 +1,19 @@
|
||||
<li class="{{#isOdd}}timeline-inverted{{/isOdd}} {{#isFirst}}timeline-first{{/isFirst}}">
|
||||
<div class="timeline-badge"><i class="fa fa-lg fa-github fa-fw"></i></div>
|
||||
<div class="timeline-panel">
|
||||
<div class="timeline-heading">
|
||||
<h4 class="timeline-title">
|
||||
<a href="{{releaseData.html_url}}" target="_blank">{{ releaseData.name }}</a>
|
||||
|
||||
<span class="text-muted pull-right">
|
||||
{{ #releaseData.prerelease }}
|
||||
<span class="label label-danger">Pre-release</span>
|
||||
{{ /releaseData.prerelease }}
|
||||
<i class="fa fa-calendar fa-fw"></i> {{ releaseDate }}
|
||||
</span>
|
||||
</h4>
|
||||
<p><small class="text-muted"></small></p>
|
||||
</div>
|
||||
<div class="timeline-body">{{{ releaseData.body }}}</div>
|
||||
</div>
|
||||
</li>
|
||||
@@ -18,7 +18,7 @@
|
||||
</button>
|
||||
|
||||
<p class="navbar-text">
|
||||
<span class="badge txt-color txt-color-grayLight">{{ @PATHFINDER.VERSION }}</span>
|
||||
<span class="badge txt-color txt-color-green pf-navbar-version-info">{{ @PATHFINDER.VERSION }}</span>
|
||||
</p>
|
||||
|
||||
<ul class="nav navbar-nav navbar-right" role="tablist">
|
||||
@@ -130,6 +130,7 @@
|
||||
</div>
|
||||
</check>
|
||||
|
||||
{* signUp/logIn buttons *}
|
||||
<div class="row text-center">
|
||||
<div class="col-sm-6 col-sm-offset-3">
|
||||
<div class="col-xs-6 col-sm-4 col-sm-offset-2" data-placement="left" title="{{@registrationStatusTitle}}">
|
||||
@@ -145,6 +146,8 @@
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
{* features/ gallery *}
|
||||
<section id="pf-landing-gallery">
|
||||
|
||||
@@ -823,6 +826,7 @@
|
||||
<p>
|
||||
Unfortunately not. I will probably not have the time to answer any question personally.
|
||||
Make sure you have read the <span class="pf-navbar-manual"><a href="javascript:void(0);">manual</a></span>.
|
||||
<br>
|
||||
If there are still some open questions, please ask in the <a target="_blank" href="https://forums.eveonline.com/">official forums</a> thread for help.
|
||||
</p>
|
||||
<h3>Which browsers are compatible with <em>Pathfinder</em>?</h3>
|
||||
|
||||
@@ -728,14 +728,14 @@ $close-text-shadow: 0 1px 0 #fff;
|
||||
//
|
||||
//##
|
||||
|
||||
$code-color: #c7254e;
|
||||
$code-bg: #f9f2f4;
|
||||
$code-color: $gray-darkest;
|
||||
$code-bg: $gray-light;
|
||||
|
||||
$kbd-color: $gray-lighter;
|
||||
$kbd-bg: $gray-darker;
|
||||
|
||||
$pre-bg: #f5f5f5;
|
||||
$pre-color: $gray-dark;
|
||||
$pre-bg: $gray-light;
|
||||
$pre-color: $gray-darkest;
|
||||
$pre-border-color: #ccc;
|
||||
$pre-scrollable-max-height: 340px;
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ code {
|
||||
color: $code-color;
|
||||
background-color: $code-bg;
|
||||
white-space: nowrap;
|
||||
border-radius: $border-radius-base;
|
||||
border-radius: $border-radius-small;
|
||||
}
|
||||
|
||||
// User input typically entered via keyboard
|
||||
@@ -42,8 +42,8 @@ pre {
|
||||
word-wrap: break-word;
|
||||
color: $pre-color;
|
||||
background-color: $pre-bg;
|
||||
border: 1px solid $pre-border-color;
|
||||
border-radius: $border-radius-base;
|
||||
// border: 1px solid $pre-border-color;
|
||||
border-radius: $border-radius-small;
|
||||
|
||||
// Account for some code outputs that place code tags in pre tags
|
||||
code {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
@import "_dialogs";
|
||||
@import "_log";
|
||||
@import "_animation";
|
||||
@import "_timeline";
|
||||
@import "_ribbon";
|
||||
@import "_loading-bar";
|
||||
@import "_youtube";
|
||||
@@ -257,29 +257,35 @@ table{
|
||||
}
|
||||
|
||||
// navbar li ====================================================
|
||||
.navbar-nav li{
|
||||
.navbar-nav {
|
||||
li{
|
||||
&:hover, &.active{
|
||||
|
||||
&:hover, &.active{
|
||||
&:before{
|
||||
top: -4px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&:before{
|
||||
top: -4px;
|
||||
opacity: 1;
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background-color: $green;
|
||||
top: 0;
|
||||
opacity: 0;
|
||||
will-change: opacity, top;
|
||||
@include transition( top 0.15s ease-out, opacity 0.15s ease-out );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
&:before{
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background-color: $green;
|
||||
top: 0;
|
||||
opacity: 0;
|
||||
will-change: opacity, top;
|
||||
@include transition( top 0.15s ease-out, opacity 0.15s ease-out );
|
||||
}
|
||||
|
||||
.pf-navbar-version-info{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
// page menu =====================================================
|
||||
|
||||
169
sass/layout/_timeline.scss
Normal file
169
sass/layout/_timeline.scss
Normal file
@@ -0,0 +1,169 @@
|
||||
.timeline {
|
||||
list-style: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.timeline:before {
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
content: " ";
|
||||
width: 1px;
|
||||
// background-color: $gray-light;
|
||||
left: 50%;
|
||||
margin-top: 20px;
|
||||
|
||||
@include background-image(linear-gradient(to bottom, $green-dark, $gray-light 25%));
|
||||
}
|
||||
|
||||
.timeline > li {
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
|
||||
&.timeline-first{
|
||||
.timeline-title{
|
||||
color: $green-dark;
|
||||
}
|
||||
|
||||
.timeline-badge{
|
||||
background-color: $green-dark;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.timeline > li:before,
|
||||
.timeline > li:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
|
||||
.timeline > li:after {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.timeline > li:before,
|
||||
.timeline > li:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
|
||||
.timeline > li:after {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.timeline > li > .timeline-panel {
|
||||
width: 47%;
|
||||
float: left;
|
||||
border: 1px solid $gray-dark;
|
||||
padding: 8px;
|
||||
position: relative;
|
||||
background-color: $gray-dark;
|
||||
@include box-shadow(0 4px 10px rgba(0,0,0, 0.4));
|
||||
@include border-radius(5px);
|
||||
}
|
||||
|
||||
.timeline > li > .timeline-panel:before {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: -8px;
|
||||
display: inline-block;
|
||||
border-top: 7px solid transparent;
|
||||
border-left: 7px solid $gray-light;
|
||||
border-right: 0 solid $gray-light;
|
||||
border-bottom: 7px solid transparent;
|
||||
}
|
||||
|
||||
.timeline > li > .timeline-panel:after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: -8px;
|
||||
display: inline-block;
|
||||
border-top: 7px solid transparent;
|
||||
border-left: 7px solid $gray-light;
|
||||
border-right: 0 solid $gray-light;
|
||||
border-bottom: 7px solid transparent;
|
||||
}
|
||||
|
||||
.timeline > li > .timeline-badge {
|
||||
color: $gray-darker;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
line-height: 22px;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
left: 50%;
|
||||
margin-left: -11px;
|
||||
background-color: $gray-light;
|
||||
z-index: 100;
|
||||
@include border-radius(50%);
|
||||
}
|
||||
|
||||
.timeline > li.timeline-inverted > .timeline-panel {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.timeline > li.timeline-inverted > .timeline-panel:before {
|
||||
border-left-width: 0;
|
||||
border-right-width: 7px;
|
||||
left: -8px;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
.timeline > li.timeline-inverted > .timeline-panel:after {
|
||||
border-left-width: 0;
|
||||
border-right-width: 8px;
|
||||
left: -9px;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
|
||||
.timeline-title {
|
||||
margin-top: 0;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.timeline-body > p,
|
||||
.timeline-body > ul {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.timeline-body > p + p {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
ul.timeline:before {
|
||||
left: 40px;
|
||||
}
|
||||
|
||||
ul.timeline > li > .timeline-panel {
|
||||
width: calc(100% - 62px);
|
||||
}
|
||||
|
||||
ul.timeline > li > .timeline-badge {
|
||||
left: 29px;
|
||||
margin-left: 0;
|
||||
top: 6px;
|
||||
}
|
||||
|
||||
ul.timeline > li > .timeline-panel {
|
||||
float: right;
|
||||
}
|
||||
|
||||
ul.timeline > li > .timeline-panel:before {
|
||||
border-left-width: 0;
|
||||
border-right-width: 7px;
|
||||
left: -8px;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
ul.timeline > li > .timeline-panel:after {
|
||||
border-left-width: 0;
|
||||
border-right-width: 7px;
|
||||
left: -8px;
|
||||
right: auto;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user