Search this site
Embedded Files
CoachingKaarten
  • Coachingkaarten
  • Translator
    • Code
  • M1
  • v1
  • Design
  • 1000
    • backup
    • Mobiel
  • NL to EN
    • CodeNLtoEN
  • NL to ES
    • Code
  • Doelgroepen
  • Developments
  • ⌘
  • STARTPAGES
  • Stijn Gabeler
  • Gabeler.eu Coaching
  • BudgetCoach Online
  • PolitiekEnZo
  • KlantKompas
  • v5
  • v4
  • v3
  • v2
  • V1
CoachingKaarten
  • Coachingkaarten
  • Translator
    • Code
  • M1
  • v1
  • Design
  • 1000
    • backup
    • Mobiel
  • NL to EN
    • CodeNLtoEN
  • NL to ES
    • Code
  • Doelgroepen
  • Developments
  • ⌘
  • STARTPAGES
  • Stijn Gabeler
  • Gabeler.eu Coaching
  • BudgetCoach Online
  • PolitiekEnZo
  • KlantKompas
  • v5
  • v4
  • v3
  • v2
  • V1
  • More
    • Coachingkaarten
    • Translator
      • Code
    • M1
    • v1
    • Design
    • 1000
      • backup
      • Mobiel
    • NL to EN
      • CodeNLtoEN
    • NL to ES
      • Code
    • Doelgroepen
    • Developments
    • ⌘
    • STARTPAGES
    • Stijn Gabeler
    • Gabeler.eu Coaching
    • BudgetCoach Online
    • PolitiekEnZo
    • KlantKompas
    • v5
    • v4
    • v3
    • v2
    • V1

NL to EN Site, Sheet, Script <> code

const CONFIG = {

 SPREADSHEET_ID: '1ZSoVnh2Eix8lyqWppqq-fRdKeNd5O457MOJnEH076xo',

 SENTENCE_SHEET: 'Zinnen',

 SETTINGS_SHEET: 'Settings',

 FAVORITES_SHEET: 'Favorieten'

};


function doGet() {

 return HtmlService

   .createTemplateFromFile('Index')

   .evaluate()

   .setTitle('1000 Zinnen · Presentator')

   .setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);

}


function include(filename) {

 return HtmlService.createHtmlOutputFromFile(filename).getContent();

}


function getSpreadsheet_() {

 return SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID);

}


function setupSheets() {

 const ss = getSpreadsheet_();


 let sentenceSheet = ss.getSheetByName(CONFIG.SENTENCE_SHEET);


 if (!sentenceSheet) {

   sentenceSheet = ss.insertSheet(CONFIG.SENTENCE_SHEET);

 }


 if (sentenceSheet.getLastRow() === 0) {

   sentenceSheet.getRange(1, 1, 1, 7).setValues([[

     'ID',

     'Zin',

     'Actief',

     'Favoriet',

     'Notitie',

     'Aangemaakt',

     'Gewijzigd'

   ]]);

 }


 let settingsSheet = ss.getSheetByName(CONFIG.SETTINGS_SHEET);


 if (!settingsSheet) {

   settingsSheet = ss.insertSheet(CONFIG.SETTINGS_SHEET);


   settingsSheet.getRange(1, 1, 1, 3).setValues([[

     'Key',

     'Value',

     'Gewijzigd'

   ]]);

 }


 let favoritesSheet = ss.getSheetByName(CONFIG.FAVORITES_SHEET);


 if (!favoritesSheet) {

   favoritesSheet = ss.insertSheet(CONFIG.FAVORITES_SHEET);


   favoritesSheet.getRange(1, 1, 1, 6).setValues([[

     'ID',

     'Zin',

     'Notitie',

     'Toegevoegd',

     'Naam',

     'Telefoon'

   ]]);

 }


 return {

   ok: true,

   message: 'Sheets gecontroleerd en klaargezet.'

 };

}


function getSentences() {

 setupSheets();


 const sheet = getSpreadsheet_().getSheetByName(CONFIG.SENTENCE_SHEET);

 const lastRow = sheet.getLastRow();


 if (lastRow < 2) {

   return [];

 }


 const values = sheet.getRange(2, 1, lastRow - 1, 7).getValues();


 return values

   .map(function(row, index) {

     return {

       id: Number(row[0]) || index + 1,

       text: String(row[1] || '').trim(),

       active: row[2] !== false,

       favorite: row[3] === true,

       note: String(row[4] || ''),

       created: row[5] || '',

       modified: row[6] || ''

     };

   })

   .filter(function(item) {

     return item.text;

   });

}


function saveSentence(sentence) {

 setupSheets();


 const sheet = getSpreadsheet_().getSheetByName(CONFIG.SENTENCE_SHEET);

 const lastRow = sheet.getLastRow();


 if (!sentence.id) {

   sentence.id = lastRow;

 }


 const data = sheet.getRange(2, 1, Math.max(1, lastRow - 1), 7).getValues();


 let targetRow = -1;


 for (let i = 0; i < data.length; i++) {

   if (Number(data[i][0]) === Number(sentence.id)) {

     targetRow = i + 2;

     break;

   }

 }


 const now = new Date();


 if (targetRow === -1) {

   targetRow = sheet.getLastRow() + 1;


   sheet.getRange(targetRow, 1, 1, 7).setValues([[

     Number(sentence.id),

     sentence.text,

     sentence.active !== false,

     sentence.favorite === true,

     sentence.note || '',

     now,

     now

   ]]);

 } else {

   const existing = sheet.getRange(targetRow, 1, 1, 7).getValues()[0];


   sheet.getRange(targetRow, 1, 1, 7).setValues([[

     Number(sentence.id),

     sentence.text,

     sentence.active !== false,

     sentence.favorite === true,

     sentence.note || '',

     existing[5] || now,

     now

   ]]);

 }


 return {

   ok: true,

   sentence: sentence

 };

}


