Skip to content

Instantly share code, notes, and snippets.

@toddq
Last active October 21, 2019 22:26
Show Gist options
  • Save toddq/6527361 to your computer and use it in GitHub Desktop.
Save toddq/6527361 to your computer and use it in GitHub Desktop.
Dashing News Ticker Widget

##Description A Dashing widget that overlays a scrolling news ticker on the bottom of your Dashboard.

This widget is a little different, in that it doesn't occupy a typical space in the Dashing grid, but instead overlays the bottom of the dashboard. If it doesn't have any data, it will be hidden. If you send it an array of Strings, it will scroll through them either vertically or horizontally. Our office uses it on our dashboards to occasionally flash important notices or reminders in an eye catching fashion, most of the time it is not displayed.

See http://widget-challenge.herokuapp.com/vertical-ticker and http://widget-challenge.herokuapp.com/horizontal-ticker for a live demo.

##Screenshots

##Dependencies jQuery Transit

Put jquery.transit.js in your assets/javascripts/ directory to use CSS3 animations (the default). Or change @CSS_ANIMATION to false in ticker.coffee and jQuery's animate() will be used instead.

##Usage To install this widget, copy ticker.html, ticker.scss, and ticker.coffee in to the widgets/ticker directory. Optionally, copy the ticker.rb file into your jobs folder for example usage.

To include the widget on a dashboard, add the following snippet to your dashboard layout file BETWEEN the closing ul tag and closing div tag with the class gridster like this:

    </ul>
    <div data-id="ticker" data-view="Ticker" style="display: none;" ></div>
</div>

By default, items will scroll vertically, pausing for 10 seconds on each item. You also have the option of a continuous horizontal scrolling animation if you specify:

<div data-id="ticker" data-view="Ticker" data-scroll_orientation="horizontal" style="display: none;" ></div>

The widget is not displayed by default, and will only display if it has content. You send it an array of any number of Strings, and it will rotate through them. If you send it an empty array, it will hide itself again.

curl -d '{ "auth_token": "YOUR_AUTH_TOKEN", "items": ["Hello World!", "Well Hello Yourself!"] }' http://localhost:3030/widgets/ticker

