How to automatically place "read more" when characters hit X amount - javascript

I'm currently using a nice bit of open source code which pulls Google reviews.
Issue is, if a review is very long... it makes my site look weird. I need to set a max amount of characters, but give the user the option to read the full review. I'm not sure if this should be done through jQuery, CSS or what... Looking for some guidance.
See my issue here:
My code is here:
jQuery(document).ready(function( $ ) {
$("#google-reviews").googlePlaces({
placeId: 'place ID' //Find placeID #: https://developers.google.com/places/place-id
, render: ['reviews']
, min_rating: 5
, max_rows:3
});
});
/* https://github.com/peledies/google-places */
(function($) {
$.googlePlaces = function(element, options) {
var defaults = {
placeId: 'place ID' // placeId provided by google api documentation
, render: ['reviews']
, min_rating: 0
, max_rows: 0
, rotateTime: false
};
var plugin = this;
plugin.settings = {}
var $element = $(element),
element = element;
plugin.init = function() {
plugin.settings = $.extend({}, defaults, options);
$element.html("<div id='map-plug'></div>"); // create a plug for google to load data into
initialize_place(function(place){
plugin.place_data = place;
// render specified sections
if(plugin.settings.render.indexOf('reviews') > -1){
renderReviews(plugin.place_data.reviews);
if(!!plugin.settings.rotateTime) {
initRotation();
}
}
});
}
var initialize_place = function(c){
var map = new google.maps.Map(document.getElementById('map-plug'));
var request = {
placeId: plugin.settings.placeId
};
var service = new google.maps.places.PlacesService(map);
service.getDetails(request, function(place, status) {
if (status == google.maps.places.PlacesServiceStatus.OK) {
c(place);
}
});
}
var sort_by_date = function(ray) {
ray.sort(function(a, b){
var keyA = new Date(a.time),
keyB = new Date(b.time);
// Compare the 2 dates
if(keyA < keyB) return -1;
if(keyA > keyB) return 1;
return 0;
});
return ray;
}
var filter_minimum_rating = function(reviews){
for (var i = reviews.length -1; i >= 0; i--) {
if(reviews[i].rating < plugin.settings.min_rating){
reviews.splice(i,1);
}
}
return reviews;
}
var renderReviews = function(reviews){
reviews = sort_by_date(reviews);
reviews = filter_minimum_rating(reviews);
var html = "";
var row_count = (plugin.settings.max_rows > 0)? plugin.settings.max_rows - 1 : reviews.length - 1;
// make sure the row_count is not greater than available records
row_count = (row_count > reviews.length-1)? reviews.length -1 : row_count;
for (var i = row_count; i >= 0; i--) {
var stars = renderStars(reviews[i].rating);
var date = convertTime(reviews[i].time);
html = html+"<div class='review-item'><div class='review-meta'><span class='review-author'>"+reviews[i].author_name+"</span><span class='review-sep'>, </span><span class='review-date'>"+date+"</span></div>"+stars+"<p class='review-text'>"+reviews[i].text+"</p></div>"
};
$element.append(html);
}
var initRotation = function() {
var $reviewEls = $element.children('.review-item');
var currentIdx = $reviewEls.length > 0 ? 0 : false;
$reviewEls.hide();
if(currentIdx !== false) {
$($reviewEls[currentIdx]).show();
setInterval(function(){
if(++currentIdx >= $reviewEls.length) {
currentIdx = 0;
}
$reviewEls.hide();
$($reviewEls[currentIdx]).fadeIn('slow');
}, plugin.settings.rotateTime);
}
}
var renderStars = function(rating){
var stars = "<div class='review-stars'><ul>";
// fill in gold stars
for (var i = 0; i < rating; i++) {
stars = stars+"<li><i class='star'></i></li>";
};
// fill in empty stars
if(rating < 5){
for (var i = 0; i < (5 - rating); i++) {
stars = stars+"<li><i class='star inactive'></i></li>";
};
}
stars = stars+"</ul></div>";
return stars;
}
var convertTime = function(UNIX_timestamp){
var a = new Date(UNIX_timestamp * 1000);
var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
var time = months[a.getMonth()] + ' ' + a.getDate() + ', ' + a.getFullYear();
return time;
}
plugin.init();
}
$.fn.googlePlaces = function(options) {
return this.each(function() {
if (undefined == $(this).data('googlePlaces')) {
var plugin = new $.googlePlaces(this, options);
$(this).data('googlePlaces', plugin);
}
});
}
})(jQuery);
#map-plug {display:none;}
#google-reviews {
display:flex;
flex-wrap:wrap;
//display: grid;
//grid-template-columns: repeat( auto-fit, minmax(320px, 1fr));
}
.review-item {
margin:0 auto;
padding:1em;
flex: 1 1 20%;
}
#media ( max-width:1200px) {
.review-item { flex: 1 1 40%; }
}
#media ( max-width:450px) {
.review-item { flex: 1 1 90%; }
}
.review-meta, .review-stars {text-align:center; font-size:115%;}
.review-author { text-transform: capitalize; font-weight:bold; }
.review-date {opacity:.6; display:block;}
.review-text { line-height:1.55; text-align:left; max-width:32em; margin:auto;}
.review-stars ul {
display: inline-block;
list-style: none;
margin:0; padding:0;
}
.review-stars ul li {
float: left;
margin-right: 1px;
line-height:1;
}
.review-stars ul li i {
color: #E4B248;
font-size: 1.4em;
font-style:normal;
}
.review-stars ul li i.inactive { color: #c6c6c6;}
.star:after { content: "\2605"; }
<div id="google-reviews"></div>

The substr method will return the part of a string between the start index and a number of characters after it and will truncate your text.
Ex:
reviews[i].text.substr(0, 100 /* amount of characters */) + " read more..."

Related

How to save time value in dynamically created li to input box with javascript

How to save a time value in dynamically created li to input box with javascript
I have a simple timer, that starts, stops, pauses, takes a time snap and resets the time snap.
The timesnap in generated and displayed in the webpage inside a li. It all works fine what I am struggling with is trying to click on a displayed time snap and have the value placed in an input box so I can later save a selected value to a database.
This is the script I am using to place the clicked on li item into the input box
var items = document.querySelectorAll("#list li");
for (var i = 0; i < items.length; i++) {
items[i].onclick = function () {
document.getElementById("inptSnap").value = this.innerHTML;
};
}
This is the html
<div class="container">
<!-- Different App -->
<div class="timeDisplay">00:00:00</div>
<button id="begin">Start</button>
<button id="hold">Pause</button>
<button id="end">Stop</button>
<button id="timeSnap">Time Snap</button>
<button id="resetSnap">Reset Time Snap</button>
<ul id="list" class="laps"></ul>
<div>
<input type="text" id="inptSnap" />
</div>
</div>
This is the full timer script with the attempted select value onclick
var begin = document.getElementById("begin");
begin.addEventListener("click", start);
var end = document.getElementById("end");
end.addEventListener("click", stop);
var hold = document.getElementById("hold");
hold.addEventListener("click", pause);
var timeSnap = document.getElementById("timeSnap");
timeSnap.addEventListener("click", snap);
var timeSnap = document.getElementById("timeSnap");
timeSnap.addEventListener("click", pause);
var resetSnap = document.getElementById("resetSnap");
resetSnap.addEventListener("click", resetSnaps);
var ms = 0,
s = 0,
m = 0;
var timeCounter;
var displayEl = document.querySelector(".timeDisplay");
var lapsContainer = document.querySelector(".laps");
function start() {
if (!timeCounter) {
timeCounter = setInterval(run, 10);
}
}
function run() {
displayEl.textContent = displayTimeCount();
ms++;
if (ms == 100) {
ms = 0;
s++;
}
if (s == 60) {
s = 0;
m++;
}
}
function stop() {
stopTimer();
ms = 0;
s = 0;
m = 0;
displayEl.textContent = displayTimeCount();
}
function stopTimer() {
clearInterval(timeCounter);
timeCounter = false;
}
function pause() {
stopTimer();
}
function displayTimeCount() {
return (
(m < 10 ? "0" + m : m) +
":" +
(s < 10 ? "0" + s : s) +
":" +
(ms < 10 ? "0" + ms : ms)
);
}
function snap() {
if (timeCounter) {
var li = document.createElement("li");
li.innerText = displayTimeCount();
lapsContainer.appendChild(li);
}
}
function resetSnaps() {
lapsContainer.innerHTML = "";
}
// Script to put lap into input box
var items = document.querySelectorAll("#list li");
for (var i = 0; i < items.length; i++) {
items[i].onclick = function () {
document.getElementById("inptSnap").value = this.innerHTML;
};
}
This is the CodePen Link
I would be very grateful for any pointers and advice, thanks
You can do somthing like that...
PS: I think that the ergonomics of your button is to be reviewed, I did a kind of revision.
const
btStartPause = document.querySelector('#container button:nth-of-type(1)')
, btStopClear = document.querySelector('#container button:nth-of-type(2)')
, btSnap = document.querySelector('#container button:nth-of-type(3)')
, snapList = document.querySelector('ol')
, inptSnap = document.querySelector('input#inptSnap')
, chrono = ((dZTime='#container time') =>
{
const
displZone = document.querySelector(dZTime)
, chronoZero = '00:00:00.000'
, one_Sec = 1000
//, one_Min = one_Sec * 60
//, one_Hrs = one_Min * 60
, n_Dgts = (n,t) => `${t}`.padStart(n,'0')
;
let startTime = null
, timeElapsed = 0
, pausedTime = 0
, reqRef = null
, reqPause = false
, stoped = false
;
displZone.textContent = chronoZero
function reqLoop(timeStamp) // timeStamp is float
{
startTime ??= timeStamp // Logical nullish assignment (??=)
if (stoped)
{
cancelAnimationFrame(reqRef)
return
}
if (reqPause)
{
pausedTime = (timeStamp - startTime) - timeElapsed;
}
else
{
timeElapsed = ((timeStamp - startTime) - pausedTime) | 0 // get integer part of float
let
Tms = timeElapsed % one_Sec
, tim = (timeElapsed - Tms) / one_Sec
, T_s = tim % 60
, T_m = 0
, T_h = 0
;
tim = (tim - T_s) / 60
T_m = tim % 60
T_h = (tim - T_m) / 60
displZone.textContent = `${n_Dgts(2,T_h)}:${n_Dgts(2,T_m)}:${n_Dgts(2,T_s)}.${n_Dgts(3,Tms)}`
}
requestAnimationFrame( reqLoop )
}
const jso =
{ dispSz: chronoZero.length
, getVal: ()=> displZone.textContent
, start() { reqRef = requestAnimationFrame(reqLoop) }
, pause(OnOff) { reqPause = OnOff }
, stop() { stoped = true }
, RaZ()
{
startTime = null
timeElapsed = 0
pausedTime = 0
reqRef = null
reqPause = false
stoped = false
displZone.textContent = chronoZero
}
}
Object.freeze(jso)
return jso
})()
;
btStartPause.onclick =_=>
{
if (btStartPause.classList.toggle('pause') )
{
btStopClear.disabled = false
if ( btStartPause.dataset.lib !== 'continue' )
{
btStartPause.dataset.lib = 'continue'
chrono.start()
}
else
chrono.pause(false)
}
else
{
btStopClear.disabled = true
btStopClear.classList.remove('clear')
chrono.pause(true)
}
}
btStopClear.onclick =_=>
{
if (btStopClear.classList.toggle('clear') )
{
btStartPause.disabled = true
btStartPause.dataset.lib = 'start'
btStartPause.classList.remove('pause')
chrono.stop()
}
else
{
btStartPause.disabled = false
btStopClear .disabled = true
chrono.RaZ()
}
}
btSnap.onclick =_=>
{
snapList
.appendChild( document.createElement('li'))
.innerHTML = chrono.getVal()
+ '<span title="delete"> ✖ </span>'
+ '<span title="copy"> &#x2398 </span>'
}
snapList.onclick =({target}) =>
{
if (!target.matches('li > span'))
{
inptSnap.value = target.closest('li').textContent.substring(0, chrono.dispSz)
inptSnap.focus()
return
}
if (target.matches('span[title=delete]'))
{
target.closest('li').remove()
}
if (target.matches('span[title=copy]'))
{
let origin = target.closest('li')
copySomething ( origin.textContent.substring(0, chrono.dispSz), origin )
}
}
async function copySomething(toCopy, el )
{
try
{
await navigator.clipboard.writeText(toCopy);
el.classList.add('copyOK')
setTimeout(() => { el.classList.remove('copyOK')}, 1200);
}
catch (err)
{
el.classList.add('copyBad')
setTimeout(() => { el.classList.remove('copyBad')}, 1200);
console.error('Failed to copy :/', err);
}
}
body {
font-family : Arial, Helvetica, sans-serif;
font-size : 16px;
}
time {
display : block;
font-size : 1.4rem;
margin : .6rem 1rem;
letter-spacing : .2rem;
}
ol {
list-style : none;
font-size : 1.6rem;
padding : 0;
margin : 1.5rem;
width : 15rem;
list-style-type: decimal;
}
li {
border : 1px solid lightblue;
padding : .3rem .6rem;
position : relative;
cursor : pointer;
}
li::marker {
font-size : .9rem;
color : darkslategray;
}
li span {
float : right;
margin : 0 0 0 .3em;
font-size : 1.2rem;
color : darkslategray;
}
li span[title=delete]:hover {
color : crimson;
}
li span[title=copy]:hover {
background : white;
color : darkblue;
}
#container button {
min-width : 4.4rem;
text-transform : capitalize;
}
#container button:before {
content : attr(data-lib)
}
#container button.pause:before {
content : 'pause'
}
#container button.clear:before {
content : 'clear'
}
li:hover {
background : lightblue;
}
li.copyOK::after,
li.copyBad::after {
position : absolute;
display : block;
font-size : .8rem;
top : 1.2rem;
left : 11rem;
padding : .1rem .2rem;
}
li.copyOK::after {
content : 'copied';
background : lightgreen;
}
li.copyBad::after {
left : 1rem;
content : 'Failed to copy :/';
background : lightcoral;
}
<input type="text" id="inptSnap" >
<hr>
<div id="container">
<time datetime="00:00:00.000">00:00:00.000</time>
<button data-lib="start"><!-- start / continue / pause --></button>
<button data-lib="stop" disabled><!-- stop / clear --></button>
<button>snap</button>
</div>
<ol></ol>
for info :
Fastest way to cast a float to an int in javascript?
So I understand that you need a place value kind of thing.
var begin = document.getElementById("begin");
begin.addEventListener("click", start);
var end = document.getElementById("end");
end.addEventListener("click", stop);
var hold = document.getElementById("hold");
hold.addEventListener("click", pause);
var timeSnap = document.getElementById("timeSnap");
timeSnap.addEventListener("click", snap);
var timeSnap = document.getElementById("timeSnap");
timeSnap.addEventListener("click", pause);
var resetSnap = document.getElementById("resetSnap");
resetSnap.addEventListener("click", resetSnaps);
var ms = 0,
s = 0,
m = 0;
var timeCounter;
var displayEl = document.querySelector(".timeDisplay");
var lapsContainer = document.querySelector(".laps");
function start() {
if (!timeCounter) {
timeCounter = setInterval(run, 10);
}
}
function run() {
displayEl.textContent = displayTimeCount();
ms++;
if (ms == 100) {
ms = 0;
s++;
}
if (s == 60) {
s = 0;
m++;
}
}
function stop() {
stopTimer();
ms = 0;
s = 0;
m = 0;
displayEl.textContent = displayTimeCount();
}
function stopTimer() {
clearInterval(timeCounter);
timeCounter = false;
}
function pause() {
stopTimer();
}
function displayTimeCount() {
return (
(m < 10 ? "0" + m : m) +
":" +
(s < 10 ? "0" + s : s) +
":" +
(ms < 10 ? "0" + ms : ms)
);
}
function snap() {
if (timeCounter) {
var input = document.createElement("input");
input.value = displayTimeCount();
lapsContainer.appendChild(input);
}
}
function resetSnaps() {
lapsContainer.innerHTML = "";
}
// Script to put lap into input box
var items = document.querySelectorAll("#list li");
for (var i = 0; i < items.length; i++) {
items[i].onclick = function () {
document.getElementById("inptSnap").value = this.innerHTML;
};
}
.timeDisplay {
font-size: 32px;
}
ul li {
list-style: none;
font-size: 32px;
}
.container {
width: 400px;
margin: auto;
}
<div class="container">
<!-- Different App -->
<div class="timeDisplay">00:00:00</div>
<button id="begin">Start</button>
<button id="hold">Pause</button>
<button id="end">Stop</button>
<button id="timeSnap">Time Snap</button>
<button id="resetSnap">Reset Time Snap</button>
<ul id="list" class="laps">
</ul>
<div>
<input type="text" id="inptSnap" />
</div>
</div>