function updateSentenceText(id, text) {

 const sheet = getSpreadsheet_().getSheetByName(CONFIG.SENTENCE_SHEET);


 if (!sheet) {

   setupSheets();

   return updateSentenceText(id, text);

 }


 const lastRow = sheet.getLastRow();


 if (lastRow < 2) {

   return false;

 }


 const ids = sheet.getRange(2, 1, lastRow - 1, 1).getValues();


 for (let i = 0; i < ids.length; i++) {

   if (Number(ids[i][0]) === Number(id)) {

     sheet.getRange(i + 2, 2).setValue(text);

     sheet.getRange(i + 2, 7).setValue(new Date());

     return true;

   }

 }


 return false;

}


function setFavorite(id, favorite) {

 const sheet = getSpreadsheet_().getSheetByName(CONFIG.SENTENCE_SHEET);


 if (!sheet) {

   setupSheets();

   return setFavorite(id, favorite);

 }


 const lastRow = sheet.getLastRow();


 if (lastRow < 2) {

   return false;

 }


 const ids = sheet.getRange(2, 1, lastRow - 1, 1).getValues();


 for (let i = 0; i < ids.length; i++) {

   if (Number(ids[i][0]) === Number(id)) {

     sheet.getRange(i + 2, 4).setValue(Boolean(favorite));

     sheet.getRange(i + 2, 7).setValue(new Date());


     syncFavorite_(id, favorite);


     return true;

   }

 }


 return false;

}


function syncFavorite_(id, favorite) {

 const ss = getSpreadsheet_();

 const source = ss.getSheetByName(CONFIG.SENTENCE_SHEET);

 const target = ss.getSheetByName(CONFIG.FAVORITES_SHEET);


 if (!target) {

   setupSheets();

   return syncFavorite_(id, favorite);

 }


 const sourceLast = source.getLastRow();


 if (sourceLast < 2) {

   return;

 }


 const values = source.getRange(2, 1, sourceLast - 1, 7).getValues();


 let sentence = null;


 for (let i = 0; i < values.length; i++) {

   if (Number(values[i][0]) === Number(id)) {

     sentence = values[i];

     break;

   }

 }


 if (!sentence) {

   return;

 }


 const targetLast = target.getLastRow();


 if (targetLast >= 2) {

   const favorites = target.getRange(2, 1, targetLast - 1, 6).getValues();


   for (let i = 0; i < favorites.length; i++) {

     if (Number(favorites[i][0]) === Number(id)) {

       if (!favorite) {

         target.deleteRow(i + 2);

       }


       return;

     }

   }

 }


 if (favorite) {

   target.appendRow([

     sentence[0],

     sentence[1],

     sentence[4],

     new Date(),

     '',

     ''

   ]);

 }

}


function getSettings() {

 setupSheets();


 const sheet = getSpreadsheet_().getSheetByName(CONFIG.SETTINGS_SHEET);

 const lastRow = sheet.getLastRow();


 const defaults = {

   name: '',

   phone: '',

   site: '',

   slogan: 'Accepteer nu wat is, verander op jouw manier wat kan.',

   note: '',

   speed: 650,

   repeats: 3,

   fontSize: 58,

   wordColor: '#ffffff',

   background: '#111827',

   animation: 'fade',

   fullscreen: false

 };


 if (lastRow < 2) {

   return defaults;

 }


 const values = sheet.getRange(2, 1, lastRow - 1, 2).getValues();


 values.forEach(function(row) {

   const key = String(row[0] || '');


   if (key) {

     defaults[key] = row[1];

   }

 });


 return defaults;

}


function saveSettings(settings) {

 setupSheets();


 const sheet = getSpreadsheet_().getSheetByName(CONFIG.SETTINGS_SHEET);


 const keys = Object.keys(settings);


 if (sheet.getLastRow() > 1) {

   sheet.getRange(

     2,

     1,

     sheet.getLastRow() - 1,

     3

   ).clearContent();

 }


 const rows = keys.map(function(key) {

   return [

     key,

     settings[key],

     new Date()

   ];

 });


 if (rows.length) {

   sheet.getRange(2, 1, rows.length, 3).setValues(rows);

 }


 return true;

}


function seedStructure() {

 setupSheets();


 const sheet = getSpreadsheet_().getSheetByName(CONFIG.SENTENCE_SHEET);

 const lastRow = sheet.getLastRow();


 if (lastRow < 2) {

   return {

     ok: false,

     count: 0,

     message: 'De sheet Zinnen is nog leeg. Plak de 1000 zinnen in kolom B en voer daarna normalizeSentences() uit.'

   };

 }


 return normalizeSentences();

}


function normalizeSentences() {

 setupSheets();


 const sheet = getSpreadsheet_().getSheetByName(CONFIG.SENTENCE_SHEET);

 const lastRow = sheet.getLastRow();


 if (lastRow < 2) {

   return {

     ok: false,

     count: 0,

     message: 'Geen zinnen gevonden.'

   };

 }


 const values = sheet.getRange(2, 1, lastRow - 1, 7).getValues();


 const output = [];


 values.forEach(function(row, index) {

   const text = String(row[1] || '').trim();


   if (!text) {

     return;

   }


   output.push([

     index + 1,

     text,

     row[2] === false ? false : true,

     row[3] === true,

     row[4] || '',

     row[5] || new Date(),

     new Date()

   ]);

 });


 if (output.length) {

   sheet.getRange(2, 1, output.length, 7).setValues(output);

 }


 if (output.length >= 1000) {

   sheet.getRange(1001, 2).setValue(

     'Accepteer nu wat is, verander op jouw manier wat kan.'

   );

 }


 return {

   ok: true,

   count: output.length,

   message: output.length + ' zinnen verwerkt.'

 };

}


function importSentencesFromText(text) {

 setupSheets();


 const sheet = getSpreadsheet_().getSheetByName(CONFIG.SENTENCE_SHEET);


 const lines = String(text || '')

   .split(/\r?\n/)

   .map(function(line) {

     return line.trim();

   })

   .filter(function(line) {

     return line;

   });


 if (!lines.length) {

   return {

     ok: false,

     count: 0

   };

 }


 const rows = lines.map(function(line, index) {

   return [

     index + 1,

     line.replace(/^\d+[\.\)]\s*/, ''),

     true,

     false,

     '',

     new Date(),

     new Date()

   ];

 });


 sheet.clearContents();


 sheet.getRange(1, 1, 1, 7).setValues([[

   'ID',

   'Zin',

   'Actief',

   'Favoriet',

   'Notitie',

   'Aangemaakt',

   'Gewijzigd'

 ]]);


 sheet.getRange(2, 1, rows.length, 7).setValues(rows);


 if (rows.length >= 1000) {

   sheet.getRange(1001, 2).setValue(

     'Accepteer nu wat is, verander op jouw manier wat kan.'

   );

 }


 return {

   ok: true,

   count: rows.length

 };

}


function getFavorites() {

 const sheet = getSpreadsheet_().getSheetByName(CONFIG.FAVORITES_SHEET);


 if (!sheet || sheet.getLastRow() < 2) {

   return [];

 }


 return sheet

   .getRange(2, 1, sheet.getLastRow() - 1, 6)

   .getValues()

   .map(function(row) {

     return {

       id: Number(row[0]),

       text: String(row[1] || ''),

       note: String(row[2] || ''),

       added: row[3] || '',

       name: String(row[4] || ''),

       phone: String(row[5] || '')

     };

   });

}


