PageSourceSearch

https://friendly-chat-93fc6.firebaseapp.com/scripts/main.js

js friendly-chat-93fc6.firebaseapp.com collected 2026-10-03 15:06:27 UTC 11,812 bytes, 303 lines download raw bytes

1/**
2 * Copyright 2015 Google Inc. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16'use strict';
17
18// Initializes FriendlyChat.
19function FriendlyChat() {
20  this.checkSetup();
21
22  // Shortcuts to DOM Elements.
23  this.messageList = document.getElementById('messages');
24  this.messageForm = document.getElementById('message-form');
25  this.messageInput = document.getElementById('message');
26  this.submitButton = document.getElementById('submit');
27  this.submitImageButton = document.getElementById('submitImage');
28  this.imageForm = document.getElementById('image-form');
29  this.mediaCapture = document.getElementById('mediaCapture');
30  this.userPic = document.getElementById('user-pic');
31  this.userName = document.getElementById('user-name');
32  this.signInButton = document.getElementById('sign-in');
33  this.signOutButton = document.getElementById('sign-out');
34  this.signInSnackbar = document.getElementById('must-signin-snackbar');
35
36  // Saves message on form submit.
37  this.messageForm.addEventListener('submit', this.saveMessage.bind(this));
38  this.signOutButton.addEventListener('click', this.signOut.bind(this));
39  this.signInButton.addEventListener('click', this.signIn.bind(this));
40
41  // Toggle for the button.
42  var buttonTogglingHandler = this.toggleButton.bind(this);
43  this.messageInput.addEventListener('keyup', buttonTogglingHandler);
44  this.messageInput.addEventListener('change', buttonTogglingHandler);
45
46  // Events for image upload.
47  this.submitImageButton.addEventListener('click', function() {
48    this.mediaCapture.click();
49  }.bind(this));
50  this.mediaCapture.addEventListener('change', this.saveImageMessage.bind(this));
51
52  this.initFirebase();
53}
54
55// Sets up shortcuts to Firebase features and initiate firebase auth.
56FriendlyChat.prototype.initFirebase = function() {
57  // Shortcuts to Firebase SDK features.
58  this.auth = firebase.auth();
59  this.database = firebase.database();
60  this.storage = firebase.storage();
61  // Initiates Firebase auth and listen to auth state changes.
62  this.auth.onAuthStateChanged(this.onAuthStateChanged.bind(this));
63};
64
65// Loads chat messages history and listens for upcoming ones.
66FriendlyChat.prototype.loadMessages = function() {
67  // Reference to the /messages/ database path.
68  this.messagesRef = this.database.ref('messages');
69  // Make sure we remove all previous listeners.
70  this.messagesRef.off();
71
72  // Loads the last 12 messages and listen for new ones.
73  var setMessage = function(data) {
74    var val = data.val();
75    this.displayMessage(data.key, val.name, val.text, val.photoUrl, val.imageUrl);
76  }.bind(this);
77  this.messagesRef.limitToLast(12).on('child_added', setMessage);
78  this.messagesRef.limitToLast(12).on('child_changed', setMessage);
79};
80
81// Saves a new message on the Firebase DB.
82FriendlyChat.prototype.saveMessage = function(e) {
83  e.preventDefault();
84  // Check that the user entered a message and is signed in.
85  if (this.messageInput.value && this.checkSignedInWithMessage()) {
86    var currentUser = this.auth.currentUser;
87    // Add a new message entry to the Firebase Database.
88    this.messagesRef.push({
89      name: currentUser.displayName,
90      text: this.messageInput.value,
91      photoUrl: currentUser.photoURL || '/images/profile_placeholder.png'
92    }).then(function() {
93      // Clear message text field and SEND button state.
94      FriendlyChat.resetMaterialTextfield(this.messageInput);
95      this.toggleButton();
96    }.bind(this)).catch(function(error) {
97      console.error('Error writing new message to Firebase Database', error);
98    });
99  }
100};
101
102// Sets the URL of the given img element with the URL of the image stored in Firebase Storage.
103FriendlyChat.prototype.setImageUrl = function(imageUri, imgElement) {
104  imgElement.src = imageUri;
105
106  // If the image is a Firebase Storage URI we fetch the URL.
107  if (imageUri.startsWith('gs://')) {
108    imgElement.src = FriendlyChat.LOADING_IMAGE_URL; // Display a loading image first.
109    this.storage.refFromURL(imageUri).getMetadata().then(function(metadata) {
110      imgElement.src = metadata.downloadURLs[0];
111    });
112  } else {
113    imgElement.src = imageUri;
114  }
115};
116
117// Saves a new message containing an image URI in Firebase.
118// This first saves the image in Firebase storage.
119FriendlyChat.prototype.saveImageMessage = function(event) {
120  var file = event.target.files[0];
121
122  // Clear the selection in the file picker input.
123  this.imageForm.reset();
124
125  // Check if the file is an image.
126  if (!file.type.match('image.*')) {
127    var data = {
128      message: 'You can only share images',
129      timeout: 2000
130    };
131    this.signInSnackbar.MaterialSnackbar.showSnackbar(data);
132    return;
133  }
134  // Check if the user is signed-in
135  // Check if the user is signed-in
136  if (this.checkSignedInWithMessage()) {
137
138    // We add a message with a loading icon that will get updated with the shared image.
139    var currentUser = this.auth.currentUser;
140    this.messagesRef.push({
141      name: currentUser.displayName,
142      imageUrl: FriendlyChat.LOADING_IMAGE_URL,
143      photoUrl: currentUser.photoURL || '/images/profile_placeholder.png'
144    }).then(function(data) {
145
146      // Upload the image to Firebase Storage.
147      var uploadTask = this.storage.ref(currentUser.uid + '/' + Date.now() + '/' + file.name)
148          .put(file, {'contentType': file.type});
149      // Listen for upload completion.
150      uploadTask.on('state_changed', null, function(error) {
151        console.error('There was an error uploading a file to Firebase Storage:', error);
152      }, function() {
153
154        // Get the file's Storage URI and update the chat message placeholder.
155        var filePath = uploadTask.snapshot.metadata.fullPath;
156        data.update({imageUrl: this.storage.ref(filePath).toString()});
157      }.bind(this));
158    }.bind(this));
159  }
160};
161
162// Signs-in Friendly Chat.
163FriendlyChat.prototype.signIn = function(googleUser) {
164  // TODO(DEVELOPER): Sign in Firebase with credential from the Google user.
165  // Sign in Firebase using popup auth and Google as the identity provider.
166  var provider = new firebase.auth.GoogleAuthProvider();
167  this.auth.signInWithPopup(provider);
168};
169
170// Signs-out of Friendly Chat.
171FriendlyChat.prototype.signOut = function() {
172  // TODO(DEVELOPER): Sign out of Firebase.
173  // Sign out of Firebase.
174  this.auth.signOut();
175};
176
177// Triggers when the auth state change for instance when the user signs-in or signs-out.
178FriendlyChat.prototype.onAuthStateChanged = function(user) {
179  if (user) { // User is signed in!
180    // Get profile pic and user's name from the Firebase user object.
181    var profilePicUrl = user.photoURL;
182    var userName = user.displayName;
183
184    // Set the user's profile pic and name.
185    this.userPic.style.backgroundImage = 'url(' + profilePicUrl + ')';
186    this.userName.textContent = userName;
187
188    // Show user's profile and sign-out button.
189    this.userName.removeAttribute('hidden');
190    this.userPic.removeAttribute('hidden');
191    this.signOutButton.removeAttribute('hidden');
192
193    // Hide sign-in button.
194    this.signInButton.setAttribute('hidden', 'true');
195
196    // We load currently existing chant messages.
197    this.loadMessages();
198  } else { // User is signed out!
199    // Hide user's profile and sign-out button.
200    this.userName.setAttribute('hidden', 'true');
201    this.userPic.setAttribute('hidden', 'true');
202    this.signOutButton.setAttribute('hidden', 'true');
203
204    // Show sign-in button.
205    this.signInButton.removeAttribute('hidden');
206  }
207};
208
209// Returns true if user is signed-in. Otherwise false and displays a message.
210FriendlyChat.prototype.checkSignedInWithMessage = function() {
211  /* Check if user is signed-in Firebase. */
212  // Return true if the user is signed in Firebase
213  if (this.auth.currentUser) {
214    return true;
215  }
216
217  // Display a message to the user using a Toast.
218  var data = {
219    message: 'You must sign-in first',
220    timeout: 2000
221  };
222  this.signInSnackbar.MaterialSnackbar.showSnackbar(data);
223  return false;
224};
225
226// Resets the given MaterialTextField.
227FriendlyChat.resetMaterialTextfield = function(element) {
228  element.value = '';
229  element.parentNode.MaterialTextfield.boundUpdateClassesHandler();
230};
231
232// Template for messages.
233FriendlyChat.MESSAGE_TEMPLATE =
234    '<div class="message-container">' +
235      '<div class="spacing"><div class="pic"></div></div>' +
236      '<div class="message"></div>' +
237      '<div class="name"></div>' +
238    '</div>';
239
240// A loading image URL.
241FriendlyChat.LOADING_IMAGE_URL = 'https://www.google.com/images/spin-32.gif';
242
243// Displays a Message in the UI.
244FriendlyChat.prototype.displayMessage = function(key, name, text, picUrl, imageUri) {
245  var div = document.getElementById(key);
246  // If an element for that message does not exists yet we create it.
247  if (!div) {
248    var container = document.createElement('div');
249    container.innerHTML = FriendlyChat.MESSAGE_TEMPLATE;
250    div = container.firstChild;
251    div.setAttribute('id', key);
252    this.messageList.appendChild(div);
253  }
254  if (picUrl) {
255    div.querySelector('.pic').style.backgroundImage = 'url(' + picUrl + ')';
256  }
257  div.querySelector('.name').textContent = name;
258  var messageElement = div.querySelector('.message');
259  if (text) { // If the message is text.
260    messageElement.textContent = text;
261    // Replace all line breaks by <br>.
262    messageElement.innerHTML = messageElement.innerHTML.replace(/\n/g, '<br>');
263  } else if (imageUri) { // If the message is an image.
264    var image = document.createElement('img');
265    image.addEventListener('load', function() {
266      this.messageList.scrollTop = this.messageList.scrollHeight;
267    }.bind(this));
268    this.setImageUrl(imageUri, image);
269    messageElement.innerHTML = '';
270    messageElement.appendChild(image);
271  }
272  // Show the card fading-in.
273  setTimeout(function() {div.classList.add('visible')}, 1);
274  this.messageList.scrollTop = this.messageList.scrollHeight;
275  this.messageInput.focus();
276};
277
278// Enables or disables the submit button depending on the values of the input
279// fields.
280FriendlyChat.prototype.toggleButton = function() {
281  if (this.messageInput.value) {
282    this.submitButton.removeAttribute('disabled');
283  } else {
284    this.submitButton.setAttribute('disabled', 'true');
285  }
286};
287
288// Checks that the Firebase SDK has been correctly setup and configured.
289FriendlyChat.prototype.checkSetup = function() {
290  if (!window.firebase || !(firebase.app instanceof Function) || !window.config) {
291    window.alert('You have not configured and imported the Firebase SDK. ' +
292        'Make sure you go through the codelab setup instructions.');
293  } else if (config.storageBucket === '') {
294    window.alert('Your Firebase Storage bucket has not been enabled. Sorry about that. This is ' +
295        'actually a Firebase bug that occurs rarely.' +
296        'Please go and re-generate the Firebase initialisation snippet (step 4 of the codelab) ' +
297        'and make sure the storageBucket attribute is not empty.');
298  }
299};
300
301window.onload = function() {
302  window.friendlyChat = new FriendlyChat();
303};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.