PUZZLE TROUBLE - trying to save initial board [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I wrote this Javascript application for a 15-puzzle. The entire application is contained in the file below. Whenever I render a new board, I'm trying to store the initial configuration in the initialBoard variable so I can replay the same game later. However, initialBoard variable always seems to equal the currentBoard variable. I'm new to Javascript and any help will be greatly appreciated.
<html>
<head>
<title>15 Puzzle</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<style type="text/css">
#puzzle-board {
border: 5px solid;
}
.puzzle-tile {
background:#fff;
background: -moz-linear-gradient(top, #fff, #eee);
background: -webkit-gradient(linear,0 0, 0 100%, from(#fff), to(#eee));
box-shadow: inset 0 0 0 1px #fff;
-moz-box-shadow: inset 0 0 0 1px #fff;
-webkit-box-shadow: inset 0 0 0 1px #fff;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size:60px;
height: 100px;
text-align: center;
text-decoration: none;
text-shadow:0 1px #fff;
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
vertical-align:middle;
width: 100px;
}
#start-stop {
float: left;
}
#timer {
float: left;
margin-left: 10px;
}
#counter {
float: left;
margin-left: 10px;
}
</style>
</head>
<body>
<div class="container-fluid">
<div class="row">
<br />
</div>
<div class="row">
<div class="col-md-5 col-md-offset-1">
<table id="puzzle"></table>
</div>
<div class="col-md-6">
<div class="row">
<br />
<button type="button" class="btn btn-lg btn-success" id="start-stop">START</button>
<button type="button" class="btn btn-lg btn-default" id="timer"></button>
<button type="button" class="btn btn-lg btn-default" id="counter"></button>
</div>
</div>
</div>
</div>
<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="http://code.jquery.com/ui/1.11.1/jquery-ui.min.js"></script>
<!--<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>-->
<script>
/**
* Puzzle Object
*/
puzzle = function(targetId) {
/************************************************************
* Private members
************************************************************/
var
currentBoard,
initialBoard,
orderedBoard = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,'']];
function canSwapTiles(source, target) {
var sourceTileRow = source.attr("data-row");
var sourceTileCol = source.attr("data-col");
var sourceTileValue = source.text();
var targetTileRow = target.attr("data-row");
var targetTileCol = target.attr("data-col");
var targetTileValue = target.text();
if (sourceTileValue != '' && targetTileValue != '') {
return false;
} else if (Math.abs(targetTileRow - sourceTileRow) > 1) {
return false;
} else if (Math.abs(targetTileCol - sourceTileCol) > 1) {
return false;
} else {
return true;
}
}
function swapTiles(source, target) {
var sourceTileRow = source.attr("data-row");
var sourceTileCol = source.attr("data-col");
var sourceTileValue = source.text();
var targetTileRow = target.attr("data-row");
var targetTileCol = target.attr("data-col");
var targetTileValue = target.text();
source.text(targetTileValue);
currentBoard[sourceTileRow][sourceTileCol] = parseInt(targetTileValue);
target.text(sourceTileValue);
currentBoard[targetTileRow][targetTileCol] = parseInt(sourceTileValue);
$(targetId).trigger('moved');
console.log("swapped tiles");
console.log(initialBoard);
if (isSolved())
{
console.log('solved puzzle');
console.log(initialBoard);
$(targetId).trigger('solved', {
board: initialBoard
});
}
}
function renderBoard(board) {
$("#puzzle-board").empty();
currentBoard = board;
//initialBoard = board;
console.log('rendering board');
console.log(initialBoard);
for (i = 0; i < 4; i++) {
$("#puzzle-board").append('<tr class="puzzle-row" id="puzzle-row-' + i + '"></tr><br />');
for (j = 0; j < 4; j++) {
var tile = '<td class="puzzle-tile" data-row="' + i + '" data-col="' + j + '">' +
board[i][j] +
'</td>';
$("#puzzle-row-" + i).append(tile);
}
}
$(".puzzle-tile").draggable(
{
revert: true,
snap: true,
snapMode: "inner",
zIndex: 100
}
).droppable(
{
drop: function (event, ui) {
var sourceTile = ui.draggable;
var targetTile = $(this);
if (canSwapTiles(sourceTile, targetTile)) {
swapTiles(sourceTile, targetTile);
}
}
}
);
}
function randomBoard() {
var tileValues = [];
for (i = 0; i < 15; i++) {
tileValues[i] = i + 1;
}
var randomlyOrderedTileValues = [''];
do {
randomlyOrderedTileValues[(16 - tileValues.length)] = tileValues.splice(Math.floor(Math.random() * tileValues.length), 1).pop();
} while (tileValues.length > 0);
var board = [];
for (i = 0; i < 4; i++) {
board[i] = [];
for (j = 0; j < 4; j++) {
board[i][j] = randomlyOrderedTileValues.pop();
}
}
return board;
}
function isSolved() {
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
if (isNaN(currentBoard[i][j]))
{
continue;
}
if (parseInt(currentBoard[i][j]) != parseInt(orderedBoard[i][j]))
{
return false;
}
}
}
return true;
}
/************************************************************
* Constructor
************************************************************/
/*
* Initialize board
*/
$(targetId).append('<tbody id="puzzle-board"></tbody>');
renderBoard(orderedBoard);
/************************************************************
* Public data and methods
************************************************************/
return {
reset: function() {
renderBoard(orderedBoard);
},
shuffle: function() {
//initialBoard = randomBoard();
initialBoard = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,'',15]];
renderBoard(initialBoard);
}
}
};
/**
* Timer Object
*/
timer = function(targetId) {
/************************************************************
* Private members
************************************************************/
var
intervalId,
totalSeconds = 0;
function pad(val) {
var valString = val + "";
if (valString.length < 2) {
return "0" + valString;
} else {
return valString;
}
}
function setTime()
{
++totalSeconds;
$("#seconds").html(pad(totalSeconds % 60));
$("#minutes").html(pad(parseInt(totalSeconds / 60)));
}
/************************************************************
* Constructor
************************************************************/
/*
* Initialize timer
*/
$(targetId).append('<i>Time: </i><i id="minutes">00</i>:<i id="seconds">00</i>');
/************************************************************
* Public data and methods
************************************************************/
return {
reset: function() {
window.clearInterval(intervalId);
totalSeconds = 0;
$("#minutes").text('00');
$("#seconds").text('00');
},
start: function () {
intervalId = window.setInterval(setTime, 1000);
},
getTime: function () {
return pad(parseInt(totalSeconds / 60)) + ':' + pad(totalSeconds % 60);
}
}
};
/**
* Counter Object
*/
counter = function(targetId) {
/************************************************************
* Private members
************************************************************/
var
steps = 0;
/************************************************************
* Constructor
************************************************************/
/*
* Initialize timer
*/
$(targetId).append('<i id="steps-title">Steps: </i><i id="steps-count">0</i>');
/************************************************************
* Public data and methods
************************************************************/
return {
reset: function() {
steps = 0;
$("#steps-count").text(steps);
},
incr: function () {
steps++;
$("#steps-count").text(steps);
},
getSteps: function () {
return steps;
}
}
};
$(document).ready(function() {
var Puzzle = puzzle("#puzzle");
var Timer = timer("#timer");
var Counter = counter("#counter");
localStorage["games"] = '[]';
$("#start-stop").click(function() {
switch ($(this).text()) {
case 'START':
$(this).removeClass("btn-success").addClass("btn-danger").text("STOP");
Puzzle.shuffle();
Timer.start();
Counter.reset();
break;
case 'STOP':
$(this).removeClass("btn-danger").addClass("btn-success").text("START");
Puzzle.reset();
Timer.reset();
Counter.reset();
break;
}
});
$("#puzzle").bind('moved',
function(e, data) {
Counter.incr();
}
).bind('solved',
function(e, data) {
console.log(data);
$("#start-stop").removeClass("btn-danger").addClass("btn-success").text("START");
Puzzle.reset();
Timer.reset();
Counter.reset();
}
);
});
</script>
</body>
When you invoke the Puzzle.shuffle() here:
$("#start-stop").click(function() {
switch ($(this).text()) {
case 'START':
$(this).removeClass("btn-success").addClass("btn-danger").text("STOP");
Puzzle.shuffle();
Timer.start();
Counter.reset();
break;
case 'STOP':
$(this).removeClass("btn-danger").addClass("btn-success").text("START");
Puzzle.reset();
Timer.reset();
Counter.reset();
break;
}
});
It initializes the board and passes it to renderBoard here
shuffle: function() {
//initialBoard = randomBoard();
initialBoard = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,'',15]];
renderBoard(initialBoard);
}
Then renderBoard does this:
currentBoard = board;
Which causes both variables point to the same object. If you want them to be separated, then in renderBoard you should clone the object, instead of assigning it. Something along the lines of this if you use jQuery:
currentBoard = [];
$.extend(currentBoard, board);

javascript game ( 3 in line ) line check logic

i've been in a battle to sort this problem since yesterday and i fear that i've gotten tunnel vision.
The game:
first player to make a line of 3 of a kind (xxx or 000) wins.
http://jsfiddle.net/brunobliss/YANAW/
The catch:
Only the first horizontal line is working!!! I can make it all work using a lot of IFS but repeating the same code over and over again is often a good indicator that i'm doing somethin wrong
The problem:
bruno.checkWin(); will check if there's a line or not, the guy who presented me this game chalenge told me that it is possible to check the lines with a for loop and that i should use it instead of IFS. I can't solve this without IFS unfortunately...
<!doctype html>
<html>
<head>
<meta charset="iso-8859-1">
<title> </title>
<style>
#jogo {
border: #000 1px solid;
width: 150px;
position: absolute;
left: 50%;
top: 50%;
margin-left: -75px;
margin-top: -75px;
}
#jogo div {
display: inline-block;
vertical-align: top;
width: 28px;
height: 28px;
padding: 10px;
font-size: 20px;
border: #000 1px solid;
border-collapse: collapse;
text-align: center;
}
#reset {
font-family: Verdana;
width: 153px;
height: 30px;
background-color: black;
color: white;
text-align: center;
cursor: pointer;
left: 50%;
top: 50%;
position: absolute;
margin-left: -76px;
margin-top: 100px;
}
</style>
<script> </script>
</head>
<body>
<div id="jogo"> </div>
<div id="reset"> RESET </div>
<script>
var ultimo = "0";
var reset = document.getElementById('reset');
var jogo = document.getElementById('jogo');
var cell = jogo.getElementsByTagName('div');
var bruno = {
init: function () {
var jogo = document.getElementById('jogo');
for ( i = 0 ; i < 9 ; i++ ) {
var cell = document.createElement('div');
cell.onclick = function () {
// variavel publica dentro do obj?
ultimo = (ultimo == "x") ? 0 : "x";
this.innerHTML = ultimo;
bruno.checkWin();
};
jogo.appendChild(cell);
}
},
checkWin: function () {
var jogo = document.getElementById('jogo');
var cell = jogo.getElementsByTagName('div');
// as diagonais nao verificar por loop
for ( i = 0 ; i < cell.length ; i=i+4 ) {
switch(i) {
case 0:
if (cell[0].innerHTML != '') {
bruno.checkFirst();
}
case 4:
if (cell[4].innerHTML != '') {
bruno.checkFirst();
}
case 8:
if (cell[8].innerHTML != '') {
bruno.checkFirst();
}
}
/*
} else
if (i == 4 && cell[4].innerHTML != '') {
bruno.checkCenter();
} else
if (i == 8 && cell[8].innerHTML != '') {
bruno.checkLast();
}*/
}
},
reset: function () {
var jogo = document.getElementById('jogo');
var cell = jogo.getElementsByTagName('div');
for ( j = 0 ; j < cell.length ; j++ ) {
cell[j].innerHTML = "";
}
},
checkFirst: function () {
if (cell[0].innerHTML == cell[1].innerHTML && cell[1].innerHTML == cell[2].innerHTML) {
alert("linha horizontal");
return false;
} else
if (cell[0].innerHTML == cell[3].innerHTML && cell[3].innerHTML == cell[6].innerHTML) {
alert("linha vertical");
return false;
}
},
checkMiddle: function () {
// check vertical and horizontal lines from the center
},
checkLast: function () {
// check last horizontal and right edge vertical
}
};
window.onload = function () {
bruno.init();
};
reset.onclick = function () {
bruno.reset();
};
</script>
</body>
</html>
I came up with a more 'compact' version of your code. No switch statements. Have a look:
http://jsfiddle.net/YANAW/1/
Here's the code, for those who prefer to read it here. Important/updated functions are checkWin() and checkCells().
var bruno = {
init: function () {
var jogo = document.getElementById('jogo');
for ( i = 0 ; i < 9 ; i++ ) {
var cell = document.createElement('div');
cell.onclick = function () {
// variavel publica dentro do obj?
ultimo = (ultimo == "x") ? 0 : "x";
this.innerHTML = ultimo;
bruno.checkWin();
};
jogo.appendChild(cell);
}
},
checkWin: function () {
var jogo = document.getElementById('jogo');
var cells = jogo.getElementsByTagName('div');
// Scan through every cell
var numRows = 3;
var numColumns = 3;
for (var i = 0; i < cells.length; i++)
{
// Determine cell's position
var isHorizontalFirstCell = ((i % numColumns) === 0);
var isVerticalFirstCell = (i < numColumns);
var isTopLeftCorner = (i == 0);
var isTopRightCorner = (i == 2);
// Check for horizontal matches
if (isHorizontalFirstCell
&& bruno.checkCells(
cells, i,
(i + 3), 1))
{
alert('Horizontal');
}
// Check for vertical matches
if (isVerticalFirstCell
&& bruno.checkCells(
cells, i,
(i + 7), 3))
{
alert('Vertical');
}
// Check for diagonal matches
if (isTopLeftCorner
&& bruno.checkCells(
cells, i,
(i + 9), 4))
{
alert('Diagonal');
}
if (isTopRightCorner
&& bruno.checkCells(
cells, i,
(i + 5), 2))
{
alert('Diagonal');
}
}
},
reset: function () {
var jogo = document.getElementById('jogo');
var cell = jogo.getElementsByTagName('div');
for ( j = 0 ; j < cell.length ; j++ ) {
cell[j].innerHTML = "";
}
},
checkCells: function(cells, index, limit, step) {
var sequenceChar = null;
for (var i = index; i < limit; i += step)
{
// Return false immediately if one
// of the cells in the sequence is empty
if (!cells[i].innerHTML)
return false;
// If this is the first cell we're checking,
// store the character(s) it holds.
if (sequenceChar === null)
sequenceChar = cells[i].innerHTML;
// Otherwise, confirm that this cell holds
// the same character(s) as the previous cell(s).
else if (cells[i].innerHTML !== sequenceChar)
return false;
}
// If we reached this point, the entire sequence
// of cells hold the same character(s).
return true;
}
};

Undefined index after running function a few times

So I was trying to create my own Blackjack in javascript for learning purposes and even though the code is overall working, I came across a weird bug.
After some clicks on the Deal html button, which calls the function deal(), I will get either a playerHand[i] undefined or dealerHand[i] undefined error on line 114 or 118, respectively, of the code posted below.
I noticed this also happened if I clicked the button very fast for whatever reason.
I suspected it had something to do with memory optimization so I used the delete command to reset those arrays between game turns, but the error persists.
So, why do my arrays break after some use?
Thanks.
JS:
var deck = [];
var dealerHand = [];
var playerHand = [];
var dscore = 0;
var pscore = 0;
var turn = 0;
function Card(suit, src) {
this.src = src;
this.suit = getSuit(suit);
this.value = getValue(src);
};
function getSuit(suit) {
if (suit == 1) return "Clubs";
if (suit == 2) return "Diamonds";
if (suit == 3) return "Hearts";
if (suit == 4) return "Spades";
};
function getValue(src) {
if (src == 1) return 11;
if (src < 10) return src;
else return 10;
};
function createDeck() {
for (i=1; i<=4; i++) {
for(j=1; j<=13; j++) {
var card = new Card(i, j);
deck.push(card);
};
};
};
function getCard() {
var rand = Math.floor(Math.random()*deck.length);
deck.splice(rand,1);
return deck[rand];
};
function deal() {
if(turn == 0) {
dealerHand.push(getCard());
playerHand.push(getCard());
};
dealerHand.push(getCard());
playerHand.push(getCard());
};
function stand() {
dealerHand.push(getCard());
};
function clearBoard () {
$('#player').html("");
$('#dealer').html("");
};
function resetDeck () {
delete deck;
deck = [];
};
function resetHands () {
delete dealerHand;
delete playerHand;
dealerHand = [];
playerHand = [];
};
function resetScore () {
pscore = 0;
dscore = 0;
};
function isAce (arr) {
for(i=0; i<arr.length; i++) {
if (arr[i].src == 1) return true;
else return false;
};
}
function updateScore() {
resetScore();
if (playerHand.length > 0 && dealerHand.length > 0) {
for(i=0; i<playerHand.length; i++) {
pscore += playerHand[i].value;
};
for(i=0; i<dealerHand.length; i++) {
dscore += dealerHand[i].value;
};
//Regra do Às
if(pscore > 21 && isAce(playerHand)) {
pscore -= 10;
};
if(dscore > 21 && isAce(dealerHand)) {
dscore -= 10;
};
} else {
pscore = 0;
dscore = 0;
};
};
function showScore () {
$('#pscore').html("<p>Player Score: " + pscore + "</p>");
$('#dscore').html("<p>Dealer Score: " + dscore + "</p>");
};
function showCards () {
for(i=0; i<playerHand.length; i++) {
var div = $("<div>");
var img = $("<img>");
img.attr('src', 'img/cards/' + playerHand[i].suit + '/' + playerHand[i].src + '.png');
div.append(img);
$('#player').append(div);
};
for(i=0; i<dealerHand.length; i++) {
var div = $("<div>");
var img = $("<img>");
img.attr('src', 'img/cards/' + dealerHand[i].suit + '/' + dealerHand[i].src + '.png');
div.append(img);
$('#dealer').append(div);
};
};
function cleanUp () {
if (pscore == 21) {
alert("Blackjack!");
newGame();
};
if (pscore > 21) {
alert("Bust!");
newGame();
};
if (dscore == 21) {
alert("You lost!");
newGame();
};
if (dscore > 21) {
alert("You won!");
newGame();
};
};
function newGame () {
turn = 0;
clearBoard();
resetHands();
resetScore();
showScore();
resetDeck();
createDeck();
};
function gameTurn () {
clearBoard();
updateScore();
showCards();
showScore();
cleanUp();
turn++;
};
$(document).ready(function() {
newGame();
$('#deal').on('click', function(){
deal();
gameTurn();
});
$('#stand').on('click', function(){
stand();
gameTurn();
});
});
CSS:
body {
background: url(../img/greenbg.png);
}
.holder {
width:800px;
margin:auto;
}
.clearfix {
clear:both;
}
#pscore, #dscore {
color: white;
margin: 10px;
display: block;
font-size: 1.2rem;
text-shadow: 0 0 5px #000;
}
.container {
width: 600px;
height: 300px;
margin: 10px;
}
div img {
float: left;
margin: 10px;
}
div button {
margin: 10px;
}
HTML:
<html>
<head>
<div class="holder clearfix">
<div id="dscore"><p>Dealer Score: 0</p>
</div>
<div id="dealer" class="container">
</div>
<div id="pscore"><p>Player Score: 0</p>
</div>
<div id="player" class="container">
</div>
<div class="">
<button id="deal">Deal</button>
<button id="stand">Stand</button>
</div>
</div>
</body>
</html>
You have a problem in this function, which may be to blame:
function getCard() {
var rand = Math.floor(Math.random()*deck.length);
deck.splice(rand,1);
return deck[rand];
};
As written, it's removing a card, and then returning the card that now has that position in the deck. If rand was the last element in the array then there is no longer a card in that position, so it'll return undefined.
You should be returning the value of the removed card itself, part of the result of the splice call:
function getCard() {
var rand = Math.floor(Math.random() * deck.length);
var pick = deck.splice(rand, 1);
return pick[0];
};
p.s. it's worth learning modern ES5 utility functions for arrays. For example, your isAce function could be rewritten thus, avoiding the bug where you always return after testing the first element:
function isAce(arr) {
return arr.some(function(n) {
return n === 1;
});
};
or, more cleanly:
function isAce(card) {
return card === 1; // test a single card
};
function holdsAce(hand) {
return hand.some(isAce); // test an array (or hand) of cards
};

How to modify this jQuery plugin slider for scrolling and orientation?

I found a jQuery slider plugin that does almost what I need. I need to change the tabs so it is on the right side (by adding an option). Also, I would like to add scrolling to the tabs in case there is more than 3 tabs (also by an option). I am trying to make it look like this which is an artist mock up:
http://i.stack.imgur.com/nR8RY.png
This is the script I am trying to modify with the code below it:
http://jqueryglobe.com/labs/feature_list/
/*
* FeatureList - simple and easy creation of an interactive "Featured Items" widget
* Examples and documentation at: http://jqueryglobe.com/article/feature_list/
* Version: 1.0.0 (01/09/2009)
* Copyright (c) 2009 jQueryGlobe
* Licensed under the MIT License: http://en.wikipedia.org/wiki/MIT_License
* Requires: jQuery v1.3+
*/
;(function($) {
$.fn.featureList = function(options) {
var tabs = $(this);
var output = $(options.output);
new jQuery.featureList(tabs, output, options);
return this;
};
$.featureList = function(tabs, output, options) {
function slide(nr) {
if (typeof nr == "undefined") {
nr = visible_item + 1;
nr = nr >= total_items ? 0 : nr;
}
tabs.removeClass('current').filter(":eq(" + nr + ")").addClass('current');
output.stop(true, true).filter(":visible").fadeOut();
output.filter(":eq(" + nr + ")").fadeIn(function() {
visible_item = nr;
});
}
var options = options || {};
var total_items = tabs.length;
var visible_item = options.start_item || 0;
options.pause_on_hover = options.pause_on_hover || true;
options.transition_interval = options.transition_interval || 5000;
output.hide().eq( visible_item ).show();
tabs.eq( visible_item ).addClass('current');
tabs.click(function() {
if ($(this).hasClass('current')) {
return false;
}
slide( tabs.index( this) );
});
if (options.transition_interval > 0) {
var timer = setInterval(function () {
slide();
}, options.transition_interval);
if (options.pause_on_hover) {
tabs.mouseenter(function() {
clearInterval( timer );
}).mouseleave(function() {
clearInterval( timer );
timer = setInterval(function () {
slide();
}, options.transition_interval);
});
}
}
};
})(jQuery);
This is the CSS:
body {
background: #EEE;
font-family: "Trebuchet MS",Verdana,Arial,sans-serif;
font-size: 14px;
line-height: 1.6;
}
#content {
width: 750px;
margin: 50px auto;
padding: 20px;
background: #FFF;
border: 1px solid #CCC;
}
h1 {
margin: 0;
}
hr {
border: none;
height: 1px; line-height: 1px;
background: #CCC;
margin-bottom: 20px;
padding: 0;
}
p {
margin: 0;
padding: 7px 0;
}
.clear {
clear: both;
line-height: 1px;
font-size: 1px;
}
a {
outline-color: #888;
}
Can anyone help with this?
Answer: jsFiddle: features box that slides and scrolls
Features:
Slides over time
Click next and previous
Support for lots of slides
Smooth scrolling
Move to item on click
Stop movement on hover
Easily extended because it uses the cycle plug-in.
Time spent on project: 4hrs
Ok, no fancy scrollbars or anything, but it will iterate through each one bringing it to the top index. I spent ages getting this working properly.
You can test it by adding additional items to the Lists.
/*
* FeatureList - simple and easy creation of an interactive "Featured Items" widget
* Examples and documentation at: http://jqueryglobe.com/article/feature_list/
* Version: 1.0.0 (01/09/2009)
* Copyright (c) 2009 jQueryGlobe
* Licensed under the MIT License: http://en.wikipedia.org/wiki/MIT_License
* Requires: jQuery v1.3+
*/
;(function($) {
$.fn.featureList = function(options) {
var tabs = $(this);
var output = $(options.output);
new jQuery.featureList(tabs, output, options);
return this;
};
$.featureList = function(tabs, output, options)
{
function slide(nr) {
if (typeof nr == "undefined") {
nr = visible_item + 1;
nr = nr >= total_items ? 0 : nr;
}
tabs.removeClass('current').filter(":eq(" + nr + ")").addClass('current');
output.stop(true, true).filter(":visible").fadeOut();
output.filter(":eq(" + nr + ")").fadeIn(function() {
visible_item = nr;
});
$(tabs[(nr - 1 + total_items) % total_items]).parent().slideUp(500,function(){
var order = "";
for(var i = total_items; i > 0; i--)
{
var nextInd = ((nr - 1) + i) % total_items;
var tab = $(tabs[nextInd]);
if(i == total_items)
tab.parent().slideDown(500);
tab.parent().prependTo(tab.parent().parent());
order += nextInd + ", ";
}
});
}
var options = options || {};
var total_items = tabs.length;
var visible_item = options.start_item || 0;
options.pause_on_hover = options.pause_on_hover || true;
options.transition_interval = options.transition_interval || 2000;
output.hide().eq( visible_item ).show();
tabs.eq( visible_item ).addClass('current');
tabs.click(function() {
if ($(this).hasClass('current')) {
return false;
}
slide( tabs.index( this) );
});
if (options.transition_interval > 0) {
var timer = setInterval(function () {
slide();
}, options.transition_interval);
if (options.pause_on_hover) {
tabs.mouseenter(function() {
clearInterval( timer );
}).mouseleave(function() {
clearInterval( timer );
timer = setInterval(function () {
slide();
}, options.transition_interval);
});
}
}
};
})(jQuery);
To increase the height of the box simply change the height of div#feature_list and to add additional items simply add an additional li item in both the ul's within feature_list

Categories