```html

<!DOCTYPE html>

<html lang="nl">

<head>

 <meta charset="UTF-8">

 <meta name="viewport"

       content="width=device-width, initial-scale=1, viewport-fit=cover">


 <title>GTKTP – Zinnenplayer Mobile</title>


 <style>

   * {

     box-sizing: border-box;

   }


   html {

     scroll-behavior: smooth;

   }


   body {

     margin: 0;

     padding: 0;

     font-family: Arial, Helvetica, sans-serif;

     background: #f5f7fa;

     color: #172033;

   }


   .app {

     width: 100%;

     max-width: 760px;

     margin: 0 auto;

     padding: 10px 12px 30px;

   }


   header {

     padding: 8px 4px 12px;

   }


   .brand {

     font-size: 25px;

     font-weight: 800;

     line-height: 1.1;

   }


   .subtitle {

     margin-top: 4px;

     font-size: 13px;

     color: #64748b;

   }


   .status {

     margin-top: 8px;

     padding: 8px 10px;

     border-radius: 10px;

     background: #e9eef5;

     font-size: 13px;

   }


   .player {

     background: white;

     border-radius: 18px;

     padding: 18px;

     box-shadow: 0 4px 18px rgba(0,0,0,.08);

     scroll-margin-top: 8px;

   }


   .counter {

     text-align: center;

     color: #64748b;

     font-size: 13px;

     margin-bottom: 12px;

   }


   .sentence {

     min-height: 145px;

     display: flex;

     align-items: center;

     justify-content: center;

     text-align: center;

     font-size: clamp(25px, 7vw, 42px);

     line-height: 1.2;

     font-weight: 700;

     padding: 15px 5px 25px;

   }


   .favorite {

     width: 100%;

     border: 0;

     background: transparent;

     font-size: 30px;

     padding: 5px;

     cursor: pointer;

   }


   .favorite.active {

     transform: scale(1.08);

   }


   .startButton {

     width: 100%;

     min-height: 64px;

     border: 0;

     border-radius: 15px;

     font-size: 21px;

     font-weight: 800;

     cursor: pointer;

     margin: 8px 0;

     background: #172033;

     color: white;

   }


   .startButton.pause {

     background: #b42318;

   }


   .controls {

     display: grid;

     grid-template-columns: repeat(2, 1fr);

     gap: 9px;

     margin-top: 12px;

   }


   button.control {

     min-height: 48px;

     border: 0;

     border-radius: 12px;

     background: #e8edf3;

     color: #172033;

     font-size: 15px;

     font-weight: 700;

     cursor: pointer;

     padding: 8px;

   }


   button.control:active,

   .startButton:active {

     transform: scale(.98);

   }


   .settings {

     margin-top: 14px;

     background: white;

     border-radius: 16px;

     padding: 15px;

     box-shadow: 0 3px 14px rgba(0,0,0,.06);

   }


   .settings h2 {

     margin: 0 0 12px;

     font-size: 18px;

   }


   .setting {

     margin-bottom: 13px;

   }


   label {

     display: block;

     font-size: 13px;

     font-weight: 700;

     margin-bottom: 5px;

     color: #475569;

   }


   select,

   input[type="number"] {

     width: 100%;

     min-height: 45px;

     border: 1px solid #cbd5e1;

     border-radius: 10px;

     padding: 8px 10px;

     font-size: 16px;

     background: white;

   }


   .stats {

     display: grid;

     grid-template-columns: repeat(3, 1fr);

     gap: 8px;

     margin-top: 14px;

   }


   .stat {

     background: white;

     border-radius: 12px;

     padding: 11px 6px;

     text-align: center;

     box-shadow: 0 2px 9px rgba(0,0,0,.05);

   }


   .statValue {

     font-size: 21px;

     font-weight: 800;

   }


   .statLabel {

     font-size: 11px;

     color: #64748b;

     margin-top: 2px;

   }


   .favoritesList {

     margin-top: 14px;

     background: white;

     border-radius: 16px;

     padding: 15px;

   }


   .favoritesList h2 {

     margin: 0 0 10px;

     font-size: 18px;

   }


   .favoriteItem {

     border-bottom: 1px solid #e2e8f0;

     padding: 10px 0;

     cursor: pointer;

   }


   .favoriteItem:last-child {

     border-bottom: 0;

   }


   .favoriteId {

     font-size: 11px;

     color: #64748b;

     margin-bottom: 3px;

   }


   .favoriteText {

     font-size: 14px;

     line-height: 1.35;

   }


   .empty {

     color: #64748b;

     font-size: 14px;

     padding: 5px 0;

   }


   .footer {

     text-align: center;

     margin-top: 20px;

     font-size: 12px;

     color: #94a3b8;

   }


   @media (min-width: 600px) {

     .app {

       padding-top: 18px;

     }


     .sentence {

       min-height: 190px;

     }

   }

 </style>

</head>


<body>


<div class="app">


 <header>

   <div class="brand">GTKTP</div>


   <div class="subtitle">

     Together we make life easier · Zinnenplayer

   </div>


   <div id="status" class="status">

     Laden...

   </div>

 </header>



 <!-- START / PAUZE BOVEN -->


 <button

   id="startTop"

   class="startButton"

   onclick="togglePlayer()">

   ▶ Start

 </button>



 <!-- PLAYER -->


 <section id="player" class="player">


   <div id="counter" class="counter">

     0 / 0

   </div>


   <div id="sentence" class="sentence">

     Zinnen worden geladen...

   </div>


   <button

     id="favoriteButton"

     class="favorite"

     onclick="toggleFavorite()"

     aria-label="Favoriet">

     ☆

   </button>


 </section>



 <!-- START / PAUZE ONDER -->


 <button

   id="startBottom"

   class="startButton"

   onclick="togglePlayer()">

   ▶ Start

 </button>



 <!-- BEDIENING -->


 <div class="controls">


   <button class="control" onclick="previousSentence()">

     ◀ Vorige

   </button>


   <button class="control" onclick="nextSentence()">

     Volgende ▶

   </button>


   <button class="control" onclick="showRandomSentence()">

     🔀 Random

   </button>


   <button class="control" onclick="showFavorites()">

     ★ Favorieten

   </button>


   <button class="control" onclick="exportJSON()">

     📤 Export JSON

   </button>


   <button

     class="control"

     onclick="document.getElementById('jsonFile').click()">

     📥 Import JSON

   </button>


   <button

     class="control"

     onclick="exportCurrentSentenceWhatsApp()">

     📲 WhatsApp

   </button>


   <button

     class="control"

     onclick="copyCurrentSentenceWhatsApp()">

     📋 Kopieer WhatsApp

   </button>


   <input

     id="jsonFile"

     type="file"

     accept=".json,application/json"

     style="display:none"

     onchange="importJSON(event)">


 </div>



 <!-- INSTELLINGEN -->


 <section class="settings">


   <h2>Instellingen</h2>


   <div class="setting">


     <label for="playMode">

       Afspelen

     </label>


     <select

       id="playMode"

       onchange="changeMode()">


       <option value="all">

         Alle zinnen

       </option>


       <option value="random">

         Random

       </option>


       <option value="favorites">

         Alleen favorieten

       </option>


     </select>


   </div>



   <div class="setting">


     <label for="repeatCount">

       Herhalen per zin

     </label>


     <input

       id="repeatCount"

       type="number"

       min="1"

       max="100"

       value="1"

       onchange="changeRepeat">


   </div>


 </section>



 <!-- STATISTIEKEN -->


 <section class="stats">


   <div class="stat">


     <div

       id="totalCount"

       class="statValue">

       0

     </div>


     <div class="statLabel">

       Zinnen

     </div>


   </div>



   <div class="stat">


     <div

       id="favoriteCount"

       class="statValue">

       0

     </div>


     <div class="statLabel">

       Favorieten

     </div>


   </div>



   <div class="stat">


     <div

       id="playedCount"

       class="statValue">

       0

     </div>


     <div class="statLabel">

       Gespeeld

     </div>


   </div>


 </section>



 <!-- FAVORIETEN -->


 <section class="favoritesList">


   <h2>

     Mijn favorieten

   </h2>


   <div id="favoritesContainer">


     <div class="empty">

       Nog geen favorieten.

     </div>


   </div>


 </section>



 <div class="footer">

   GTKTP · We keep it simple.

 </div>


</div>



<script>


 let sentences = [];


 let currentIndex = 0;


 let playing = false;


 let timer = null;


 let repeatCount = 1;


 let currentRepeat = 0;


 let mode = "all";


 let favorites = new Set();


 let playedCount = 0;



 /*

  * ---------------------------------------------------------

  * CREDITS VOOR WHATSAPP

  * ---------------------------------------------------------

  */


 const WHATSAPP_TOP_CREDIT =

   "https://sites.google.com/view/coachingkaarten/nl-to-en";


 const WHATSAPP_BOTTOM_CREDIT =

   "https://sites.google.com/view/gtktp";



 /*

  * ---------------------------------------------------------

  * START

  * ---------------------------------------------------------

  */


 window.addEventListener(

   "load",

   function() {


     loadLocalSettings();


     loadSentences();


   }

 );



 /*

  * ---------------------------------------------------------

  * ZINNEN LADEN

  * ---------------------------------------------------------

  */


 function loadSentences() {


   setStatus(

     "Zinnen laden..."

   );


   if (

     typeof google !== "undefined" &&

     google.script &&

     google.script.run

   ) {


     google.script.run


       .withSuccessHandler(

         function(data) {


           if (!data) {


             setStatus(

               "Geen zinnen ontvangen."

             );


             return;


           }



           if (

             typeof data === "string"

           ) {


             try {


               data =

                 JSON.parse(data);


             } catch (e) {


               console.error(e);


             }


           }



           if (

             Array.isArray(data)

           ) {


             sentences =

               normalizeSentences(

                 data

               );


           } else if (

             data.sentences

           ) {


             sentences =

               normalizeSentences(

                 data.sentences

               );


           }



           finishLoading();


         }

       )


       .withFailureHandler(

         function(error) {


           console.error(error);


           setStatus(

             "Zinnen konden niet worden geladen."

           );


         }

       )


       .getSentences();


   } else {


     setStatus(

       "Deze HTML is niet gekoppeld aan Apps Script."

     );


   }


 }



 /*

  * ---------------------------------------------------------

  * NORMALISEREN

  * ---------------------------------------------------------

  */


 function normalizeSentences(data) {


   return data


     .map(

       function(item, index) {


         if (

           typeof item === "string"

         ) {


           return {


             id: index + 1,


             text: item


           };


         }



         return {


           id:

             item.id ??

             item.ID ??

             index + 1,


           text:

             item.text ??

             item.zin ??

             item.Zin ??

             item.sentence ??

             ""


         };


       }

     )


     .filter(

       function(item) {


         return (

           String(

             item.text

           ).trim() !== ""

         );


       }

     );


 }



 /*

  * ---------------------------------------------------------

  * KLAAR

  * ---------------------------------------------------------

  */


 function finishLoading() {


   if (

     !sentences.length

   ) {


     setStatus(

       "Geen zinnen gevonden."

     );


     return;


   }



   document.getElementById(

     "totalCount"

   ).textContent =

     sentences.length;



   updateFavoriteCount();


   updateFavoriteList();


   showCurrentSentence();



   setStatus(

     sentences.length +

     " zinnen geladen."

   );


 }



 /*

  * ---------------------------------------------------------

  * DISPLAY

  * ---------------------------------------------------------

  */


 function showCurrentSentence() {


   if (

     !sentences.length

   ) {

     return;

   }



   const item =

     sentences[currentIndex];



   document.getElementById(

     "sentence"

   ).textContent =

     item.text;



   document.getElementById(

     "counter"

   ).textContent =

     (

       currentIndex + 1

     ) +

     " / " +

     sentences.length;



   updateFavoriteButton();


 }



 /*

  * ---------------------------------------------------------

  * FAVORIET

  * ---------------------------------------------------------

  */


 function toggleFavorite() {


   if (

     !sentences.length

   ) {

     return;

   }



   const id =

     String(

       sentences[currentIndex].id

     );



   if (

     favorites.has(id)

   ) {


     favorites.delete(id);


   } else {


     favorites.add(id);


   }



   saveLocalSettings();


   updateFavoriteButton();


   updateFavoriteCount();


   updateFavoriteList();


 }



 function updateFavoriteButton() {


   if (

     !sentences.length

   ) {

     return;

   }



   const id =

     String(

       sentences[currentIndex].id

     );



   const button =

     document.getElementById(

       "favoriteButton"

     );



   if (

     favorites.has(id)

   ) {


     button.textContent =

       "★";


     button.classList.add(

       "active"

     );


   } else {


     button.textContent =

       "☆";


     button.classList.remove(

       "active"

     );


   }


 }



 function updateFavoriteCount() {


   document.getElementById(

     "favoriteCount"

   ).textContent =

     favorites.size;


 }



 /*

  * ---------------------------------------------------------

  * FAVORIETEN LIJST

  * ---------------------------------------------------------

  */


 function updateFavoriteList() {


   const container =

     document.getElementById(

       "favoritesContainer"

     );



   if (

     !favorites.size

   ) {


     container.innerHTML =

       '<div class="empty">' +

       'Nog geen favorieten.' +

       '</div>';


     return;


   }



   let html = "";



   sentences.forEach(

     function(item, index) {


       if (

         !favorites.has(

           String(item.id)

         )

       ) {

         return;

       }



       html +=

         '<div class="favoriteItem" ' +

         'onclick="openFavorite(' +

         index +

         ')">' +


         '<div class="favoriteId">' +

         "ID " +

         item.id +

         "</div>" +


         '<div class="favoriteText">' +

         escapeHTML(

           item.text

         ) +

         "</div>" +


         "</div>";


     }

   );



   container.innerHTML =

     html;


 }



 function openFavorite(index) {


   currentIndex =

     index;


   showCurrentSentence();


   scrollToPlayer();


 }



 /*

  * ---------------------------------------------------------

  * VOLGENDE / VORIGE

  * ---------------------------------------------------------

  */


 function nextSentence() {


   if (

     !sentences.length

   ) {

     return;

   }



   const list =

     getPlaybackList();



   if (

     !list.length

   ) {


     setStatus(

       "Er zijn geen zinnen beschikbaar in deze selectie."

     );


     pausePlayer();


     return;


   }



   let position =

     list.indexOf(

       currentIndex

     );



   if (

     position === -1

   ) {


     position = 0;


   } else {


     position++;


   }



   if (

     position >= list.length

   ) {


     position = 0;


   }



   currentIndex =

     list[position];



   currentRepeat = 0;


   playedCount++;



   document.getElementById(

     "playedCount"

   ).textContent =

     playedCount;



   showCurrentSentence();


   scrollToPlayer();


 }



 function previousSentence() {


   if (

     !sentences.length

   ) {

     return;

   }



   const list =

     getPlaybackList();



   if (

     !list.length

   ) {

     return;

   }



   let position =

     list.indexOf(

       currentIndex

     );



   if (

     position <= 0

   ) {


     position =

       list.length - 1;


   } else {


     position--;


   }



   currentIndex =

     list[position];



   currentRepeat = 0;


   showCurrentSentence();


   scrollToPlayer();


 }



 /*

  * ---------------------------------------------------------

  * PLAYBACK

  * ---------------------------------------------------------

  */


 function getPlaybackList() {


   if (

     mode === "favorites"

   ) {


     return sentences


       .map(

         function(item, index) {


           return favorites.has(

             String(item.id)

           )

             ? index

             : null;


         }

       )


       .filter(

         function(index) {


           return index !== null;


         }

       );


   }



   return sentences.map(

     function(item, index) {


       return index;


     }

   );


 }



 function togglePlayer() {


   if (

     playing

   ) {


     pausePlayer();


   } else {


     startPlayer();


   }


 }



 function startPlayer() {


   if (

     !sentences.length

   ) {


     setStatus(

       "Er zijn nog geen zinnen geladen."

     );


     return;


   }



   if (

     mode === "favorites" &&

     favorites.size === 0

   ) {


     setStatus(

       "Voeg eerst favorieten toe."

     );


     return;


   }



   playing = true;


   updateStartButtons();



   setStatus(

     "Afspelen actief."

   );



   playCurrent();


 }



 function pausePlayer() {


   playing = false;



   if (

     timer

   ) {


     clearTimeout(timer);


     timer = null;


   }



   if (

     "speechSynthesis" in window

   ) {


     window.speechSynthesis.cancel();


   }



   updateStartButtons();



   setStatus(

     "Gepauzeerd. Instellingen kunnen nu worden gewijzigd."

   );


 }



 function playCurrent() {


   if (

     !playing

   ) {

     return;

   }



   showCurrentSentence();


   speakCurrent();


 }



 /*

  * ---------------------------------------------------------

  * SPRAAK

  * ---------------------------------------------------------

  */


 function speakCurrent() {


   if (

     !("speechSynthesis" in window)

   ) {


     setStatus(

       "Tekst-naar-spraak wordt niet ondersteund."

     );


     return;


   }



   window.speechSynthesis.cancel();



   const item =

     sentences[currentIndex];



   const utterance =

     new SpeechSynthesisUtterance(

       item.text

     );



   utterance.lang =

     "nl-NL";



   utterance.rate =

     0.9;



   utterance.onend =

     function() {


       if (

         !playing

       ) {

         return;

       }



       currentRepeat++;



       if (

         currentRepeat <

         repeatCount

       ) {


         timer =

           setTimeout(

             playCurrent,

             500

           );


       } else {


         currentRepeat = 0;



         timer =

           setTimeout(

             nextSentenceAndPlay,

             700

           );


       }


     };



   window.speechSynthesis.speak(

     utterance

   );


 }



 function nextSentenceAndPlay() {


   if (

     !playing

   ) {

     return;

   }



   nextSentence();


   playCurrent();


 }



 /*

  * ---------------------------------------------------------

  * RANDOM

  * ---------------------------------------------------------

  */


 function showRandomSentence() {


   if (

     !sentences.length

   ) {

     return;

   }



   const list =

     getPlaybackList();



   if (

     !list.length

   ) {


     setStatus(

       "Geen zinnen in deze selectie."

     );


     return;


   }



   const randomPosition =

     Math.floor(

       Math.random() *

       list.length

     );



   currentIndex =

     list[randomPosition];



   currentRepeat = 0;


   showCurrentSentence();


   scrollToPlayer();


 }



 /*

  * ---------------------------------------------------------

  * FAVORIETEN MODE

  * ---------------------------------------------------------

  */


 function showFavorites() {


   mode =

     "favorites";



   document.getElementById(

     "playMode"

   ).value =

     "favorites";



   saveLocalSettings();



   if (

     !favorites.size

   ) {


     setStatus(

       "Nog geen favorieten."

     );


     return;


   }



   const list =

     getPlaybackList();



   currentIndex =

     list[0];



   showCurrentSentence();



   setStatus(

     favorites.size +

     " favorieten geselecteerd."

   );



   scrollToPlayer();


 }



 /*

  * ---------------------------------------------------------

  * INSTELLINGEN

  * ---------------------------------------------------------

  */


 function changeMode() {


   mode =

     document.getElementById(

       "playMode"

     ).value;



   saveLocalSettings();



   if (

     mode === "favorites" &&

     favorites.size === 0

   ) {


     setStatus(

       "Favorietenmodus gekozen, maar er zijn nog geen favorieten."

     );


     return;


   }



   setStatus(

     "Modus: " +

     document.getElementById(

       "playMode"

     ).selectedOptions[0].text

   );


 }



 function changeRepeat() {


   let value =

     parseInt(

       document.getElementById(

         "repeatCount"

       ).value,

       10

     );



   if (

     !Number.isFinite(value)

   ) {


     value = 1;


   }



   value =

     Math.max(

       1,

       Math.min(

         100,

         value

       )

     );



   repeatCount =

     value;



   document.getElementById(

     "repeatCount"

   ).value =

     value;



   saveLocalSettings();


 }



 /*

  * ---------------------------------------------------------

  * START BUTTONS

  * ---------------------------------------------------------

  */


 function updateStartButtons() {


   const buttons = [


     document.getElementById(

       "startTop"

     ),


     document.getElementById(

       "startBottom"

     )


   ];



   buttons.forEach(

     function(button) {


       if (

         playing

       ) {


         button.textContent =

           "⏸ Pauze";


         button.classList.add(

           "pause"

         );


       } else {


         button.textContent =

           "▶ Start";


         button.classList.remove(

           "pause"

         );


       }


     }

   );


 }



 /*

  * ---------------------------------------------------------

  * LOCAL OPSLAG

  * ---------------------------------------------------------

  */


 function saveLocalSettings() {


   const data = {


     version:

       "GTKTP-MOBILE-1.0",


     favorites:

       Array.from(

         favorites

       ),


     mode:

       mode,


     repeatCount:

       repeatCount


   };



   localStorage.setItem(

     "GTKTP_MOBILE_SETTINGS",

     JSON.stringify(data)

   );


 }



 function loadLocalSettings() {


   const raw =

     localStorage.getItem(

       "GTKTP_MOBILE_SETTINGS"

     );



   if (

     !raw

   ) {

     return;

   }



   try {


     const data =

       JSON.parse(raw);



     if (

       Array.isArray(

         data.favorites

       )

     ) {


       favorites =

         new Set(

           data.favorites.map(

             String

           )

         );


     }



     if (

       data.mode

     ) {


       mode =

         data.mode;



       const select =

         document.getElementById(

           "playMode"

         );



       if (

         select

       ) {


         select.value =

           mode;


       }


     }



     if (

       data.repeatCount &&

       Number.isFinite(

         Number(

           data.repeatCount

         )

       )

     ) {


       repeatCount =

         Math.max(

           1,

           Math.min(

             100,

             Number(

               data.repeatCount

             )

           )

         );



       document.getElementById(

         "repeatCount"

       ).value =

         repeatCount;


     }


   } catch (error) {


     console.error(

       "Instellingen konden niet worden geladen.",

       error

     );


   }


 }



 /*

  * ---------------------------------------------------------

  * JSON EXPORT

  * ---------------------------------------------------------

  */


 function exportJSON() {


   const data = {


     gtkp:

       "GTKTP",


     version:

       "1.0",


     exported:

       new Date().toISOString(),


     settings: {


       mode:

         mode,


       repeatCount:

         repeatCount


     },


     favorites:

       Array.from(

         favorites

       ),


     favoriteSentences:

       sentences


         .filter(

           function(item) {


             return favorites.has(

               String(item.id)

             );


           }

         )


         .map(

           function(item) {


             return {


               id:

                 item.id,


               zin:

                 item.text


             };


           }

         )


   };



   const blob =

     new Blob(

       [

         JSON.stringify(

           data,

           null,

           2

         )

       ],

       {

         type:

           "application/json"

       }

     );



   const url =

     URL.createObjectURL(

       blob

     );



   const a =

     document.createElement(

       "a"

     );



   a.href =

     url;



   a.download =

     "GTKTP-favorieten-" +

     dateForFilename() +

     ".json";



   document.body.appendChild(

     a

   );



   a.click();


   a.remove();



   URL.revokeObjectURL(

     url

   );



   setStatus(

     favorites.size +

     " favorieten geëxporteerd."

   );


 }



 /*

  * ---------------------------------------------------------

  * WHATSAPP TEKST MAKEN

  * ---------------------------------------------------------

  */


 function getCurrentWhatsAppText() {


   if (

     !sentences.length

   ) {

     return "";

   }



   const item =

     sentences[currentIndex];



   const sentence =

     String(

       item.text || ""

     ).trim();



   if (

     !sentence

   ) {

     return "";

   }



   return (


     WHATSAPP_TOP_CREDIT +


     "\n\n" +


     sentence +


     "\n\n" +


     WHATSAPP_BOTTOM_CREDIT


   );


 }



 /*

  * ---------------------------------------------------------

  * WHATSAPP EXPORT

  * ---------------------------------------------------------

  */


 function exportCurrentSentenceWhatsApp() {


   const message =

     getCurrentWhatsAppText();



   if (

     !message

   ) {


     setStatus(

       "Er is geen huidige zin om te delen."

     );


     return;


   }



   const whatsappURL =

     "https://wa.me/?text=" +

     encodeURIComponent(

       message

     );



   /*

    * Op telefoon/tablet:

    * native deelvenster gebruiken.

    */

   if (

     navigator.share &&

     /Android|iPhone|iPad|iPod/i.test(

       navigator.userAgent

     )

   ) {


     navigator.share({


       title:

         "GTKTP",


       text:

         message


     })


     .then(

       function() {


         setStatus(

           "Zin gedeeld."

         );


       }

     )


     .catch(

       function(error) {


         if (

           error &&

           error.name !==

           "AbortError"

         ) {


           window.open(

             whatsappURL,

             "_blank"

           );


         }


       }

     );


     return;


   }



   /*

    * Chromebook / desktop:

    * WhatsApp Web openen.

    */

   window.open(

     whatsappURL,

     "_blank"

   );



   setStatus(

     "WhatsApp geopend met de huidige zin."

   );


 }



 /*

  * ---------------------------------------------------------

  * KOPIEER WHATSAPP

  * ---------------------------------------------------------

  */


 function copyCurrentSentenceWhatsApp() {


   const message =

     getCurrentWhatsAppText();



   if (

     !message

   ) {


     setStatus(

       "Er is geen huidige zin om te kopiëren."

     );


     return;


   }



   /*

    * Moderne Clipboard API

    */

   if (

     navigator.clipboard &&

     window.isSecureContext

   ) {


     navigator.clipboard

       .writeText(message)


       .then(

         function() {


           setStatus(

             "WhatsApp-tekst gekopieerd."

           );


         }

       )


       .catch(

         function() {


           copyTextFallback(

             message

           );


         }

       );


     return;


   }



   /*

    * Fallback voor oudere browsers.

    */

   copyTextFallback(

     message

   );


 }



 function copyTextFallback(text) {


   const textarea =

     document.createElement(

       "textarea"

     );



   textarea.value =

     text;



   textarea.style.position =

     "fixed";


   textarea.style.left =

     "-9999px";


   textarea.style.top =

     "0";



   document.body.appendChild(

     textarea

   );



   textarea.focus();


   textarea.select();



   try {


     const successful =

       document.execCommand(

         "copy"

       );



     if (

       successful

     ) {


       setStatus(

         "WhatsApp-tekst gekopieerd."

       );


     } else {


       setStatus(

         "Kopiëren is niet gelukt."

       );


     }


   } catch (error) {


     console.error(

       error

     );



     setStatus(

       "Kopiëren is niet gelukt."

     );


   }



   textarea.remove();


 }



 /*

  * ---------------------------------------------------------

  * JSON IMPORT

  * ---------------------------------------------------------

  */


 function importJSON(event) {


   const file =

     event.target.files[0];



   if (

     !file

   ) {

     return;

   }



   const reader =

     new FileReader();



   reader.onload =

     function(e) {


       try {


         const data =

           JSON.parse(

             e.target.result

           );



         if (

           data.favorites &&

           Array.isArray(

             data.favorites

           )

         ) {


           favorites =

             new Set(

               data.favorites.map(

                 String

               )

             );


         }



         if (

           data.settings &&

           data.settings.mode

         ) {


           mode =

             data.settings.mode;



           document.getElementById(

             "playMode"

           ).value =

             mode;


         }



         if (

           data.settings &&

           data.settings.repeatCount

         ) {


           repeatCount =

             Number(

               data.settings.repeatCount

             );



           document.getElementById(

             "repeatCount"

           ).value =

             repeatCount;


         }



         saveLocalSettings();


         updateFavoriteCount();


         updateFavoriteList();


         updateFavoriteButton();



         setStatus(

           favorites.size +

           " favorieten geïmporteerd."

         );



       } catch (error) {


         console.error(error);



         setStatus(

           "Ongeldig JSON-bestand."

         );


       }


     };



   reader.readAsText(

     file

   );



   event.target.value =

     "";


 }



 /*

  * ---------------------------------------------------------

  * HULPFUNCTIES

  * ---------------------------------------------------------

  */


 function scrollToPlayer() {


   document

     .getElementById(

       "player"

     )

     .scrollIntoView({


       behavior:

         "smooth",


       block:

         "start"


     });


 }



 function setStatus(text) {


   document.getElementById(

     "status"

   ).textContent =

     text;


 }



 function dateForFilename() {


   const d =

     new Date();



   return d

     .toISOString()

     .slice(

       0,

       10

     );


 }



 function escapeHTML(text) {


   return String(text)


     .replace(

       /&/g,

       "&amp;"

     )


     .replace(

       /</g,

       "&lt;"

     )


     .replace(

       />/g,

       "&gt;"

     )


     .replace(

       /"/g,

       "&quot;"

     )


     .replace(

       /'/g,

       "&#039;"

     );


 }


</script>


</body>

</html>

```



Be the energy you want to see in the world.

questions → silence → space → thinking → feeling → choosing → acting.

**Stay confident.

Be secure & safe.
Create success.
Learn from every moment.
Attract energy.
Lift people up.
Encourage.
Ask. Listen. Give space.
Celebrate goodness.
Be an inspiration.**

Gabeler.eu Coaching / Stichting GODOGOOD.eu 


Google Sites
Report abuse
Page details
Page updated
Google Sites
Report abuse