curl -d '{ "auth_token": "YOUR_AUTH_TOKEN", "items": [] }' http://localhost:3030/widgets/ticker
class Dashing.Ticker extends Dashing.Widget
# pixels per second
@SPEED: 70
# easing function to use for the animation
@EASING: "linear"
# Whether to use CSS3 animations. If false, jQuery animate() will be used.
@CSS_ANIMATION: true
# Number of milliseconds between jQuery animation frames. The
# default is 13, which causes this ticker to have pretty high cpu usage.
# 40 is about the highest I've gone before animations start to look bad.
# Lower numbers will cause smoother animations.
# NOTE: this will cause a change to GLOBAL jQuery animations on the page.
# Only used if CSS_ANIMATION is false.
@FX_INTERVAL: 30
# Used for vertical scrolling, the number of seconds to show an alert before
# scrolling to the next one.
@DELAY = 10
getTicker: ->
$("#news_ticker")
# should be either "vertical" or "horizontal"
getScrollOrientation: ->
if $(@node).data('scroll_orientation')
$(@node).data('scroll_orientation')
else
"vertical"
initialize: ->
return true if @initialized
if not $(".gridster.ready").length or not @getTicker().length
@log( "not ready yet..." )
return false
@log( "initializing..." )
if not Ticker.CSS_ANIMATION and @getScrollOrientation() == 'horizontal'
@log( "setting jQuery fx interval to #{Ticker.FX_INTERVAL}" )
jQuery.fx.interval = Ticker.FX_INTERVAL
$.fn.transition = $.fn.animate
@ticker = @getTicker()
# Set a width first based on the board size
@log( "setting node width to " + $(".gridster.ready").width() )
$(@node).width( $(".gridster.ready").width() )
# Wrap the ticker in some extra divs
@log( "adding wrapper divs" )
@ticker.addClass('newsticker')
@ticker.wrap('<div class="mask" />')
@ticker.parent().wrap('<div class="tickercontainer" />')
@initialized = true
ready: ->
# This is fired when the widget is done being rendered
@log("ready")
@doTicker()
onData: (data) ->
@log( "onData" )
@doTicker()
doTicker: ->
@initialize() if not @initialized
if @initialized
if @hasChanged() and @hasItems()
@resetTicker()
else if not @hasItems()
@stopTicker()
startTicker: ->
@log( "" )
@log("starting ticker")
@should_stop = false
$(@node).show()
if @getScrollOrientation() == "horizontal"
@doHorizontalScroll()
else
@doVerticalScroll()
doHorizontalScroll: ->
@log( "doing horizontal scroll" )
viewWidth = @ticker.parent().width()
@log( "viewWidth: #{viewWidth}" )
# Add a spacer between ticker elements so that only one of them is showing at once
@insertSpacers( viewWidth )
# calculate content width
@log( "calculating content width..." )
contentWidth = @contentWidth()
# set ticker width to the sum of the viewport and the content
tickerWidth = viewWidth + contentWidth
@log( "setting ticker width to #{tickerWidth}" )
@ticker.css('width', tickerWidth)
# set ticker left value to viewWidth
@ticker.css('left', viewWidth)
# then animate the left value to -contentWidth
animationSettings = { 'left': -contentWidth }
@log( "animating 'left' value from #{viewWidth} to #{-contentWidth}" )
duration = tickerWidth * 1000 / Ticker.SPEED
console.log( "duration: #{duration} ms" )
@ticker.transition animationSettings, duration, Ticker.EASING, =>
if not @should_stop
@log( "loop complete, do it again!" )
@startTicker()
doVerticalScroll: ->
@log( "doing vertical scroll" )
@itemHeight = @ticker.children('li').first().outerHeight()
contentHeight = @itemHeight * @ticker.children('li').size()
# set ticker height to the content height
@ticker.css('height', contentHeight)
# set ticker top value to @itemHeight
@ticker.css('top', @itemHeight)
@ticker.children('li').width('100%')
@nextItem()
nextItem: =>
@log( "scrolling to next item" )
animationSettings = { 'top': @ticker.position().top - @itemHeight }
duration = 1000
@ticker.transition animationSettings, duration, "linear", =>
if not @should_stop
@log( "comparing #{@ticker.position().top - 2} to #{[email protected]()}" )
if @ticker.position().top - 2 > [email protected]()
@log( "scheduling the next item" )
@scrollNext = setTimeout( @nextItem, Ticker.DELAY * 1000 )
else
@log( "loop complete, do it again!" )
@startTicker()
stopTicker: ->
@log("stopping ticker")
@should_stop = true
if @ticker
@ticker.stop( true )
clearTimeout( @scrollNext )
@ticker.removeAttr('style')
$(@node).hide()
resetTicker: ->
@stopTicker()
@startTicker()
contentWidth: ->
contentWidth = 0
for child in @ticker.children()
contentWidth += $(child).outerWidth( true )
return contentWidth
hasItems: ->
return @get('items' ) and @get('items').length > 0
hasChanged: ->
new_items = @get('items')
changed = false
if @old_items == new_items
# short circuit, they're probably both null
return false
if @old_items and not new_items
changed = true
else if new_items and not @old_items
changed = true
else if @old_items.toString() != new_items.toString()
changed = true
if changed
@log( "items changed" )
else
@log( "items the same" )
@old_items = new_items
return changed
insertSpacers: (width) ->
@log( "inserting spacers" )
@ticker.children().not(':last-child').css('margin-right', width)
log: (msg) ->
# console.log( "[ticker] #{msg}" )
return
<ul id="news_ticker">
<li data-foreach-item="items" data-bind="item | raw"></li>
</ul>
ticker_items = [
"Staff meeting today in the cafeteria today at 10am. &nbsp;&nbsp; Don't forget to give yourself 20 minutes to walk over there.",
"The Dashing Widget Challenge deadline has been moved to September 26th. &nbsp;&nbsp; Don't delay in getting your awesome widget submitted!"
]
SCHEDULER.every '10m', :first_in => 0 do
send_event( 'ticker', { :items => ticker_items } )
end
// ----------------------------------------------------------------------------
// Sass declarations
// ----------------------------------------------------------------------------
$background-color: rgba(0, 0, 0, .6);
$text-color: #ffff00;
$border-color: #ffffff;
$border: 1px solid $border-color;
$height: 60px;
$width: 100%;
// ----------------------------------------------------------------------------
// Widget-ticker styles
// ----------------------------------------------------------------------------
.widget-ticker {
position: fixed;
bottom: 10px;
z-index: 1000;
padding: 0px !important;
width: $width;
height: $height;
color: $text-color;
background: $background-color;
/* the outer div with the border */
.tickercontainer {
border: $border;
background: $background-color;
height: $height;
margin: 0;
padding: 0;
overflow: hidden;
li {
float: left;
}
.mask {
position: relative;
left: 0px;
top: 0px;
overflow: hidden;
height: $height;
}
}
ul.newsticker {
position: relative;
list-style-type: none;
-webkit-transform: translate3d(0, 0, 0);
-webkit-backface-visibility: hidden;
-webkit-perspective: 1000;
}
ul.newsticker li {
float: left;
font-size: 20px;
font-weight: bold;
font-style: italic;
line-height: $height;
height: $height;
white-space: nowrap;
text-align: center;
}
}
@kylezero
Copy link

Is there any way to change the text color to anything besides yellow? Editing the scss doesn't seem to do anything.

@cadm-frank
Copy link

Is there a way to have two tickers on the screen?
I changed the copied widget so that one displays on the top, the other on the bottom, but no matter what, only the first one defined in my dashboard will show anything.

@itsonlym3
Copy link

itsonlym3 commented May 15, 2018

i swear i had this working yesterday, and i can't for the life of me get ANYTHING to scroll or be displayed. :-/

RESOLVED: looks like i was having jQuery issues. awesome widget, thank you!

@dzpchr762
Copy link

Having trouble getting this working. It's throwing one error on my localhost and another on Heroku.
localhost sampletv
dash sampletv

@dzpchr762
Copy link

Came back this morning and both the localhost and Heroku deployed site are throwing the same "Uncaught Error: Syntax Error, unrecognized expression: [data-row^=]. I believe this to be a parsing error. Any comments?

@dzpchr762
Copy link

dzpchr762 commented Jul 24, 2019

Found the problem! There was a script at the beginning of the .erb file that overwrote the positioning of the widgets "Dashing.gristerLayout('[{"col":#, "row":#},...]')". It did not like this, commented out, began working correctly. Now to fix the scheduler.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment